diff --git a/.env b/.env index 2e32ac97bbf..3c4b26fdad4 100644 --- a/.env +++ b/.env @@ -6,6 +6,7 @@ REACT_APP_COVER_IMAGE=https://cdn.ohc.network/care_logo.svg REACT_APP_COVER_IMAGE_ALT=https://cdn.ohc.network/care_logo.svg REACT_PUBLIC_URL=https://care.ohc.network HEADERS="/*\n Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\n X-XSS-Protection: 1; mode=block\n X-Frame-Options: SAMEORIGIN\n X-Content-Type-Options: nosniff\n Referrer-Policy: strict-origin-when-cross-origin\n Permissions-Policy: geolocation=(self), microphone=()" +REACT_RECAPTCHA_SITE_KEY=6LcedK8qAAAAAM2PpuqlqhZUxQpmIqHqluL74dDs # Care API URL without the /api prefix REACT_CARE_API_URL=https://careapi.ohc.network diff --git a/.example.env b/.example.env index 04055c21c5e..862f05eb5c4 100644 --- a/.example.env +++ b/.example.env @@ -35,9 +35,6 @@ REACT_CUSTOM_DESCRIPTION= # Google Maps API key REACT_GMAPS_API_KEY= -# Government data API key -REACT_GOV_DATA_API_KEY= - # reCAPTCHA site key REACT_RECAPTCHA_SITE_KEY= @@ -47,16 +44,9 @@ REACT_SENTRY_DSN= # Sentry environment (default: staging) REACT_SENTRY_ENVIRONMENT= -# Camera feed, still watching idle timeout (in seconds; default: 180) -REACT_STILL_WATCHING_IDLE_TIMEOUT= - -# Camera feed, still watching prompt duration (in seconds; default: 30) -REACT_STILL_WATCHING_PROMPT_DURATION= - # Feature flags REACT_ENABLE_HCX=true REACT_ENABLE_ABDM=true -REACT_WARTIME_SHIFTING=true # JWT token refresh interval (in milliseconds) (default: 5 minutes) REACT_JWT_TOKEN_REFRESH_INTERVAL= diff --git a/care.config.ts b/care.config.ts index 6813a60b331..d99d6fe73cd 100644 --- a/care.config.ts +++ b/care.config.ts @@ -58,22 +58,7 @@ const careConfig = { gmapsApiKey: env.REACT_GMAPS_API_KEY || "AIzaSyDsBAc3y7deI5ZO3NtK5GuzKwtUzQNJNUk", - govDataApiKey: - env.REACT_GOV_DATA_API_KEY || - "579b464db66ec23bdd000001cdd3946e44ce4aad7209ff7b23ac571b", - reCaptchaSiteKey: - env.REACT_RECAPTCHA_SITE_KEY || "6LdvxuQUAAAAADDWVflgBqyHGfq-xmvNJaToM0pN", - - wartimeShifting: boolean("REACT_WARTIME_SHIFTING"), - - stillWatching: { - idleTimeout: env.REACT_STILL_WATCHING_IDLE_TIMEOUT - ? parseInt(env.REACT_STILL_WATCHING_IDLE_TIMEOUT) - : 3 * 60, - promptDuration: env.REACT_STILL_WATCHING_PROMPT_DURATION - ? parseInt(env.REACT_STILL_WATCHING_PROMPT_DURATION) - : 30, - }, + reCaptchaSiteKey: env.REACT_RECAPTCHA_SITE_KEY, auth: { tokenRefreshInterval: env.REACT_JWT_TOKEN_REFRESH_INTERVAL diff --git a/cypress/e2e/patient_spec/patient_creation.cy.ts b/cypress/e2e/patient_spec/patient_creation.cy.ts index 8f2c1606c38..dc237fdd1bf 100644 --- a/cypress/e2e/patient_spec/patient_creation.cy.ts +++ b/cypress/e2e/patient_spec/patient_creation.cy.ts @@ -106,7 +106,7 @@ describe("Patient Management", () => { patientTestCases.forEach(({ description, data }) => { it(`creates a new ${description} and verifies registration`, () => { - facilityCreation.selectFacility("GHC Trikaripur"); + facilityCreation.selectFacility("GHC payyanur"); patientCreation .clickSearchPatients() .clickCreateNewPatient() @@ -134,7 +134,7 @@ describe("Patient Management", () => { }); it("Search patient with phone number and create a new encounter", () => { - facilityCreation.selectFacility("GHC Trikaripur"); + facilityCreation.selectFacility("GHC payyanur"); patientCreation .clickSearchPatients() .searchPatient(TEST_PHONE) @@ -167,7 +167,7 @@ describe("Patient Management", () => { address: generateAddress(true), }; - facilityCreation.selectFacility("GHC Trikaripur"); + facilityCreation.selectFacility("GHC payyanur"); patientEncounter .navigateToEncounters() .openFirstEncounterDetails() diff --git a/cypress/e2e/patient_spec/patient_details.cy.ts b/cypress/e2e/patient_spec/patient_details.cy.ts index 35732dbecd0..d96baf9bfef 100644 --- a/cypress/e2e/patient_spec/patient_details.cy.ts +++ b/cypress/e2e/patient_spec/patient_details.cy.ts @@ -15,7 +15,7 @@ describe("Patient Management", () => { it("Assign users to a patient", () => { const userName = "nihal-nurse"; const userRole = "Nurse"; - facilityCreation.selectFacility("GHC Trikaripur"); + facilityCreation.selectFacility("GHC payyanur"); patientEncounter .navigateToEncounters() .openFirstEncounterDetails() diff --git a/cypress/e2e/patient_spec/patient_encounter.cy.ts b/cypress/e2e/patient_spec/patient_encounter.cy.ts index 6fd08494d92..2569b9567bc 100644 --- a/cypress/e2e/patient_spec/patient_encounter.cy.ts +++ b/cypress/e2e/patient_spec/patient_encounter.cy.ts @@ -15,7 +15,7 @@ describe("Patient Encounter Questionnaire", () => { pco2: "120", po2: "80", }; - facilityCreation.selectFacility("GHC Trikaripur"); + facilityCreation.selectFacility("GHC payyanur"); // Chain the methods instead of multiple separate calls patientEncounter diff --git a/cypress/pageObject/Users/UserCreation.ts b/cypress/pageObject/Users/UserCreation.ts index fb2a2a180f0..016dc948560 100644 --- a/cypress/pageObject/Users/UserCreation.ts +++ b/cypress/pageObject/Users/UserCreation.ts @@ -72,7 +72,7 @@ export class UserCreation { { label: "Confirm Password", message: "Required" }, { label: "Phone Number", - message: "Invalid input", + message: "This field is required", }, { label: "State", message: "Required" }, ]); diff --git a/cypress/pageObject/facility/FacilityCreation.ts b/cypress/pageObject/facility/FacilityCreation.ts index 054cb3539e9..c7182aa5f1d 100644 --- a/cypress/pageObject/facility/FacilityCreation.ts +++ b/cypress/pageObject/facility/FacilityCreation.ts @@ -94,7 +94,7 @@ export class FacilityCreation { { label: "Address", message: "Address is required" }, { label: "Phone Number", - message: "Invalid input", + message: "This field is required", }, { label: "Pincode", message: "Invalid Pincode" }, ]); diff --git a/package-lock.json b/package-lock.json index 3aa43279fa1..ce7c22f4b60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "care_fe", - "version": "2.13.0", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "care_fe", - "version": "2.13.0", + "version": "3.1.0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -88,7 +88,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", - "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@trivago/prettier-plugin-sort-imports": "^5.0.0", "@types/dompurify": "^3.0.5", "@types/events": "^3.0.3", "@types/google.maps": "^3.58.1", @@ -296,74 +296,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/core/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -374,15 +306,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" @@ -401,20 +334,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", @@ -462,79 +381,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -611,181 +457,157 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { + "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { + "node_modules/@babel/helper-wrap-function": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, "license": "MIT", "dependencies": { + "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" }, @@ -793,606 +615,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-module-imports/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/helpers": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", @@ -1406,26 +628,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helpers/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -1434,19 +643,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/parser/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", @@ -1464,79 +660,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", @@ -1604,79 +727,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -1789,79 +839,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", @@ -1926,95 +903,45 @@ "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-classes/node_modules/globals": { @@ -2027,19 +954,6 @@ "node": ">=4" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", @@ -2206,79 +1120,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-function-name/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-function-name/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-function-name/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-function-name/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-function-name/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-transform-json-strings": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", @@ -2396,79 +1237,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", @@ -3036,116 +1804,54 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/@babel/traverse/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -6359,26 +5065,37 @@ } }, "node_modules/@trivago/prettier-plugin-sort-imports": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", - "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", + "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@babel/generator": "7.17.7", - "@babel/parser": "^7.20.5", - "@babel/traverse": "7.23.2", - "@babel/types": "7.17.0", - "javascript-natural-sort": "0.7.1", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", + "javascript-natural-sort": "^0.7.1", "lodash": "^4.17.21" }, + "engines": { + "node": ">18.12" + }, "peerDependencies": { "@vue/compiler-sfc": "3.x", - "prettier": "2.x - 3.x" + "prettier": "2.x - 3.x", + "prettier-plugin-svelte": "3.x", + "svelte": "4.x || 5.x" }, "peerDependenciesMeta": { "@vue/compiler-sfc": { "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "svelte": { + "optional": true } } }, @@ -6423,19 +5140,6 @@ "@types/babel__traverse": "*" } }, - "node_modules/@types/babel__core/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@types/babel__generator": { "version": "7.6.8", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", @@ -6464,19 +5168,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/babel__traverse/node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -12694,16 +11385,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -16997,16 +15687,6 @@ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -17980,15 +16660,6 @@ "node": ">=14.14" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index ce9575a1b39..393214e6980 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "care_fe", - "version": "2.13.0", + "version": "3.1.0", "description": "Care is a Digital Public Good enabling TeleICU & Decentralised Administration of Healthcare Capacity across States.", "private": true, "repository": { @@ -127,7 +127,7 @@ "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", - "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@trivago/prettier-plugin-sort-imports": "^5.0.0", "@types/dompurify": "^3.0.5", "@types/events": "^3.0.3", "@types/google.maps": "^3.58.1", diff --git a/public/locale/en.json b/public/locale/en.json index 131cb515b31..6c3c92de78c 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -48,6 +48,7 @@ "DOMESTIC_HEALTHCARE_SUPPORT__NO_SUPPORT": "No support", "DOMESTIC_HEALTHCARE_SUPPORT__PAID_CAREGIVER": "Paid caregiver", "ENCOUNTER_TAB__abdm": "ABDM Records", + "ENCOUNTER_TAB__claims": "Insurance Claims", "ENCOUNTER_TAB__feed": "Feed", "ENCOUNTER_TAB__files": "Files", "ENCOUNTER_TAB__medicines": "Medicines", @@ -290,6 +291,7 @@ "add_beds_to_configure_presets": "Add beds to this location to configure presets for them.", "add_consultation": "Add consultation", "add_consultation_update": "Add Consultation Update", + "add_department_team": "Add Department/Team", "add_details_of_patient": "Add Details of Patient", "add_exception": "Add Exception", "add_facility": "Add Facility", @@ -383,6 +385,7 @@ "approved_by_district_covid_control_room": "Approved by District COVID Control Room", "approving_facility": "Name of Approving Facility", "archive": "Archive", + "archive_file": "Archive File", "archived": "Archived", "archived_at": "Archived at", "archived_by": "Archived by", @@ -394,6 +397,7 @@ "are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?", "are_you_sure_want_to_delete_this_record": "Are you sure want to delete this record?", "are_you_sure_want_to_remove": "Are you sure you want to remove {{name}} from the patient? This action cannot be undone", + "are_you_sure_you_want_to_delete_user": "Are you sure you want to delete this user?", "ari": "ARI - Acute Respiratory illness", "arrived": "Arrived", "as_needed_prn": "As Needed / PRN", @@ -504,7 +508,7 @@ "care_backend": "Care Backend", "care_frontend": "Care Frontend", "category": "Category", - "category_description": "Choose the category that best describes the resource needed.", + "category_description": "Choose the category ", "caution": "Caution", "central_nursing_station": "Central Nursing Station", "change_avatar": "Change Avatar", @@ -638,6 +642,8 @@ "create_add_more": "Create & Add More", "create_asset": "Create Asset", "create_consultation": "Create Consultation", + "create_department_team": "Create Department/Team", + "create_department_team_description": "Create a new department/team in this facility.", "create_encounter": "Create Encounter", "create_facility": "Create Facility", "create_new": "Create New", @@ -645,6 +651,7 @@ "create_new_encounter": "Create a new encounter to get started", "create_new_facility": "Create a new facility and add it to the organization.", "create_new_tag": "Create New Tag", + "create_organization": "Create Organization", "create_position_preset": "Create a new position preset", "create_position_preset_description": "Creates a new position preset in Care from the current position of the camera for the given name", "create_preset_prerequisite": "To create presets for this bed, you'll need to link the camera to the bed first.", @@ -696,8 +703,10 @@ "delete_account_btn": "Yes, delete this account", "delete_account_note": "Deleting this account will remove all associated data and cannot be undone.", "delete_facility": "Delete Facility", + "delete_facility_confirmation": "Are you sure you want to delete {{name}}? This action cannot be undone.", "delete_item": "Delete {{name}}", "delete_record": "Delete Record", + "delete_user": "Delete User", "deleting": "Deleting...", "demography": "Demography", "denied_on": "Denied On", @@ -934,6 +943,8 @@ "end_time_before_start_error": "End time cannot be before start time", "end_time_future_error": "End time cannot be in the future", "ended": "Ended", + "enter_department_team_description": "Enter department/team description (optional)", + "enter_department_team_name": "Enter department/team name", "enter_dosage_instructions": "Enter Dosage Instructions", "enter_file_name": "Enter File Name", "enter_message": "Start typing...", @@ -994,6 +1005,7 @@ "facility_consent_requests_page_title": "Patient Consent List", "facility_count_one": "{{count}} Facility", "facility_count_other": "{{count}} Facilities ", + "facility_deleted_successfully": "{{name}} has been deleted successfully.", "facility_district_name": "Facility/District Name", "facility_district_pincode": "Facility/District/Pincode", "facility_for_care_support": "Facility for Care Support", @@ -1373,6 +1385,8 @@ "new_password_same_as_old": "Your new password must not match the old password ", "new_password_validation": "New password is not valid.", "new_session": "New Session", + "next": "Next", + "next_file": "Next file", "next_month": "Next month", "next_sessions": "Next Sessions", "next_week_short": "Next wk", @@ -1515,6 +1529,7 @@ "ordering": "Ordering", "organization": "Organization", "organization_access_help": "Organizations help you manage facilities, users, and resources efficiently. Contact your administrator to get access.", + "organization_created_successfully": "Organization created successfully", "organization_for_care_support": "Organization for Care Support", "organization_forbidden": "You don't have access to any organizations yet.", "organization_not_found": "No Organizations Found", @@ -1642,6 +1657,7 @@ "please_enter_correct_birth_year": "Please enter the correct birth year to verify the patient details.", "please_enter_current_password": "Please enter your current password.", "please_enter_new_password": "Please enter your new password.", + "please_enter_organization_name": "Please enter an organization name", "please_enter_username": "Please enter the username", "please_fix_errors": "Please fix the errors in the highlighted fields and try submitting again.", "please_select_a_facility": "Please select a facility", @@ -1695,6 +1711,7 @@ "preset_name_placeholder": "Specify an identifiable name for the new preset", "preset_updated": "Preset updated", "prev_sessions": "Prev Sessions", + "previous": "Previous", "primary_ph_no": "Primary Ph No.", "primary_phone_no": "Primary ph. no.", "principal": "Principal", @@ -1839,6 +1856,7 @@ "resource_status__transportation_to_be_arranged": "Transportation to be arranged", "resource_title": "Resource Title", "resource_type": "Request Type", + "resource_updated_successfully": "Resource updated successfully", "respiratory_status": "Respiratory Status", "result": "Result", "result_date": "Result Date", @@ -1857,6 +1875,8 @@ "right": "Right", "role": "Role", "room_apt": "Room/Apt & Time", + "rotate_left": "Rotate Left", + "rotate_right": "Rotate Right", "route": "Route", "routine": "Routine", "sample_collection_date": "Sample Collection Date", @@ -1911,6 +1931,7 @@ "search_by_patient_no": "Search by Patient Number", "search_by_phone_number": "Search by Phone Number", "search_by_resource_title": "Search by resource title", + "search_by_user_name": "Search by user name", "search_by_username": "Search by username", "search_country": "Search country...", "search_encounters": "Search Encounters", @@ -1930,6 +1951,7 @@ "search_tags": "Search tags...", "search_user": "Search User", "search_user_description": "Search for a user and assign a role to add them to the patient.", + "search_users": "Search users...", "searching": "Searching...", "see_attachments": "See Attachments", "see_details": "See Details", @@ -1964,6 +1986,7 @@ "select_method": "Select method", "select_new_role": "Select New Role", "select_organization": "Select Organization", + "select_organization_type": "Select organization type", "select_patient": "Select Patient", "select_policy": "Select an Insurance Policy", "select_policy_to_add_items": "Select a Policy to Add Items", @@ -2053,6 +2076,7 @@ "something_wrong": "Something went wrong! Try again later!", "sort_by": "Sort By", "source": "Source", + "source_file": "Source Files", "spdx_sbom_version": "SPDX SBOM Version", "spokes": "Spoke Facilities", "srf_id": "SRF ID", @@ -2153,6 +2177,7 @@ "true": "True", "try_again_later": "Try again later!", "try_different_abha_linking_option": "Want to try a different linking option, here are some more:", + "type": "Type", "type_any_extra_comments_here": "type any extra comments here", "type_b_cylinders": "B Type Cylinders", "type_c_cylinders": "C Type Cylinders", @@ -2233,6 +2258,7 @@ "update_record": "Update Record", "update_record_for_asset": "Update record for asset", "update_request": "Update Request", + "update_resource_request": "Update Resource Request", "update_role": "Update Role", "update_shift_request": "Update Shift Request", "update_status": "Update Status", diff --git a/src/CAREUI/display/Card.tsx b/src/CAREUI/display/Card.tsx deleted file mode 100644 index 3772fa0b75b..00000000000 --- a/src/CAREUI/display/Card.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { HTMLAttributes, ReactNode } from "react"; - -export default function Card( - props: { - children?: ReactNode; - } & HTMLAttributes, -) { - const { children, ...rest } = props; - return ( - - {children} - - ); -} diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 00d052ba366..2aaa8c115e8 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -1,6 +1,5 @@ import QuestionnaireResponseView from "@/components/Facility/ConsultationDetails/QuestionnaireResponseView"; import EncounterQuestionnaire from "@/components/Patient/EncounterQuestionnaire"; -import FileUploadPage from "@/components/Patient/FileUploadPage"; import TreatmentSummary from "@/components/Patient/TreatmentSummary"; import { AppRoutes } from "@/Routers/AppRouter"; @@ -72,18 +71,6 @@ const consultationRoutes: AppRoutes = { ({ patientId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/encounterId/:id/files/": ({ - facilityId, - patientId, - id, - }) => ( - - ), }; export default consultationRoutes; diff --git a/src/Routers/routes/FacilityRoutes.tsx b/src/Routers/routes/FacilityRoutes.tsx index bf716284545..875d3e32ebd 100644 --- a/src/Routers/routes/FacilityRoutes.tsx +++ b/src/Routers/routes/FacilityRoutes.tsx @@ -1,7 +1,7 @@ import { Redirect } from "raviger"; import FacilityUsers from "@/components/Facility/FacilityUsers"; -import ResourceCreate from "@/components/Resource/ResourceCreate"; +import ResourceCreate from "@/components/Resource/ResourceForm"; import { AppRoutes } from "@/Routers/AppRouter"; import { FacilityOverview } from "@/pages/Facility/overview"; diff --git a/src/Routers/routes/PatientRoutes.tsx b/src/Routers/routes/PatientRoutes.tsx index 77230f3ee76..26b2ff4aa38 100644 --- a/src/Routers/routes/PatientRoutes.tsx +++ b/src/Routers/routes/PatientRoutes.tsx @@ -1,4 +1,3 @@ -import FileUploadPage from "@/components/Patient/FileUploadPage"; import { facilityPatientTabs, patientTabs, @@ -45,16 +44,6 @@ const PatientRoutes: AppRoutes = { "/facility/:facilityId/patient/:id/update": ({ facilityId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/files": ({ - facilityId, - patientId, - }) => ( - - ), }; export default PatientRoutes; diff --git a/src/Routers/routes/ResourceRoutes.tsx b/src/Routers/routes/ResourceRoutes.tsx index 390e566325b..d82b17d1672 100644 --- a/src/Routers/routes/ResourceRoutes.tsx +++ b/src/Routers/routes/ResourceRoutes.tsx @@ -1,6 +1,6 @@ import PrintResourceLetter from "@/components/Resource/PrintResourceLetter"; import ResourceDetails from "@/components/Resource/ResourceDetails"; -import { ResourceDetailsUpdate } from "@/components/Resource/ResourceDetailsUpdate"; +import ResourceForm from "@/components/Resource/ResourceForm"; import ResourceList from "@/components/Resource/ResourceList"; import { AppRoutes } from "@/Routers/AppRouter"; @@ -13,7 +13,7 @@ const ResourceRoutes: AppRoutes = { ), "/facility/:facilityId/resource/:id/update": ({ facilityId, id }) => ( - + ), "/facility/:facilityId/resource/:id/print": ({ id }) => ( diff --git a/src/Utils/request/README.md b/src/Utils/request/README.md index 77b4e2f99e4..4277593fe5c 100644 --- a/src/Utils/request/README.md +++ b/src/Utils/request/README.md @@ -1,6 +1,6 @@ # CARE's data fetching utilities -CARE now uses TanStack Query (formerly React Query) as its data fetching solution. For backward compatibility, we maintain a wrapper `useTanStackQueryInstead` that provides the same API as our previous `useQuery` hook. +CARE now uses TanStack Query (formerly React Query) as its data fetching solution. ## Using TanStack Query (Recommended for new code) @@ -178,265 +178,3 @@ function UpdatePatient({ patientId }: { patientId: string }) { return ; } ``` - -### mutate - -`mutate` is our wrapper around the API call functionality that works with TanStack Query's `useMutation`. It: - -- Handles request body serialization -- Sets appropriate headers -- Integrates with our global error handling -- Provides TypeScript type safety for your mutation payload - -```typescript -interface APICallOptions { - pathParams?: Record; // URL parameters - queryParams?: QueryParams; // Query string parameters - body?: TBody; // Request body - silent?: boolean; // Suppress error notifications - headers?: HeadersInit; // Additional headers -} - -// Basic usage -useMutation({ - mutationFn: mutate(routes.users.create), -}); - -// With parameters -useMutation({ - mutationFn: mutate(routes.users.update, { - pathParams: { id }, - silent: true, // Optional: suppress error notifications - }), -}); -``` - -## Migration Guide & Reference - -### Understanding the Transition - -Our codebase contains two patterns for data fetching: - -1. Legacy pattern using `useTanStackQueryInstead` (wrapper around TanStack Query) -2. Modern pattern using TanStack Query directly - -### Pattern Comparison - -Here's the same API call implemented both ways: - -```tsx -// Legacy Pattern (existing code) -function LegacyComponent({ id }) { - const { data, loading, error, refetch } = useTanStackQueryInstead( - UserRoutes.getUser, - { - pathParams: { id }, - prefetch: true, - refetchOnWindowFocus: false, - }, - ); -} - -// Modern Pattern (new code) -function ModernComponent({ id }) { - const { data, isLoading, error, refetch } = useQuery({ - queryKey: [UserRoutes.getUser.path, id], - queryFn: query(UserRoutes.getUser, { - pathParams: { id }, - }), - enabled: true, - refetchOnWindowFocus: false, - }); -} -``` - -### Migration Mapping - -When migrating from `useTanStackQueryInstead` to direct TanStack Query usage: - -```typescript -// Legacy options -> TanStack Query options -{ - prefetch: true -> enabled: true - loading -> isLoading - refetchOnWindowFocus: false -> refetchOnWindowFocus: false - - // Response structure - data -> data (direct access, no .data property) - res.status -> Use error handling or onError callback - error -> error -} -``` - -### Common Patterns - -1. **Conditional Fetching**: - -```tsx -// Legacy -useTanStackQueryInstead(route, { prefetch: shouldFetch }); - -// Modern -useQuery({ - queryKey: [route.path], - queryFn: query(route), - enabled: shouldFetch, -}); -``` - -2. **With Parameters**: - -```tsx -// Legacy -useTanStackQueryInstead(route, { - pathParams: { id }, - query: { filter }, -}); - -// Modern -useQuery({ - queryKey: [route.path, id, filter], - queryFn: query(route, { - pathParams: { id }, - queryParams: { filter }, - }), -}); -``` - -3. **Error Handling**: - -```tsx -// Legacy -const { error, res } = useTanStackQueryInstead(route); -if (res?.status === 403) handleForbidden(); - -// Modern -useQuery({ - queryKey: [route.path], - queryFn: query(route, { - silent: true, // Optional: suppress error notifications - }), - // Error handling is now done globally -}); -``` - -## Legacy Support: `useTanStackQueryInstead` - -For existing code or maintaining consistency with older patterns, use our wrapper around TanStack Query: - -```jsx -import { useTanStackQueryInstead } from "@care/request"; -import FooRoutes from "@foo/routes"; - -export default function FooDetails({ children, id }) { - const { res, data, loading, error } = useTanStackQueryInstead( - FooRoutes.getFoo, - { - pathParams: { id }, - }, - ); - - if (loading) return ; - - if (res.status === 403) { - navigate("/forbidden"); - return null; - } - - if (error) { - return ; - } - - return ( - - {data.id} - {data.name} - - ); -} -``` - -### API - -```ts -useTanStackQueryInstead(route: Route, options?: QueryOptions): ReturnType; -``` - -#### `route` - -A route object that specifies the endpoint to fetch data from. - -```ts -const FooRoutes = { - getFoo: { - path: "/api/v1/foo/{id}/", // 👈 The path to the endpoint. Slug parameters can be specified using curly braces. - method: "GET", // 👈 The HTTP method to use. Optional; defaults to "GET". - TRes: Type(), // 👈 The type of the response body (for type inference). - TBody: Type(), // 👈 The type of the request body (for type inference). - noAuth: true, // 👈 Whether to skip adding the Authorization header to the request. - }, -} as const; // 👈 This is important for type inference to work properly. -``` - -#### `options` - -An object that specifies options for the request. - -```ts -const options = { - prefetch: true, // 👈 Whether to prefetch the data when the component mounts. - refetchOnWindowFocus: true, // 👈 Whether to refetch the data when the window regains focus. - - // The following options are passed directly to the underlying `request` function. - pathParams: { id: "123" }, // 👈 The slug parameters to use in the path. - query: { limit: 10 }, // 👈 The query parameters to be added to the request URL. - body: { name: "foo" }, // 👈 The body to be sent with the request. - headers: { "X-Foo": "bar" }, // 👈 Additional headers to be sent with the request. - silent: true, // 👈 Whether to suppress notifications for this request. - - onResponse: (res) => { - if (res.status === 403) { - navigate("/forbidden"); - } - }, -}; -``` - -#### Return Type - -The hook returns an object with the following properties: - -```ts -{ - res: Type | undefined; // 👈 The response object. `undefined` if the request has not been made yet. - data: TRes | null; // 👈 The response body. `null` if the request has not been made yet. - error: any; // 👈 The error that occurred while making the request if any. - loading: boolean; // 👈 Whether the request is currently in progress. - refetch: () => void; // 👈 A function that can be called to refetch the data. -} -``` - -## `request` - -`request` is a function that allows you to fetch data. It is a wrapper around `fetch` that adds some useful features. It can be used in both React components and non-React code. For fetching data in React components, prefer using TanStack Query or `useTanStackQueryInstead`. For mutations, use `request`. - -### `request` usage - -```ts -import { request } from "@care/request"; -import FooRoutes from "@foo/routes"; - -export default async function updateFoo(id: string, object: Foo) { - const { res, data } = await request(FooRoutes.updateFoo, { - pathParams: { id }, - body: object, // 👈 The body is automatically serialized to JSON. - }); - - if (res.status === 403) { - navigate("/forbidden"); - return null; - } - - return data; -} -``` diff --git a/src/Utils/request/api.tsx b/src/Utils/request/api.tsx index c01425c3d74..5da5881de59 100644 --- a/src/Utils/request/api.tsx +++ b/src/Utils/request/api.tsx @@ -17,8 +17,6 @@ import { import { PaginatedResponse } from "@/Utils/request/types"; import { AppointmentPatientRegister } from "@/pages/Patient/Utils"; import { Encounter, EncounterEditRequest } from "@/types/emr/encounter"; -import { MedicationAdministration } from "@/types/emr/medicationAdministration/medicationAdministration"; -import { MedicationStatementRead } from "@/types/emr/medicationStatement"; import { PartialPatientModel, Patient } from "@/types/emr/newPatient"; import { Observation, @@ -116,11 +114,6 @@ const routes = { TBody: Type<{ refresh: JwtTokenObtainPair["refresh"] }>(), }, - token_verify: { - path: "/api/v1/auth/token/verify/", - method: "POST", - }, - checkResetToken: { path: "/api/v1/password_reset/check/", method: "POST", @@ -164,12 +157,6 @@ const routes = { TRes: Type(), }, - userList: { - path: "/api/v1/users/", - method: "GET", - TRes: Type>(), - }, - deleteProfilePicture: { path: "/api/v1/users/{username}/profile_picture/", method: "DELETE", @@ -184,20 +171,6 @@ const routes = { TBody: Type(), }, - // Facility Endpoints - - getPermittedFacilities: { - path: "/api/v1/facility/", - TRes: Type>(), - }, - - createFacility: { - path: "/api/v1/facility/", - method: "POST", - TRes: Type(), - TBody: Type(), - }, - getPermittedFacility: { path: "/api/v1/facility/{id}/", method: "GET", @@ -224,11 +197,6 @@ const routes = { TBody: Type(), }, - getFacilityUsers: { - path: "/api/v1/facility/{facility_id}/get_users/", - TRes: Type>(), - }, - getScheduleAbleFacilityUser: { path: "/api/v1/facility/{facility_id}/schedulable_users/{user_id}/", TRes: Type(), @@ -338,11 +306,6 @@ const routes = { method: "GET", TRes: Type(), }, - downloadResourceRequests: { - path: "/api/v1/resource/", - method: "GET", - TRes: Type(), - }, getResourceComments: { path: "/api/v1/resource/{id}/comment/", method: "GET", @@ -650,22 +613,6 @@ const routes = { }, }, }, - - medicationStatement: { - list: { - path: "/api/v1/patient/{patientId}/medication/statement/", - method: "GET", - TRes: Type>(), - }, - }, - - medicationAdministration: { - list: { - path: "/api/v1/patient/{patientId}/medication/administration/", - method: "GET", - TRes: Type>(), - }, - }, } as const; export default routes; diff --git a/src/Utils/request/handleResponse.ts b/src/Utils/request/handleResponse.ts deleted file mode 100644 index 1a97a5b8b24..00000000000 --- a/src/Utils/request/handleResponse.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { t } from "i18next"; -import { navigate } from "raviger"; -import { toast } from "sonner"; - -import * as Notifications from "@/Utils/Notifications"; -import { RequestResult } from "@/Utils/request/types"; - -/** - * @deprecated in favor of useQuery/useMutation/callApi - */ -export default function handleResponse( - { res, error }: RequestResult, - silent?: boolean, -) { - const notify = silent ? undefined : Notifications; - - if (res === undefined) { - return; - } - - // 404 Not Found - if (res.status === 404) { - toast.error(t("not_found")); - return; - } - - // 400/406 Bad Request - if (res.status === 400 || res.status === 406) { - notify?.BadRequest({ errs: error }); - return; - } - - // Other Errors between 400-599 (inclusive) - if (res.status >= 400 && res.status < 600) { - // Handle invalid token / session expiry - if ( - !silent && - (error?.code === "token_not_valid" || - error?.detail === "Authentication credentials were not provided.") - ) { - if (!location.pathname.startsWith("/session-expired")) { - navigate(`/session-expired?redirect=${window.location.href}`); - } - return; - } - - toast.error((error?.detail as string) || t("something_went_wrong")); - return; - } -} diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index aeabda3e55e..ef32bb1f5d4 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -2,14 +2,13 @@ import careConfig from "@careConfig"; import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; -import { getResponseBody } from "@/Utils/request/request"; import { ApiCallOptions, ApiRoute, HTTPError, PaginatedResponse, } from "@/Utils/request/types"; -import { makeHeaders, makeUrl } from "@/Utils/request/utils"; +import { getResponseBody, makeHeaders, makeUrl } from "@/Utils/request/utils"; import { sleep } from "@/Utils/utils"; export async function callApi>( diff --git a/src/Utils/request/request.ts b/src/Utils/request/request.ts deleted file mode 100644 index 7bbfebb2614..00000000000 --- a/src/Utils/request/request.ts +++ /dev/null @@ -1,91 +0,0 @@ -import careConfig from "@careConfig"; - -import handleResponse from "@/Utils/request/handleResponse"; -import { ApiRoute, RequestOptions, RequestResult } from "@/Utils/request/types"; -import { makeHeaders, makeUrl } from "@/Utils/request/utils"; - -type Options = RequestOptions & { - signal?: AbortSignal; -}; - -/** - * @deprecated use useQuery/useMutation/callApi instead - * - * This no longer ensures that the path params are provided correctly during runtime. - * Usages so far works as path params were passed correctly, but this should not be used anymore. - */ -export default async function request( - { path, method, noAuth }: ApiRoute, - { - query, - body, - pathParams, - onResponse, - silent, - signal, - }: Options = {}, -): Promise> { - const url = `${careConfig.apiUrl}${makeUrl(path, query, pathParams)}`; - - const options: RequestInit = { method, signal }; - - if (body) { - options.body = JSON.stringify(body); - } - - let result: RequestResult = { - res: undefined, - data: undefined, - error: undefined, - }; - - options.headers = makeHeaders(noAuth ?? false); - - try { - const res = await fetch(url, options); - - const data = await getResponseBody(res); - - result = { - res, - data: res.ok ? data : undefined, - error: res.ok ? undefined : (data as Record), - }; - - onResponse?.(result); - handleResponse(result, silent); - - return result; - } catch (error: any) { - result = { error, res: undefined, data: undefined }; - if (error.name === "AbortError") { - return result; - } - } - - console.error(`Request failed `, result.error); - return result; -} - -export async function getResponseBody(res: Response): Promise { - if (!(res.headers.get("content-length") !== "0")) { - return null as TData; - } - - const isJson = res.headers.get("content-type")?.includes("application/json"); - const isImage = res.headers.get("content-type")?.includes("image"); - - if (isImage) { - return (await res.blob()) as TData; - } - - if (!isJson) { - return (await res.text()) as TData; - } - - try { - return await res.json(); - } catch { - return (await res.text()) as TData; - } -} diff --git a/src/Utils/request/types.ts b/src/Utils/request/types.ts index 07bdb032bd0..00f72fcba43 100644 --- a/src/Utils/request/types.ts +++ b/src/Utils/request/types.ts @@ -16,26 +16,6 @@ export interface ApiRoute { noAuth?: boolean; } -/** - * @deprecated in favor of useQuery/useMutation/callApi - */ -export interface RequestResult { - res: Response | undefined; - data: TData | undefined; - error: undefined | Record; -} - -/** - * @deprecated in favor of ApiCallOptions used by useQuery/useMutation/callApi - */ -export interface RequestOptions { - query?: QueryParams; - body?: TBody; - pathParams?: Record; - onResponse?: (res: RequestResult) => void; - silent?: boolean; -} - type ExtractRouteParams = T extends `${infer _Start}{${infer Param}}${infer Rest}` ? Param | ExtractRouteParams diff --git a/src/Utils/request/useQuery.ts b/src/Utils/request/useQuery.ts deleted file mode 100644 index 422e5f96868..00000000000 --- a/src/Utils/request/useQuery.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { useMemo, useRef } from "react"; - -import request from "@/Utils/request/request"; -import { ApiRoute, RequestOptions } from "@/Utils/request/types"; - -import { mergeRequestOptions } from "./utils"; - -export interface QueryOptions extends RequestOptions { - prefetch?: boolean; - key?: string; -} - -/** - * @deprecated use `useQuery` from `@tanstack/react-query` instead. - */ -export default function useTanStackQueryInstead( - route: ApiRoute, - options?: QueryOptions, -) { - const overridesRef = useRef>(); - - // Ensure unique key for each usage of the hook unless explicitly provided - // (hack to opt-out of tanstack query's caching between usages) - const key = useMemo(() => options?.key ?? Math.random(), [options?.key]); - - const { - data: response, - refetch, - isFetching: isLoading, - } = useQuery({ - queryKey: [route.path, options?.pathParams, options?.query, key], - queryFn: async ({ signal }) => { - const resolvedOptions = overridesRef.current - ? mergeRequestOptions(options || {}, overridesRef.current) - : options; - - return await request(route, { ...resolvedOptions, signal }); - }, - enabled: options?.prefetch ?? true, - refetchOnWindowFocus: false, - }); - - return { - data: response?.data, - loading: isLoading, - error: response?.error, - res: response?.res, - /** - * Refetch function that applies new options and fetches fresh data. - */ - refetch: async (overrides?: QueryOptions) => { - overridesRef.current = overrides; - await refetch(); - return response!; - }, - }; -} diff --git a/src/Utils/request/utils.ts b/src/Utils/request/utils.ts index 8edb710f32d..86199a01bbe 100644 --- a/src/Utils/request/utils.ts +++ b/src/Utils/request/utils.ts @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction } from "react"; import { LocalStorageKeys } from "@/common/constants"; -import { QueryParams, RequestOptions } from "@/Utils/request/types"; +import { QueryParams } from "@/Utils/request/types"; export function makeUrl( path: string, @@ -64,27 +64,27 @@ export function getAuthorizationHeader() { return null; } -export function mergeRequestOptions( - options: RequestOptions, - overrides: RequestOptions, -): RequestOptions { - return { - ...options, - ...overrides, - - query: { ...options.query, ...overrides.query }, - body: (options.body || overrides.body) && { - ...(options.body ?? {}), - ...(overrides.body ?? {}), - }, - pathParams: { ...options.pathParams, ...overrides.pathParams }, - - onResponse: (res) => { - options.onResponse?.(res); - overrides.onResponse?.(res); - }, - silent: overrides.silent ?? options.silent, - }; +export async function getResponseBody(res: Response): Promise { + if (!(res.headers.get("content-length") !== "0")) { + return null as TData; + } + + const isJson = res.headers.get("content-type")?.includes("application/json"); + const isImage = res.headers.get("content-type")?.includes("image"); + + if (isImage) { + return (await res.blob()) as TData; + } + + if (!isJson) { + return (await res.text()) as TData; + } + + try { + return await res.json(); + } catch { + return (await res.text()) as TData; + } } export function handleUploadPercentage( diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index add2677343b..78987ad8c59 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -9,7 +9,6 @@ import { Organization, OrganizationParent, } from "@/types/organization/organization"; -import { Quantity } from "@/types/questionnaire/quantity"; const DATE_FORMAT = "DD/MM/YYYY"; const TIME_FORMAT = "hh:mm A"; @@ -93,21 +92,6 @@ function _isAppleDevice() { */ export const isAppleDevice = _isAppleDevice(); -/** - * Conditionally concatenate classes. An alternate replacement for `clsx`. - * - * **Example Usage:** - * ```tsx - * - * // "md:flex p-0" - * ``` - * - * @deprecated Use `cn` from `@/lib/utils` instead. - */ -export const classNames = (...classes: (string | boolean | undefined)[]) => { - return classes.filter(Boolean).join(" "); -}; - export const isUserOnline = (user: { last_login: DateLike }) => { return user.last_login ? dayjs().subtract(5, "minutes").isBefore(user.last_login) @@ -158,20 +142,6 @@ export const formatPatientAge = ( return `${day}${suffixes.day}`; }; -export const mergeQueryOptions = ( - selected: T[], - queryOptions: T[], - compareBy: (obj: T) => T[keyof T], -) => { - if (!selected.length) return queryOptions; - return [ - ...selected, - ...queryOptions.filter( - (option) => !selected.find((s) => compareBy(s) === compareBy(option)), - ), - ]; -}; - /** * A utility method to format an array of string to human readable format. * @@ -212,12 +182,6 @@ export const getMonthStartAndEnd = (date: Date) => { }; }; -export const displayQuantity = (quantity?: Quantity) => { - if (!quantity) return "N/A"; - - return [quantity.value ?? "N/A", quantity.unit].join(" "); -}; - /** * Returns hours and minutes between two dates. * @@ -266,13 +230,6 @@ export const conditionalAttribute = ( return condition ? attributes : {}; }; -export const conditionalArrayAttribute = ( - condition: boolean, - attributes: T[], -) => { - return condition ? attributes : []; -}; - export const stringifyGeoOrganization = (org: Organization) => { const levels: string[] = []; @@ -284,3 +241,12 @@ export const stringifyGeoOrganization = (org: Organization) => { return levels.join(", "); }; + +export const mergeAutocompleteOptions = ( + options: { label: string; value: string }[], + value?: { label: string; value: string }, +) => { + if (!value) return options; + if (options.find((o) => o.value === value.value)) return options; + return [value, ...options]; +}; diff --git a/src/common/constants.tsx b/src/common/constants.tsx index 8fda21a820f..1ce2a011725 100644 --- a/src/common/constants.tsx +++ b/src/common/constants.tsx @@ -62,42 +62,6 @@ export const FACILITY_TYPES: Array = [ { id: 4000, text: "Community Based Organization" }, ]; -export const SHIFTING_FILTER_ORDER: Array = [ - { id: 1, text: "created_date", desc: "ASC Created Date" }, - { id: 2, text: "-created_date", desc: "DESC Created Date" }, - { id: 3, text: "modified_date", desc: "ASC Modified Date" }, - { id: 4, text: "-modified_date", desc: "DESC Modified Date" }, -]; - -export const CONSCIOUSNESS_LEVEL = [ - { id: 20, value: "UNRESPONSIVE" }, - { id: 15, value: "RESPONDS_TO_PAIN" }, - { id: 10, value: "RESPONDS_TO_VOICE" }, - { id: 5, value: "ALERT" }, - { id: 25, value: "AGITATED_OR_CONFUSED" }, - { - id: 30, - value: "ONSET_OF_AGITATION_AND_CONFUSION", - }, -] as const; - -export const LIMB_RESPONSE_OPTIONS = [ - { id: 0, value: "UNKNOWN" }, - { id: 5, value: "STRONG" }, - { id: 10, value: "MODERATE" }, - { id: 15, value: "WEAK" }, - { id: 20, value: "FLEXION" }, - { id: 25, value: "EXTENSION" }, - { id: 30, value: "NONE" }, -] as const; - -export const OXYGEN_MODALITY_OPTIONS = [ - { value: "NASAL_PRONGS" }, - { value: "SIMPLE_FACE_MASK" }, - { value: "NON_REBREATHING_MASK" }, - { value: "HIGH_FLOW_NASAL_CANNULA" }, -] as const; - export const GENDER_TYPES = [ { id: "male", text: "Male", icon: "M" }, { id: "female", text: "Female", icon: "F" }, @@ -109,41 +73,6 @@ export const GENDERS = GENDER_TYPES.map((gender) => gender.id) as [ (typeof GENDER_TYPES)[number]["id"], ]; -export const CONSULTATION_SUGGESTION = [ - { id: "HI", text: "Home Isolation", deprecated: true }, // # Deprecated. Preserving option for backward compatibility (use only for readonly operations) - { id: "A", text: "Admission" }, - { id: "R", text: "Refer to another Hospital", editDisabled: true }, - { id: "OP", text: "OP Consultation" }, - { id: "DC", text: "Domiciliary Care" }, - { id: "DD", text: "Declare Death", editDisabled: true }, -] as const; - -export const RESPIRATORY_SUPPORT = [ - { id: "NIV", value: "NON_INVASIVE" }, - { id: "IV", value: "INVASIVE" }, - { id: "O2", value: "OXYGEN_SUPPORT" }, - { id: "NONE", value: "UNKNOWN" }, -] as const; - -export const VENTILATOR_MODE_OPTIONS = [ - "VCV", - "PCV", - "PRVC", - "APRV", - "VC_SIMV", - "PC_SIMV", - "PRVC_SIMV", - "ASV", - "PSV", -] as const; - -export const INSULIN_INTAKE_FREQUENCY_OPTIONS = [ - "UNKNOWN", - "OD", - "BD", - "TD", -] as const; - export const BLOOD_GROUP_CHOICES = [ { id: "unknown", text: "Unknown" }, { id: "A_positive", text: "A+" }, @@ -173,109 +102,6 @@ export const RESOURCE_STATUS_CHOICES = [ { icon: "l-check-circle", text: "completed" }, ] as const; -export const RESOURCE_FILTER_ORDER: Array = [ - { id: 1, text: "created_date", desc: "ASC Created Date" }, - { id: 2, text: "-created_date", desc: "DESC Created Date" }, - { id: 3, text: "modified_date", desc: "ASC Modified Date" }, - { id: 4, text: "-modified_date", desc: "DESC Modified Date" }, -]; - -export const HEARTBEAT_RHYTHM_CHOICES = [ - "REGULAR", - "IRREGULAR", - "UNKNOWN", -] as const; - -export const NURSING_CARE_PROCEDURES = [ - "oral_care", - "hair_care", - "bed_bath", - "eye_care", - "perineal_care", - "skin_care", - "pre_enema", - "wound_dressing", - "lymphedema_care", - "ascitic_tapping", - "colostomy_care", - "colostomy_change", - "personal_hygiene", - "positioning", - "suctioning", - "ryles_tube_care", - "ryles_tube_change", - "iv_sitecare", - "nubulisation", - "dressing", - "dvt_pump_stocking", - "restrain", - "chest_tube_care", - "tracheostomy_care", - "tracheostomy_tube_change", - "stoma_care", - "catheter_care", - "catheter_change", -] as const; - -export const BOWEL_ISSUE_CHOICES = [ - "NO_DIFFICULTY", - "CONSTIPATION", - "DIARRHOEA", -] as const; - -export const BLADDER_DRAINAGE_CHOICES = [ - "NORMAL", - "CONDOM_CATHETER", - "DIAPER", - "INTERMITTENT_CATHETER", - "CONTINUOUS_INDWELLING_CATHETER", - "CONTINUOUS_SUPRAPUBIC_CATHETER", - "UROSTOMY", -] as const; - -export const BLADDER_ISSUE_CHOICES = [ - "NO_ISSUES", - "INCONTINENCE", - "RETENTION", - "HESITANCY", -] as const; - -export const URINATION_FREQUENCY_CHOICES = [ - "NORMAL", - "DECREASED", - "INCREASED", -] as const; - -export const SLEEP_CHOICES = [ - "EXCESSIVE", - "SATISFACTORY", - "UNSATISFACTORY", - "NO_SLEEP", -] as const; - -export const NUTRITION_ROUTE_CHOICES = [ - "ORAL", - "RYLES_TUBE", - "GASTROSTOMY_OR_JEJUNOSTOMY", - "PEG", - "PARENTERAL_TUBING_FLUID", - "PARENTERAL_TUBING_TPN", -] as const; - -export const ORAL_ISSUE_CHOICES = [ - "NO_ISSUE", - "DYSPHAGIA", - "ODYNOPHAGIA", -] as const; - -export const APPETITE_CHOICES = [ - "INCREASED", - "SATISFACTORY", - "REDUCED", - "NO_TASTE_FOR_FOOD", - "CANNOT_BE_ASSESSED", -] as const; - export const FACILITY_FEATURE_TYPES: { id: number; name: string; @@ -505,233 +331,6 @@ export const DEFAULT_ALLOWED_EXTENSIONS = [ "application/vnd.oasis.opendocument.spreadsheet,application/pdf", ]; -export const HumanBodyPaths = { - anterior: [ - { - d: "M535.244,212.572c32.253.43,32.684-31.823,32.684-31.823,9.891-.215,14.191-19.783,13.331-23.653s-7.526-1.5-7.526-1.5c3.656-30.1-9.676-48.38-17.847-53.756S535.244,95.6,535.244,95.6h.43s-12.472.86-20.643,6.236-21.5,23.653-17.846,53.756c0,0-6.666-2.365-7.526,1.5s3.44,23.438,13.331,23.653c0,0,.43,32.253,32.684,31.823Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorHead", - }, - { - d: "M512.129,213.97s31.608,4.954,47.574-1.394v14.456s-26.287,4.355-47.574,0Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorNeck", - }, - { - d: "M505.355,231.279s-56.766,25.8-69.452,34.4c0,0,15.7,20.857,21.072,66.872C456.975,332.555,469.417,246.838,505.355,231.279Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightShoulder", - }, - { - d: "M526.482,232.838l.806,137.346s-46.607-22.2-67.745,18.762C459.543,388.946,455.685,234.612,526.482,232.838Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightChest", - }, - { - d: "M433.108,269.768s34.728,55.552,18.279,141.992c0,0-19.57-9.107-33.761-7.333,0,0-1.613-106.276,0-110.952S429.721,271.058,433.108,269.768Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightArm", - }, - { - d: "M415.207,408.781s27.254-.968,35.963,11.45c0,0-7.58,59.024-13.547,77.57s-19.03,56.766-19.03,56.766l-22.254-2.742s1.451-34.672,1.29-45.477,5-49.993,9.514-62.249S415.207,408.781,415.207,408.781Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightForearm", - }, - { - d: "M396.6,556.524l18.245,2.606a1.808,1.808,0,0,1,1.565,1.776c.049,6.373.053,30.692-2.6,41.987-2.568,10.951-16.244,28.022-26.205,35.726a4.126,4.126,0,0,1-6.575-2.7c-.192-1.322-.39-2.923-.584-4.855a1.828,1.828,0,0,0-2.054-1.637l-4.174.551a1.818,1.818,0,0,1-2.026-2.171c.631-3.043,1.887-8.187,3.72-11.529,2.591-4.724,5.9-18.948,5.442-26.76a1.79,1.79,0,0,0-1.514-1.635,7.118,7.118,0,0,0-5.448,1c-1.364,1.043-3.83,4.558-5.963,7.825-1.941,2.973-6.715.452-5.152-2.736.018-.037.037-.074.056-.111,1.936-3.71,13.063-18.708,16.288-24.513,2.9-5.221,13.627-8.747,15.171-11.984A1.706,1.706,0,0,1,396.6,556.524Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightHand", - }, - { - d: "M674.037,556.2l-18.244,2.606a1.808,1.808,0,0,0-1.566,1.776c-.049,6.373-.052,30.692,2.6,41.988,2.569,10.951,16.244,28.021,26.205,35.726a4.126,4.126,0,0,0,6.576-2.7c.191-1.322.389-2.922.584-4.855a1.827,1.827,0,0,1,2.053-1.637l4.174.551a1.818,1.818,0,0,0,2.027-2.17c-.632-3.043-1.888-8.188-3.721-11.53-2.59-4.723-5.9-18.948-5.442-26.76a1.79,1.79,0,0,1,1.515-1.634,7.114,7.114,0,0,1,5.447,1c1.364,1.043,3.83,4.558,5.964,7.826,1.94,2.973,6.715.451,5.151-2.736-.018-.038-.037-.075-.056-.112-1.935-3.709-13.063-18.707-16.288-24.513-2.9-5.221-13.627-8.746-15.171-11.984A1.707,1.707,0,0,0,674.037,556.2Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftHand", - }, - { - d: "M544.705,232.838h19.137s18.062,15.643,20,19.513,29.888,42.79,26.878,128.154c0,0-16.557-16.556-31.178-15.051,0,0,2.365-33.114-34.834-34.619Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftChest", - }, - { - d: "M569.432,231.279s61.927,31.824,65.153,35.694c0,0-12.9,9.752-18.707,73.791C615.878,340.764,610.072,268.048,569.432,231.279Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftShoulder", - }, - { - d: "M638.455,271.058s14.407,18.923,14.837,23.223-1.291,105.362.86,108.8c0,0-26.233,1.29-34.834,9.891,0,0-4.3-51.176.86-78.484S633.079,279.659,638.455,271.058Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftArm", - }, - { - d: "M621.038,419s16.342-12.257,33.974-10.537c0,0,7.741,26.233,8.816,34.189s10.321,49.241,9.246,66.658.087,41.069.087,41.069-16.214,3.44-20.084,4.731c0,0-17.2-46.661-18.062-52.036S620.982,426.52,621.038,419Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftForearm", - }, - { - d: "M510.758,934.272s-20.723,1.451-24.973,1.5a56.32,56.32,0,0,0-1.556,10.672c0,4.355.484,25.481-.645,28.061s-21.771,27.254-23.383,30.641.645,8.386,1.935,9.192,2.1,4.757,4.193,5.644c1.807.765,3.064,3.709,5.644,4.032s10.482-.645,12.418.726c0,0,.887,3.144,2.58,3.306.864.082,5.644,1.774,10.644-5.967s13.04-35.019,13.439-37.791c.249-1.732-1.183-2.125-1.506-5.189a112.484,112.484,0,0,1,1.855-20.64C513.419,948.3,510.758,934.272,510.758,934.272Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightFoot", - }, - { - d: "M563.251,934.191s20.756.564,25.006.616c0,0,.151,7.125.151,11.479s.162,24.351,1.29,26.932,22.531,27.576,24.144,30.963-.645,8.386-1.935,9.192-2.1,4.758-4.193,5.645c-1.807.764-3.064,3.709-5.645,4.031s-10.482-.645-12.417.726c0,0-.887,3.145-2.581,3.306-.864.082-5.644,1.774-10.643-5.967s-13.04-35.018-13.439-37.79c-.25-1.733,1.182-2.126,1.5-5.19a112.484,112.484,0,0,0-1.855-20.64C560.623,947.334,563.251,934.191,563.251,934.191Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftFoot", - }, - { - d: "M485.2,932.363l24.513-1.4s1.666-37.2,2.526-41.285,4.731-85.149,4.086-99.771c0,0-30,2.527-49.348-3.924,0,0-6.451,44.026-1.828,62.841C467.775,859.527,484.874,929.6,485.2,932.363Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightLeg", - }, - { - d: "M469.231,420.715s-5.966-30.318-4.515-34.834a115.141,115.141,0,0,1,16.772-10.966c10.428-5.483,29.727-6.773,36.339-3.548,5.81,2.834,4.972,2.548,13.439,4.73l.054-142.13h9.192V334.92s32.415,1.291,31.931,33.06a72.9,72.9,0,0,0,8.869,2.419c5.322,1.129,23.062,9.031,25.642,19.675,0,0-4.945,22.2-3.655,32.684,0,0-39.4-29.835-47.306-31.609s-12.959,2.31-16.933,2.8c-4.483.547-11.71-.628-18.142-2.9C514.306,388.7,475.2,414.909,469.231,420.715Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLowerChest", - }, - { - d: "M461.813,481.665c2.43-11.313,8.042-43.207,7.1-55.467,0,0,48.3-30.56,50.88-30.4s12.122,5.564,23.841,2.338c0,0,6.719-3.225,13.331.162s34.874,24.149,46.324,28.987c0,0-.524,28.746,1.573,37.777s10.159,42.091,10.966,46.123,0,.806,0,.806-58.057,50.155-59.669,52.574c0,0-6.451-6.29-20.481-6.774s-20.643,6.774-20.643,6.774l-60.152-51.122S460.965,485.617,461.813,481.665Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorAbdomen", - }, - { - d: "M554.381,790.77s30.748,3.226,51.39-4.945c0,0,3.441,40.424,0,63.432s-16.449,76.871-17.094,81.816c0,0-23.33.108-25.91-1.4,0,0-3.011-33.328-3.871-43S550.08,810.982,554.381,790.77Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftLeg", - }, - { - d: "M454.072,520.056s-4.515,29.35-6.128,48.7.323,59.346,6.128,81.278,21.288,89.343,14.514,131.272c0,0,20.464,8.064,47.808,4.516,0,0,7.2-74.822,6.7-87.73,0,0,3.333-50.745,3.333-58.7s1.72-27.738,1.72-27.738-20.642-10.106-14.837-44.08C513.311,567.576,462.351,524.571,454.072,520.056Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorRightThigh", - }, - { - d: "M553.114,785.825s37.713,2.741,50.615-3.548c0,0-6.451-35.8-1.129-63.325s19.943-77.408,20.8-92.03,2.33-87.3-7.221-108.371l-56.426,49.455s3.441,37.629-18.922,44.725c0,0,7.741,68.807,7.741,78.913S554.857,777.654,553.114,785.825Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorLeftThigh", - }, - { - d: "M535.624,610.466s16.722-10.1,18.818-27.355-3.386-17.578-5.805-18.545-13.063-1.291-13.063-1.291h.1s-10.644.323-13.063,1.291-7.9,1.29-5.806,18.545S535.624,610.466,535.624,610.466Z", - transform: "translate(-362.967 -95.599)", - region: "AnteriorGroin", - }, - ], - - posterior: [ - { - d: "M 506.9838 158.0121 C 509.6029 173.1336 512.1258 187.9477 521.5039 184.4407 C 517.7283 191.6346 525.6919 202.9266 528.0919 210.8841 C 544.9623 208.3461 562.3174 208.3461 579.1878 210.8841 C 581.5893 202.9236 589.5363 191.6662 585.7863 184.4511 C 595.6744 187.4586 596.8188 174.3021 600.3813 158.5926 C 600.1173 156.4611 595.9999 158.5806 594.7788 159.0816 C 597.7384 128.3122 591.2088 97.1811 553.7104 97.22 C 516.1444 97.1497 509.5249 128.2116 512.5008 159.0891 C 511.0564 158.4651 508.4914 157.0971 506.9838 158.0121 Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorHead", - }, - { - d: "M 503.129 213.97 s 30.871 -1.97 46.871 0.03 v 12.456 s -26 -2.456 -47.574 0 Z", - transform: "translate(-362.967 -95.599)", - region: "PosteriorNeck", - }, - { - d: "M545.584,228.037V361.6s-13.6,10.828-25.282,13.145c-10.077,2-36.162,3.374-36.766-.857S478.9,239.117,545.584,228.037Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftChest", - }, - { - d: "M563.865,228.037V361.6s13.6,10.828,25.282,13.145c10.076,2,36.161,3.374,36.766-.857S630.546,239.117,563.865,228.037Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightChest", - }, - { - d: "M550.973,228.188h8.914l.151,136.435s20.7,17.828,59.681,16.317c0,0-4.684,38.528-1.057,56.508s9.216,41.248,9.216,41.248-77.812,30.218-145.954-.151c0,0,9.67-35.96,9.972-58.321a167.6,167.6,0,0,0-4.23-39.888s37.924,5.439,62.1-15.713C549.764,364.623,550.52,228.188,550.973,228.188Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorAbdomen", - }, - { - d: "M523.223,230.857s-40.694,20.548-50.968,25.182-11.08,5.439-11.08,5.439,15.512,18.735,18.533,70.509C479.708,331.987,489.58,244.354,523.223,230.857Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftShoulder", - }, - { - d: "M587.084,230.857s40.693,20.548,50.968,25.182,11.08,5.439,11.08,5.439S633.62,280.213,630.6,331.987C630.6,331.987,620.726,244.354,587.084,230.857Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightShoulder", - }, - { - d: "M457.951,265.306s-12.49,14.706-13.5,29.613,1.813,82.194.6,95.691c0,0,15.512-1.209,22.16,3.022s9.872,4.23,9.872,4.23,3.223-32.232,1.41-53.385S467.823,277.393,457.951,265.306Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftArm", - }, - { - d: "M444.655,394.639s3.627-1.209,8.864,1.612,21.153,4.835,21.153,4.835a241.987,241.987,0,0,1-6.245,50.968c-6.446,27.8-23.167,79.977-22.966,81.992,0,0-17.325-4.03-20.951-3.828,0,0,1.209-21.354,1.612-31.427s.2-42.91,6.648-63.659S444.454,396.049,444.655,394.639Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftForearm", - }, - { - d: "M423.5,533.844s-4.029,2.82-7.454,5.036-12.49,13.1-15.311,18.131-11.482,15.915-10.274,16.923,5.44.2,7.454-2.216,7.051-8.663,10.476-7.253c0,0,1.007,12.087-3.224,22.966s-4.633,13.7-4.633,13.7,2.591,2.22,7.063.809q.291-.091.592-.2s1.612,4.835.806,8.864,3.022,3.425,7.655,1.007,21.959-22.562,24.175-35.053,1.611-40.895,1.611-40.895S427.33,534.65,423.5,533.844Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftHand", - }, - { - d: "M650.678,265.306s12.49,14.706,13.5,29.613-1.813,82.194-.6,95.691c0,0-15.512-1.209-22.16,3.022s-9.871,4.23-9.871,4.23-3.224-32.232-1.41-53.385S640.807,277.393,650.678,265.306Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightArm", - }, - { - d: "M663.974,394.639s-3.626-1.209-8.864,1.612-21.153,4.835-21.153,4.835a242.066,242.066,0,0,0,6.245,50.968c6.447,27.8,23.168,79.977,22.966,81.992,0,0,17.325-4.03,20.951-3.828,0,0-1.208-21.354-1.611-31.427s-.2-42.91-6.648-63.659S664.175,396.049,663.974,394.639Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightForearm", - }, - { - d: "M685.127,533.844s4.029,2.82,7.453,5.036,12.491,13.1,15.311,18.131,11.483,15.915,10.274,16.923-5.439.2-7.454-2.216-7.051-8.663-10.475-7.253c0,0-1.008,12.087,3.223,22.966s4.633,13.7,4.633,13.7-2.59,2.22-7.062.809q-.291-.091-.593-.2s-1.612,4.835-.806,8.864-3.022,3.425-7.655,1.007-21.958-22.562-24.174-35.053-1.612-40.895-1.612-40.895S681.3,534.65,685.127,533.844Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightHand", - }, - { - d: "M552.635,495.366s0,66.279-.6,69.9c-.051.277-.126.982-.2,2.065-5.691,6.673-27.473,28.254-58.673,9.04a10.164,10.164,0,0,1-1.738-1.309c-23.066-21.783-7.076-50.968-6.371-52.2l-2.216-1.234c-.176.327-17.652,32.107,6.849,55.249a14.16,14.16,0,0,0,2.166,1.662c9.519,5.842,18.232,8.033,25.988,8.033,16.116,0,27.977-9.519,33.642-15.235-1.661,20.07-6.144,82.369-6.5,86-.4,4.231-7.605,77.51-7.605,80.935,0,0-36.111-4.785-45.579-2.972,0,0,.2-37.672-2.821-59.63s-14.5-65.473-15.914-101.936-1.411-65.473,7.453-91.46C480.514,482.272,499.048,497.582,552.635,495.366Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftThighAndButtock", - }, - { - d: "M555.471,495.366s0,66.279.6,69.9c.051.277.126.982.2,2.065,5.691,6.673,27.473,28.254,58.673,9.04a10.164,10.164,0,0,0,1.738-1.309c23.066-21.783,7.076-50.968,6.371-52.2l2.216-1.234c.176.327,17.652,32.107-6.85,55.249a14.151,14.151,0,0,1-2.165,1.662c-9.519,5.842-18.232,8.033-25.988,8.033-16.116,0-27.977-9.519-33.643-15.235,1.662,20.07,6.145,82.369,6.5,86,.4,4.231,7.605,77.51,7.605,80.935,0,0,36.111-4.785,45.579-2.972,0,0-.2-37.672,2.82-59.63s14.5-65.473,15.915-101.936,1.41-65.473-7.453-91.46C627.592,482.272,609.058,497.582,555.471,495.366Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightThighAndButtock", - }, - { - d: "M492.2,739.529s21.354-2.418,42.909,3.425c0,0,3.627,43.312,1.612,61.846s-7.655,75.445-6.849,80.078c0,0-19.944.907-25.988,2.518,0,0-2.619-29.009-9.267-49.154S486.961,754.839,492.2,739.529Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftLeg", - }, - { - d: "M617.088,739.529s-21.354-2.418-42.909,3.425c0,0-3.626,43.312-1.612,61.846s7.655,75.445,6.85,80.078c0,0,19.944.907,25.987,2.518,0,0,2.619-29.009,9.267-49.154S622.326,754.839,617.088,739.529Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightLeg", - }, - { - d: "M504.387,891.023s17.728-.806,24.879-2.619c0,0,2.015,6.245,1.209,18.131s-1.007,21.555-.6,23.771,1.813,9.67-1.209,15.512S520,967.172,516.978,972.007s-10.275,5.439-11.886-1.611c0,0-1.813,3.424-7.857,1.41s-9.67-1.209-11.483-5.44-4.835-11.684-1.41-16.922,18.937-18.534,20.145-25.182S505.6,895.455,504.387,891.023Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorLeftFoot", - }, - { - d: "M604.752,891.023s-17.728-.806-24.88-2.619c0,0-2.014,6.245-1.209,18.131s1.008,21.555.605,23.771-1.813,9.67,1.209,15.512,8.662,21.354,11.684,26.189,10.274,5.439,11.886-1.611c0,0,1.813,3.424,7.856,1.41s9.67-1.209,11.483-5.44,4.835-11.684,1.41-16.922-18.936-18.534-20.145-25.182S603.543,895.455,604.752,891.023Z", - transform: "translate(-390.349 -94.472)", - region: "PosteriorRightFoot", - }, - ], -} as const; - -export type HumanBodyRegion = (typeof HumanBodyPaths)[ - | "anterior" - | "posterior"][number]["region"]; - -export const PressureSoreExudateAmountOptions = [ - "None", - "Light", - "Moderate", - "Heavy", -] as const; - -export const PressureSoreTissueTypeOptions = [ - "Closed", - "Epithelial", - "Granulation", - "Slough", - "Necrotic", -] as const; - export const FILE_EXTENSIONS = { IMAGE: ["jpeg", "jpg", "png", "gif", "svg", "bmp", "webp", "jfif"], AUDIO: ["mp3", "wav"], diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx index c79eacaa7ba..dfe4aaac4de 100644 --- a/src/components/Auth/Login.tsx +++ b/src/components/Auth/Login.tsx @@ -504,7 +504,7 @@ const Login = (props: LoginProps) => { )} - {isCaptchaEnabled && ( + {isCaptchaEnabled && reCaptchaSiteKey && ( void; imageUrl?: string; handleUpload: (file: File, onError: () => void) => Promise; handleDelete: (onError: () => void) => Promise; - onClose?: () => void; hint?: React.ReactNode; } @@ -48,10 +52,10 @@ type IVideoConstraint = const AvatarEditModal = ({ title, open, + onOpenChange, imageUrl, handleUpload, handleDelete, - onClose, hint, }: Props) => { const [isProcessing, setIsProcessing] = useState(false); @@ -91,7 +95,7 @@ const AvatarEditModal = ({ setPreview(undefined); setIsProcessing(false); setSelectedFile(undefined); - onClose?.(); + onOpenChange(false); }; useEffect(() => { @@ -179,246 +183,246 @@ const AvatarEditModal = ({ const hintMessage = hint || defaultHint; return ( - - - - {!isCameraOpen ? ( - <> - {preview || imageUrl ? ( - <> - - - - - {hintMessage} - - > - ) : ( - - + + + {title} + + + + {!isCameraOpen ? ( + <> + {preview || imageUrl ? ( + <> + + + + + {hintMessage} + + > + ) : ( + - - - - {dragProps.fileDropError !== "" - ? dragProps.fileDropError - : `${t("drag_drop_image_to_upload")}`} - - - {t("no_image_found")}. {hintMessage} - - - )} + + + + + {dragProps.fileDropError !== "" + ? dragProps.fileDropError + : `${t("drag_drop_image_to_upload")}`} + + + {t("no_image_found")}. {hintMessage} + + + )} - - + + + + + + {t("upload_an_image")} + + + + { + setConstraint(() => VideoConstraints.user); + setIsCameraOpen(true); + }} > - - - {t("upload_an_image")} - - + {`${t("open_camera")}`} - - { - setConstraint(() => VideoConstraints.user); - setIsCameraOpen(true); - }} - > - {`${t("open")} ${t("camera")}`} - - - { - e.stopPropagation(); - closeModal(); - dragProps.setFileDropError(""); - }} - disabled={isProcessing} - > - {t("cancel")} - - {imageUrl && ( + { + e.stopPropagation(); + closeModal(); + dragProps.setFileDropError(""); + }} disabled={isProcessing} > - {t("delete")} + {t("cancel")} - )} - - {isProcessing ? ( - - ) : ( - - )} - - {isProcessing ? `${t("uploading")}...` : `${t("save")}`} - - - - > - ) : ( - <> - - {!previewImage ? ( - <> - { - setIsCameraOpen(false); - toast.warning(t("camera_permission_denied")); - }} - /> - > - ) : ( - <> - - > - )} - - {/* buttons for mobile screens */} - - {!previewImage ? ( - <> - - - {`${t("switch")} ${t("camera")}`} - - { - captureImage(); - }} - > - - {t("capture")} - - > - ) : ( - <> + {imageUrl && ( { - setPreviewImage(null); - }} - > - {t("retake")} - - - {isCaptureImgBeingUploaded ? ( - <> - - {`${t("submitting")}...`} - > - ) : ( - <> {t("submit")}> - )} + {t("delete")} - > - )} - - { - setPreviewImage(null); - setIsCameraOpen(false); - webRef.current.stopCamera(); - }} - disabled={isProcessing} - > - {t("close")} - - - > - )} + )} + + {isProcessing ? ( + + ) : ( + + )} + + {isProcessing ? `${t("uploading")}...` : `${t("save")}`} + + + + > + ) : ( + <> + + {!previewImage ? ( + <> + { + setIsCameraOpen(false); + toast.warning(t("camera_permission_denied")); + }} + /> + > + ) : ( + <> + + > + )} + + {/* buttons for mobile screens */} + + {!previewImage ? ( + <> + + + {`${t("switch")} ${t("camera")}`} + + { + captureImage(); + }} + > + + {t("capture")} + + > + ) : ( + <> + { + setPreviewImage(null); + }} + > + {t("retake")} + + + {isCaptureImgBeingUploaded ? ( + <> + + {`${t("submitting")}...`} + > + ) : ( + <> {t("submit")}> + )} + + > + )} + + { + setPreviewImage(null); + setIsCameraOpen(false); + webRef.current.stopCamera(); + }} + disabled={isProcessing} + > + {t("close")} + + + > + )} + - - + + ); }; diff --git a/src/components/Common/ConfirmDialog.tsx b/src/components/Common/ConfirmDialog.tsx deleted file mode 100644 index 78cebe23f8e..00000000000 --- a/src/components/Common/ConfirmDialog.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Button, ButtonVariant } from "@/components/ui/button"; - -import DialogModal from "@/components/Common/Dialog"; - -type ConfirmDialogProps = { - className?: string; - title: React.ReactNode; - description?: React.ReactNode; - disabled?: boolean; - show: boolean; - action: React.ReactNode; - variant?: ButtonVariant; - onClose: () => void; - onConfirm: () => void; - children?: React.ReactNode; - cancelLabel?: string; - name?: string; -}; - -const ConfirmDialog = ({ - disabled, - variant, - action, - onConfirm, - cancelLabel, - children, - name, - ...props -}: ConfirmDialogProps) => { - return ( - - {children} - - - {cancelLabel} - - - {action} - - - - ); -}; - -export default ConfirmDialog; diff --git a/src/components/Common/Dialog.tsx b/src/components/Common/Dialog.tsx deleted file mode 100644 index a67981dd3f8..00000000000 --- a/src/components/Common/Dialog.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { - Dialog, - DialogPanel, - DialogTitle, - Transition, - TransitionChild, -} from "@headlessui/react"; - -import { classNames } from "@/Utils/utils"; - -type DialogProps = { - title: React.ReactNode; - description?: React.ReactNode; - show: boolean; - onClose: () => void; - children: React.ReactNode; - className?: string; - titleAction?: React.ReactNode; - fixedWidth?: boolean; -}; - -const DialogModal = (props: DialogProps) => { - const { - title, - description, - show, - onClose, - children, - className, - fixedWidth = true, - } = props; - return ( - - - - - - - - - - - - - - {title} - - {description} - - - {props.titleAction} - - {children} - - - - - - - - ); -}; - -export default DialogModal; diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index fbcaf9ae69a..f5c44865549 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -16,10 +16,14 @@ import useKeyboardShortcut from "use-keyboard-shortcut"; import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import CircularProgress from "@/components/Common/CircularProgress"; -import DialogModal from "@/components/Common/Dialog"; -import { StateInterface } from "@/components/Files/FileUpload"; import { FileUploadModel } from "@/components/Patient/models"; const PDFViewer = lazy(() => import("@/components/Common/PDFViewer")); @@ -35,6 +39,19 @@ export const zoom_values = [ "scale-200", ]; +export interface StateInterface { + open: boolean; + isImage: boolean; + name: string; + extension: string; + zoom: number; + isZoomInDisabled: boolean; + isZoomOutDisabled: boolean; + rotation: number; + id?: string; + associating_id?: string; +} + type FilePreviewProps = { title?: ReactNode; description?: ReactNode; @@ -148,11 +165,11 @@ const FilePreviewDialog = (props: FilePreviewProps) => { }; function getRotationClass(rotation: number) { - let normalizedRotation = ((rotation % 360) + 360) % 360; // Normalize rotation to be within [0, 360) + let normalizedRotation = ((rotation % 360) + 360) % 360; if (normalizedRotation > 180) { - normalizedRotation -= 360; // Adjust to be within [-180, 180) + normalizedRotation -= 360; } - return normalizedRotation === -90 // Special case for -90 rotation since tailwind doesn't support 270deg + return normalizedRotation === -90 ? "-rotate-90" : `rotate-${normalizedRotation}`; } @@ -164,232 +181,236 @@ const FilePreviewDialog = (props: FilePreviewProps) => { ); return ( - { - handleClose(); - }} - title={{t("file_preview")}} - show={show} - > - {fileUrl ? ( - <> - - - - - - - {fileNameTooltip} - - - - - {fileName} + !open && handleClose()}> + + + + {t("file_preview")} + + + + {fileUrl ? ( + <> + + + + + + + {fileNameTooltip} + + + + + {fileName} + + + + + {uploadedFiles && + uploadedFiles[index] && + uploadedFiles[index].created_date && ( + + {t("created_on")}{" "} + {new Date( + uploadedFiles[index].created_date!, + ).toLocaleString("en-US", { + dateStyle: "long", + timeStyle: "short", + })} - - - - {uploadedFiles && - uploadedFiles[index] && - uploadedFiles[index].created_date && ( - - {t("created_on")}{" "} - {new Date( - uploadedFiles[index].created_date!, - ).toLocaleString("en-US", { - dateStyle: "long", - timeStyle: "short", - })} - + )} + + + {downloadURL && downloadURL.length > 0 && ( + + + + {t("download")} + + )} + + {t("close")} + + - - {downloadURL && downloadURL.length > 0 && ( - - - - {t("download")} - + + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index - 1)} + disabled={index <= 0} + aria-label="Previous file" + > + )} - - {t("close")} - - - - - {uploadedFiles && uploadedFiles.length > 1 && ( - handleNext(index - 1)} - disabled={index <= 0} - aria-label="Previous file" - > - - - )} - - {file_state.isImage ? ( - - ) : file_state.extension === "pdf" ? ( - }> - { - setPage(1); - setNumPages(numPages); - }} - pageNumber={page} - scale={scale} + + {file_state.isImage ? ( + - - ) : previewExtensions.includes(file_state.extension) ? ( - - ) : ( - - }> + { + setPage(1); + setNumPages(numPages); + }} + pageNumber={page} + scale={scale} + /> + + ) : previewExtensions.includes(file_state.extension) ? ( + - {t("file_preview_not_supported")} - - )} - + ) : ( + + + {t("file_preview_not_supported")} + + )} + - {uploadedFiles && uploadedFiles.length > 1 && ( - handleNext(index + 1)} - disabled={index >= uploadedFiles.length - 1} - aria-label="Next file" - > - - - )} - - - - {file_state.isImage && ( - <> - {[ - [ - t("Zoom In"), - "l-search-plus", - handleZoomIn, - file_state.zoom === zoom_values.length, - ], - [ - `${25 * file_state.zoom}%`, - false, - () => { - setFileState({ ...file_state, zoom: 4 }); - }, - false, - ], - [ - t("Zoom Out"), - "l-search-minus", - handleZoomOut, - file_state.zoom === 1, - ], - [ - t("Rotate Left"), - "l-corner-up-left", - () => handleRotate(-90), - false, - ], - [ - t("Rotate Right"), - "l-corner-up-right", - () => handleRotate(90), - false, - ], - ].map((button, index) => ( - void} - className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" - disabled={button[3] as boolean} - > - {button[1] && ( - - )} - {button[0] as string} - - ))} - > - )} - {file_state.extension === "pdf" && ( - <> - {[ - ["Zoom In", "l-search-plus", handleZoomIn, scale >= 2], - [`${Math.round(scale * 100)}%`, false, () => {}, false], - ["Zoom Out", "l-search-minus", handleZoomOut, scale <= 0.5], - [ - "Previous", - "l-arrow-left", - () => setPage((prev) => prev - 1), - page === 1, - ], - [`${page}/${numPages}`, false, () => ({}), false], - [ - "Next", - "l-arrow-right", - () => setPage((prev) => prev + 1), - page === numPages, - ], - ].map((button, index) => ( - void} - className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" - disabled={button[3] as boolean} - > - {button[1] && ( - - )} - {button[0] as string} - - ))} - > + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index + 1)} + disabled={index >= uploadedFiles.length - 1} + aria-label={t("next_file")} + > + + )} + + + {file_state.isImage && ( + <> + {[ + [ + t("zoom_in"), + "l-search-plus", + handleZoomIn, + file_state.zoom === zoom_values.length, + ], + [ + `${25 * file_state.zoom}%`, + false, + () => { + setFileState({ ...file_state, zoom: 4 }); + }, + false, + ], + [ + t("zoom_out"), + "l-search-minus", + handleZoomOut, + file_state.zoom === 1, + ], + [ + t("rotate_left"), + "l-corner-up-left", + () => handleRotate(-90), + false, + ], + [ + t("rotate_right"), + "l-corner-up-right", + () => handleRotate(90), + false, + ], + ].map((button, index) => ( + void} + className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" + disabled={button[3] as boolean} + > + {button[1] && ( + + )} + {button[0] as string} + + ))} + > + )} + {file_state.extension === "pdf" && ( + <> + {[ + [t("zoom_in"), "l-search-plus", handleZoomIn, scale >= 2], + [`${Math.round(scale * 100)}%`, false, () => {}, false], + [ + t("zoom_out"), + "l-search-minus", + handleZoomOut, + scale <= 0.5, + ], + [ + t("previous"), + "l-arrow-left", + () => setPage((prev) => prev - 1), + page === 1, + ], + [`${page}/${numPages}`, false, () => ({}), false], + [ + t("next"), + "l-arrow-right", + () => setPage((prev) => prev + 1), + page === numPages, + ], + ].map((button, index) => ( + void} + className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" + disabled={button[3] as boolean} + > + {button[1] && ( + + )} + {button[0] as string} + + ))} + > + )} + + + > + ) : ( + + - > - ) : ( - - - - )} - + )} + + ); }; diff --git a/src/components/Common/HelperComponents.tsx b/src/components/Common/HelperComponents.tsx deleted file mode 100644 index 5100476284e..00000000000 --- a/src/components/Common/HelperComponents.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Transition, TransitionEvents } from "@headlessui/react"; -import { ReactNode } from "react"; - -type DropdownMenuTransitionProps = { - show?: boolean | undefined; - children: ReactNode; -} & TransitionEvents; - -export const DropdownTransition = ({ - show, - children, - ...transitionEvents -}: DropdownMenuTransitionProps) => ( - - {children} - -); diff --git a/src/components/Common/LanguageSelector.tsx b/src/components/Common/LanguageSelector.tsx index e1f3446eca0..a3b9d5c3d5d 100644 --- a/src/components/Common/LanguageSelector.tsx +++ b/src/components/Common/LanguageSelector.tsx @@ -2,7 +2,9 @@ import careConfig from "@careConfig"; import { useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { classNames, keysOf } from "@/Utils/utils"; +import { cn } from "@/lib/utils"; + +import { keysOf } from "@/Utils/utils"; import { LANGUAGES } from "@/i18n"; export const LanguageSelector = (props: any) => { @@ -27,7 +29,7 @@ export const LanguageSelector = (props: any) => { return ( { @@ -32,7 +34,7 @@ export const LanguageSelectorLogin = () => { handleLanguage(e)} - className={classNames( + className={cn( "text-primary-400 hover:text-primary-600", (i18n.language === e || (i18n.language === "en-US" && e === "en")) && diff --git a/src/components/Common/Tabs.tsx b/src/components/Common/Tabs.tsx deleted file mode 100644 index 9c375db618e..00000000000 --- a/src/components/Common/Tabs.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { type ReactNode, useEffect, useRef } from "react"; - -import useWindowDimensions from "@/hooks/useWindowDimensions"; - -import { classNames } from "@/Utils/utils"; - -export default function Tabs(props: { - className?: string; - currentTab: string | number; - onTabChange: (value: string | number) => void; - tabs: { text: ReactNode; value: string | number; id?: string }[]; -}) { - const { className, currentTab, onTabChange, tabs } = props; - const ref = useRef(null); - const tabSwitcherRef = useRef(null); - - const dimensions = useWindowDimensions(); - - useEffect(() => { - const currentTabIndex = tabs.findIndex((t) => t.value === currentTab); - if ( - typeof currentTabIndex != "number" || - !ref.current || - !tabSwitcherRef.current - ) - return; - const tabButton = ref.current.querySelectorAll("button")[currentTabIndex]; - if (!tabButton) return; - tabSwitcherRef.current.style.width = tabButton.clientWidth + "px"; - tabSwitcherRef.current.style.left = - tabButton.getBoundingClientRect().left - - ref.current.getBoundingClientRect().left + - ref.current.scrollLeft + - "px"; - }, [currentTab, tabSwitcherRef.current, ref.current, dimensions]); - - return ( - - - {/* There has to be a better way of handling this... */} - {tabs.map((tab, i) => ( - - {tab.text} - - ))} - - {tabs.map((tab, i) => ( - onTabChange(tab.value)} - className={`${currentTab === tab.value ? "text-white" : "text-primary-500 hover:text-primary-600"} flex-1 whitespace-nowrap px-6 py-2 text-sm font-semibold transition-all`} - > - {tab.text} - - ))} - - - ); -} diff --git a/src/components/Common/UpdatableApp.tsx b/src/components/Common/UpdatableApp.tsx index 814779a99d3..3a383d73408 100644 --- a/src/components/Common/UpdatableApp.tsx +++ b/src/components/Common/UpdatableApp.tsx @@ -1,12 +1,12 @@ import { Popover, Transition } from "@headlessui/react"; import { ReactNode, useEffect, useState } from "react"; +import { cn } from "@/lib/utils"; + import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; -import { classNames } from "@/Utils/utils"; - const META_URL = "/build-meta.json"; const APP_VERSION_KEY = "app-version"; const APP_UPDATED_KEY = "app-updated"; @@ -120,10 +120,7 @@ const UpdateAppPopup = ({ onUpdate }: UpdateAppPopupProps) => { Software Update diff --git a/src/components/Common/UserAutocompleteFormField.tsx b/src/components/Common/UserAutocompleteFormField.tsx deleted file mode 100644 index 90addafb702..00000000000 --- a/src/components/Common/UserAutocompleteFormField.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { useEffect, useState } from "react"; - -import { Autocomplete } from "@/components/Form/FormFields/Autocomplete"; -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import { UserType } from "@/components/Users/UserFormValidations"; -import { UserBareMinimum } from "@/components/Users/models"; - -import routes from "@/Utils/request/api"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; -import { - classNames, - formatName, - isUserOnline, - mergeQueryOptions, -} from "@/Utils/utils"; - -import { Avatar } from "./Avatar"; - -type BaseProps = FormFieldBaseProps & { - placeholder?: string; - userType?: UserType; - noResultsError?: string; -}; - -type UserSearchProps = BaseProps & { - facilityId?: undefined; - homeFacility?: string; -}; - -export default function UserAutocomplete(props: UserSearchProps) { - const field = useFormFieldPropsResolver(props); - const [query, setQuery] = useState(""); - const [disabled, setDisabled] = useState(false); - - const { data, loading } = useTanStackQueryInstead(routes.userList, { - query: { - home_facility: props.homeFacility, - user_type: props.userType, - search_text: query, - limit: 50, - offset: 0, - }, - }); - - useEffect(() => { - if ( - loading || - query || - !field.required || - !props.noResultsError || - !data?.results - ) { - return; - } - - if (data.results.length === 0) { - setDisabled(true); - field.handleChange(undefined as unknown as UserBareMinimum); - } - }, [loading, field.required, data?.results, props.noResultsError]); - - const getAvatar = (option: UserBareMinimum) => { - return ( - - ); - }; - - return ( - - obj.username, - )} - optionLabel={formatName} - optionIcon={userOnlineDot} - optionImage={getAvatar} - optionDescription={(option) => - `${option.user_type} - ${option.username}` - } - optionValue={(option) => option} - onQuery={setQuery} - isLoading={loading} - /> - - ); -} - -const userOnlineDot = (user: UserBareMinimum) => ( - -); diff --git a/src/components/Facility/DuplicatePatientDialog.tsx b/src/components/Facility/DuplicatePatientDialog.tsx index 2e795092042..beac625f852 100644 --- a/src/components/Facility/DuplicatePatientDialog.tsx +++ b/src/components/Facility/DuplicatePatientDialog.tsx @@ -4,6 +4,13 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { Table, TableBody, @@ -13,120 +20,125 @@ import { TableRow, } from "@/components/ui/table"; -import DialogModal from "@/components/Common/Dialog"; - import { PartialPatientModel } from "@/types/emr/newPatient"; interface Props { + open: boolean; + onOpenChange: (open: boolean) => void; patientList: Array; handleOk: (action: string) => void; - handleCancel: () => void; } const DuplicatePatientDialog = (props: Props) => { const { t } = useTranslation(); - const { patientList, handleOk, handleCancel } = props; + const { open, onOpenChange, patientList, handleOk } = props; const [action, setAction] = useState(""); return ( - - - - - {t("patient_records_found_description")}( - {patientList[0].phone_number}) - - - - - - - - {[`${t("patient_name")} / ID`, t("gender")].map( - (heading, i) => ( - {heading} - ), - )} - - - - {patientList.map((patient, i) => { - return ( - - - - {patient.name} - - - ID : {patient.id} - - - {patient.gender} - - ); - })} - - + + + + {t("patient_records_found")} + + + + + {t("patient_records_found_description")}( + {patientList[0].phone_number}) + - - - - - setAction(e.target.value)} - /> - {t("duplicate_patient_record_confirmation")} - + + + + + + {[`${t("patient_name")} / ID`, t("gender")].map( + (heading, i) => ( + {heading} + ), + )} + + + + {patientList.map((patient, i) => { + return ( + + + + {patient.name} + + + ID : {patient.id} + + + {patient.gender} + + ); + })} + + + + + + + setAction(e.target.value)} + /> + {t("duplicate_patient_record_confirmation")} + + - - - setAction(e.target.value)} - /> - {t("duplicate_patient_record_rejection")} - - + + + setAction(e.target.value)} + /> + {t("duplicate_patient_record_rejection")} + + - {t("duplicate_patient_record_birth_unknown")} + {t("duplicate_patient_record_birth_unknown")} + - - - - - {t("close")} - - handleOk(action)} - disabled={!action} - variant={"primary"} - > - - {t("continue")} - - - + + + onOpenChange(false)} + className="gap-1" + variant={"secondary"} + > + + {t("close")} + + handleOk(action)} + disabled={!action} + variant={"primary"} + > + + {t("continue")} + + + + + ); }; diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx index 547d1b209d6..195e4d700bf 100644 --- a/src/components/Facility/FacilityHome.tsx +++ b/src/components/Facility/FacilityHome.tsx @@ -7,14 +7,26 @@ import { } from "@radix-ui/react-tooltip"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query"; -import { Hospital, MapPin, MoreVertical, Settings } from "lucide-react"; +import { Edit2, Hospital, MapPin, MoreVertical, Settings } from "lucide-react"; import { navigate } from "raviger"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; +import { cn } from "@/lib/utils"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import { Button, buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { DropdownMenu, @@ -26,7 +38,6 @@ import { Markdown } from "@/components/ui/markdown"; import { Avatar } from "@/components/Common/Avatar"; import AvatarEditModal from "@/components/Common/AvatarEditModal"; -import ConfirmDialog from "@/components/Common/ConfirmDialog"; import ContactLink from "@/components/Common/ContactLink"; import Loading from "@/components/Common/Loading"; @@ -96,7 +107,6 @@ const renderGeoOrganizations = (geoOrg: Organization) => { export const FacilityHome = ({ facilityId }: Props) => { const { t } = useTranslation(); - const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const [editCoverImage, setEditCoverImage] = useState(false); const queryClient = useQueryClient(); @@ -106,13 +116,14 @@ export const FacilityHome = ({ facilityId }: Props) => { pathParams: { id: facilityId }, }), }); - const { mutate: deleteFacility } = useMutation({ + + const { mutate: deleteFacility, isPending: isDeleting } = useMutation({ mutationFn: mutate(routes.deleteFacility, { pathParams: { id: facilityId }, }), onSuccess: () => { toast.success( - t("entity_deleted_successfully", { name: facilityData?.name }), + t("facility_deleted_successfully", { name: facilityData?.name }), ); navigate("/facility"); }, @@ -184,26 +195,13 @@ export const FacilityHome = ({ facilityId }: Props) => { return ( - - {t("are_you_sure_want_to_delete", { name: facilityData?.name })} - - } - action="Delete" - variant="destructive" - show={openDeleteDialog} - onClose={() => setOpenDeleteDialog(false)} - onConfirm={() => deleteFacility()} - /> setEditCoverImage(false)} + onOpenChange={(open) => setEditCoverImage(open)} hint={coverImageHint} /> @@ -267,7 +265,7 @@ export const FacilityHome = ({ facilityId }: Props) => { className="cursor-pointer" onClick={() => setEditCoverImage(true)} > - + {t("edit_cover_photo")} )} @@ -276,7 +274,7 @@ export const FacilityHome = ({ facilityId }: Props) => { facilityId={facilityId} trigger={ { e.preventDefault(); }} @@ -286,16 +284,45 @@ export const FacilityHome = ({ facilityId }: Props) => { } /> - {/* TODO: get permissions from backend */} - {/* {hasPermissionToDeleteFacility && ( - setOpenDeleteDialog(true)} - > - - {t("delete_facility")} - - )} */} + + {/* TODO: add delete facility */} + {/* + e.preventDefault()} + > + + {t("delete_facility")} + + */} + + + + {t("delete_facility")} + + + {t("delete_facility_confirmation", { + name: facilityData?.name, + })} + + + + + {t("cancel")} + + deleteFacility()} + className={cn( + buttonVariants({ variant: "destructive" }), + )} + disabled={isDeleting} + > + {isDeleting ? t("deleting") : t("delete")} + + + + + ; export interface CommentModel { diff --git a/src/components/Files/CameraCaptureDialog.tsx b/src/components/Files/CameraCaptureDialog.tsx index 54b9debc21e..8ee66a313d6 100644 --- a/src/components/Files/CameraCaptureDialog.tsx +++ b/src/components/Files/CameraCaptureDialog.tsx @@ -6,21 +6,25 @@ import { toast } from "sonner"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; - -import DialogModal from "@/components/Common/Dialog"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import useBreakpoints from "@/hooks/useBreakpoints"; export interface CameraCaptureDialogProps { - show: boolean; - onHide: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; onCapture: (file: File, fileName: string) => void; onResetCapture: () => void; setPreview?: (isPreview: boolean) => void; } export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { - const { show, onHide, onCapture, onResetCapture, setPreview } = props; + const { open, onOpenChange, onCapture, onResetCapture, setPreview } = props; const isLaptopScreen = useBreakpoints({ lg: true, default: false }); const [cameraFacingMode, setCameraFacingMode] = useState( @@ -34,8 +38,9 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { height: { ideal: 2160 }, facingMode: cameraFacingMode, }; + useEffect(() => { - if (!show) return; + if (!open) return; let stream: MediaStream | null = null; navigator.mediaDevices @@ -45,7 +50,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }) .catch(() => { toast.warning(t("camera_permission_denied")); - onHide(); + onOpenChange(false); }); return () => { @@ -55,7 +60,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }); } }; - }, [show, cameraFacingMode, onHide]); + }, [open, cameraFacingMode, onOpenChange]); const handleSwitchCamera = useCallback(async () => { const devices = await navigator.mediaDevices.enumerateDevices(); @@ -87,130 +92,62 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }; return ( - - - - - - {t("camera")} - - - } - className="max-w-2xl" - onClose={onHide} - > - - {!previewImage ? ( - - - - ) : ( - - - - )} - + + + + + + + + + + + {t("camera")} + + + + - {/* buttons for mobile screens */} - - - {!previewImage ? ( - - {t("switch")} - - ) : ( - <>> - )} - {!previewImage ? ( - <> - - { - captureImage(); - setPreview?.(true); - }} - className="m-2" - > - {t("capture")} - - - > + + + ) : ( - <> - - { - setPreviewImage(null); - onResetCapture(); - setPreview?.(false); - }} - className="m-2" - > - {t("retake")} - - { - setPreviewImage(null); - onHide(); - setPreview?.(false); - }} - className="m-2" - > - {t("submit")} - - - > + + + )} - - { - setPreviewImage(null); - onResetCapture(); - onHide(); - }} - className="m-2" - > - {t("close")} - - - - {/* buttons for laptop screens */} - - - - - {`${t("switch")} ${t("camera")}`} - - - + {/* buttons for mobile and tablet screens */} + + + {!previewImage ? ( + + {t("switch")} + + ) : ( + <>> + )} + {!previewImage ? ( <> @@ -221,8 +158,8 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { captureImage(); setPreview?.(true); }} + className="m-2" > - {t("capture")} @@ -237,16 +174,18 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { onResetCapture(); setPreview?.(false); }} + className="m-2" > {t("retake")} { - onHide(); setPreviewImage(null); + onOpenChange(false); setPreview?.(false); }} + className="m-2" > {t("submit")} @@ -254,20 +193,82 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { > )} - - { - setPreviewImage(null); - onResetCapture(); - onHide(); - setPreview?.(false); - }} - > - {`${t("close")} ${t("camera")}`} - + + { + setPreviewImage(null); + onResetCapture(); + onOpenChange(false); + }} + className="m-2" + > + {t("close")} + + + + + {/* buttons for laptop screens */} + + + + {!previewImage ? ( + <> + + { + captureImage(); + setPreview?.(true); + }} + > + + {t("capture")} + + + > + ) : ( + <> + + { + setPreviewImage(null); + onResetCapture(); + setPreview?.(false); + }} + > + {t("retake")} + + { + onOpenChange(false); + setPreviewImage(null); + setPreview?.(false); + }} + > + {t("submit")} + + + > + )} + + + { + setPreviewImage(null); + onResetCapture(); + onOpenChange(false); + setPreview?.(false); + }} + > + {`${t("close")} ${t("camera")}`} + + - - + + ); } diff --git a/src/components/Files/FileBlock.tsx b/src/components/Files/FileBlock.tsx deleted file mode 100644 index 1b9cb9f8959..00000000000 --- a/src/components/Files/FileBlock.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import dayjs from "dayjs"; -import { t } from "i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; - -import { FileUploadModel } from "@/components/Patient/models"; - -import { FileManagerResult } from "@/hooks/useFileManager"; - -import { FILE_EXTENSIONS } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export interface FileBlockProps { - file: FileUploadModel; - fileManager: FileManagerResult; - associating_id: string; - editable: boolean; - archivable?: boolean; -} - -export default function FileBlock(props: FileBlockProps) { - const { - file, - fileManager, - associating_id, - editable = false, - archivable = false, - } = props; - - const filetype = fileManager.getFileType(file); - - const { data: fileData } = useQuery({ - queryKey: ["file", { id: file.id, type: fileManager.type, associating_id }], - queryFn: query(routes.retrieveUpload, { - queryParams: { file_type: fileManager.type, associating_id }, - pathParams: { id: file.id || "" }, - }), - enabled: filetype === "AUDIO" && !file.is_archived, - }); - - const icons: Record = { - AUDIO: "l-volume", - IMAGE: "l-image", - PRESENTATION: "l-presentation-play", - VIDEO: "l-video", - UNKNOWN: "l-file-medical", - DOCUMENT: "l-file-medical", - }; - - const archived = file.is_archived; - - return ( - - - - - - - - {file.name} - {file.extension} {file.is_archived && "(Archived)"} - - - {dayjs( - file.is_archived ? file.archived_datetime : file.created_date, - ).format("DD MMM YYYY, hh:mm A")}{" "} - by{" "} - {file.is_archived - ? file.archived_by?.username - : file.uploaded_by?.username} - - - - - {filetype === "AUDIO" && !file.is_archived && ( - - - - )} - {!file.is_archived && - (fileManager.isPreviewable(file) ? ( - fileManager.viewFile(file, associating_id)} - className="w-full md:w-auto" - > - - {t("view")} - - ) : ( - fileManager.downloadFile(file, associating_id)} - className="w-full md:w-auto" - > - - {t("download")} - - ))} - - {!file.is_archived && editable && ( - fileManager.editFile(file, associating_id)} - className="flex-1 md:flex-auto" - > - - {t("rename")} - - )} - {(file.is_archived || editable) && archivable && ( - fileManager.archiveFile(file, associating_id)} - className="flex-1 md:flex-auto" - > - - {file.is_archived ? t("more_info") : t("archive")} - - )} - - - - ); -} diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx deleted file mode 100644 index 54881c80c0a..00000000000 --- a/src/components/Files/FileUpload.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { Loader2 } from "lucide-react"; -import { ReactNode, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; - -import Pagination from "@/components/Common/Pagination"; -import Tabs from "@/components/Common/Tabs"; -import FileBlock from "@/components/Files/FileBlock"; -import { FileUploadModel } from "@/components/Patient/models"; - -import useAuthUser from "@/hooks/useAuthUser"; -import useFileManager from "@/hooks/useFileManager"; -import useFileUpload from "@/hooks/useFileUpload"; - -import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export const LinearProgressWithLabel = (props: { value: number }) => { - return ( - - - - - - - - {`${Math.round(props.value)}%`} - - - ); -}; - -interface FileUploadProps { - type: string; - patientId?: string; - encounterId?: string; - consentId?: string; - allowAudio?: boolean; - sampleId?: string; - claimId?: string; - className?: string; - hideUpload?: boolean; -} - -export interface ModalDetails { - name?: string; - id?: string; - reason?: string; - userArchived?: string; - archiveTime?: string; - associatedId?: string; -} - -export interface StateInterface { - open: boolean; - isImage: boolean; - name: string; - extension: string; - zoom: number; - isZoomInDisabled: boolean; - isZoomOutDisabled: boolean; - rotation: number; - id?: string; - associating_id?: string; -} - -export const FileUpload = (props: FileUploadProps) => { - const { t } = useTranslation(); - const { - encounterId, - patientId, - consentId, - type, - sampleId, - claimId, - allowAudio, - hideUpload, - } = props; - const [currentPage, setCurrentPage] = useState(1); - const [offset, setOffset] = useState(0); - const [tab, setTab] = useState("UNARCHIVED"); - const authUser = useAuthUser(); - const queryClient = useQueryClient(); - - const handlePagination = (page: number, limit: number) => { - const offset = (page - 1) * limit; - setCurrentPage(page); - setOffset(offset); - }; - - const UPLOAD_HEADING: { [index: string]: string } = { - PATIENT: t("upload_headings__patient"), - CONSULTATION: t("upload_headings__consultation"), - SAMPLE_MANAGEMENT: t("upload_headings__sample_report"), - CLAIM: t("upload_headings__supporting_info"), - }; - const VIEW_HEADING: { [index: string]: string } = { - PATIENT: t("file_list_headings__patient"), - CONSULTATION: t("file_list_headings__consultation"), - SAMPLE_MANAGEMENT: t("file_list_headings__sample_report"), - CLAIM: t("file_list_headings__supporting_info"), - }; - - const associatedId = - { - PATIENT: patientId, - CONSENT_RECORD: consentId, - ENCOUNTER: encounterId, - SAMPLE_MANAGEMENT: sampleId, - CLAIM: claimId, - }[type] || ""; - - const refetchAll = () => { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "active", type, associatedId], - }); - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "archived", type, associatedId], - }); - if (type === "consultation") { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "discharge_summary", associatedId], - }); - } - }; - - const { data: activeFiles, isLoading: activeFilesLoading } = useQuery({ - queryKey: ["viewUpload", "active", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: archivedFiles, isLoading: archivedFilesLoading } = useQuery({ - queryKey: ["viewUpload", "archived", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: true, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: dischargeSummary, isLoading: dischargeSummaryLoading } = - useQuery({ - queryKey: ["viewUpload", "discharge_summary", associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: "discharge_summary", - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - silent: true, - }, - }), - enabled: type === "consultation", - }); - - const queries = { - UNARCHIVED: { data: activeFiles, isLoading: activeFilesLoading }, - ARCHIVED: { data: archivedFiles, isLoading: archivedFilesLoading }, - DISCHARGE_SUMMARY: { - data: dischargeSummary, - isLoading: dischargeSummaryLoading, - }, - }; - - const loading = Object.values(queries).some((q) => q.isLoading); - const fileQuery = queries[tab as keyof typeof queries]; - - const tabs = [ - { text: "Active Files", value: "UNARCHIVED" }, - { text: "Archived Files", value: "ARCHIVED" }, - ...(dischargeSummary?.results?.length - ? [ - { - text: "Discharge Summary", - value: "DISCHARGE_SUMMARY", - }, - ] - : []), - ]; - - const fileUpload = useFileUpload({ - type, - allowedExtensions: [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "mp4", - "mov", - "avi", - "wmv", - "mp3", - "wav", - "ogg", - "txt", - "csv", - "rtf", - "doc", - "odt", - "pdf", - "xls", - "xlsx", - "ods", - "pdf", - ], - allowNameFallback: false, - onUpload: refetchAll, - }); - - const fileManager = useFileManager({ - type, - onArchive: refetchAll, - onEdit: refetchAll, - uploadedFiles: - fileQuery?.data?.results - .slice() - .reverse() - .map((file) => ({ - ...file, - associating_id: associatedId, - })) || [], - }); - const dischargeSummaryFileManager = useFileManager({ - type: "DISCHARGE_SUMMARY", - onArchive: refetchAll, - onEdit: refetchAll, - }); - - const uploadButtons: { - name: string; - icon: IconName; - onClick?: () => void; - children?: ReactNode; - show?: boolean; - id: string; - }[] = [ - { - name: t("choose_file"), - icon: "l-file-upload-alt", - children: , - id: "upload-file", - }, - { - name: t("open_camera"), - icon: "l-camera", - onClick: fileUpload.handleCameraCapture, - id: "open-webcam", - }, - { - name: t("record"), - icon: "l-microphone", - onClick: fileUpload.handleAudioCapture, - show: allowAudio, - id: "record-audio", - }, - ]; - return ( - - {fileUpload.Dialogues} - {fileManager.Dialogues} - {dischargeSummaryFileManager.Dialogues} - {!hideUpload && ( - <> - {UPLOAD_HEADING[type]} - {fileUpload.files[0] ? ( - - - - - {fileUpload.files[0].name} - - - - - - {t("enter_file_name")} - fileUpload.setFileName(e.target.value)} - /> - {fileUpload.error && ( - {fileUpload.error} - )} - - fileUpload.handleFileUpload(associatedId)} - disabled={fileUpload.uploading} // Disable the button when loading - className={`w-full ${fileUpload.uploading ? "opacity-50" : ""}`} - id="upload_file_button" - > - {fileUpload.uploading ? ( - - ) : ( - - )} - {t("upload")} - - - - {t("discard")} - - - {!!fileUpload.progress && ( - - )} - - ) : ( - - {uploadButtons - .filter((b) => b.show !== false) - .map((button, i) => ( - - - {button.name} - {button.children} - - ))} - - )} - > - )} - - {VIEW_HEADING[type]} - setTab(v.toString())} - currentTab={tab} - /> - - - {!(fileQuery?.data?.results || []).length && loading && ( - - )} - {fileQuery?.data?.results.map((item: FileUploadModel) => ( - - ))} - {!(fileQuery?.data?.results || []).length && ( - - - {t("no_files_found", { type: tab.toLowerCase() })} - - - )} - - {(fileQuery?.data?.count ?? 0) > RESULTS_PER_PAGE_LIMIT && ( - - - - )} - - ); -}; diff --git a/src/components/Form/FieldValidators.tsx b/src/components/Form/FieldValidators.tsx deleted file mode 100644 index 989a7aa6688..00000000000 --- a/src/components/Form/FieldValidators.tsx +++ /dev/null @@ -1 +0,0 @@ -export type FieldError = string | undefined; diff --git a/src/components/Form/FormFields/Autocomplete.tsx b/src/components/Form/FormFields/Autocomplete.tsx deleted file mode 100644 index 7e00c89fc7c..00000000000 --- a/src/components/Form/FormFields/Autocomplete.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { - Combobox, - ComboboxButton, - ComboboxInput, - ComboboxOption, - ComboboxOptions, -} from "@headlessui/react"; -import { ReactNode, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { DropdownTransition } from "@/components/Common/HelperComponents"; -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type AutocompleteProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: string; - optionLabel: OptionCallback; - optionIcon?: OptionCallback; - optionImage?: OptionCallback; - optionValue?: OptionCallback; - optionDescription?: OptionCallback; - optionDisabled?: OptionCallback; - className?: string; - minQueryLength?: number; - onQuery?: (query: string) => void; - requiredError?: boolean; - isLoading?: boolean; - allowRawInput?: boolean; - error?: string; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `AutocompleteFormField` instead as - * its API is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -export const Autocomplete = (props: AutocompleteProps) => { - const { t } = useTranslation(); - const [query, setQuery] = useState(""); // Ensure lower case - - useEffect(() => { - props.onQuery?.(query); - }, [query]); - - const mappedOptions = props.options.map((option) => { - const label = props.optionLabel(option); - const description = props.optionDescription?.(option); - return { - label, - description, - search: label.toLowerCase(), - icon: props.optionIcon?.(option), - image: props.optionImage?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const getOptions = () => { - if (!query) return mappedOptions; - - const knownOption = mappedOptions.find( - (o) => o.value == props.value || o.label == props.value, - ); - - if (knownOption) return mappedOptions; - return [ - { - label: query, - description: undefined, - search: query.toLowerCase(), - icon: , - image: undefined, - value: query, - disabled: undefined, - }, - ...mappedOptions, - ]; - }; - - const options = props.allowRawInput ? getOptions() : mappedOptions; - - const value = options.find((o) => props.value == o.value); - - const filteredOptions = - props.onQuery === undefined - ? options.filter((o) => o.search.includes(query)) - : options; - - return ( - - props.onChange(selection?.value)} - > - - - value?.label || ""} - onChange={(event) => setQuery(event.target.value.toLowerCase())} - onBlur={() => value && setQuery("")} - autoComplete="off" - /> - {!props.disabled && ( - - - {value?.icon} - - {value && !props.isLoading && !props.required && ( - - { - e.preventDefault(); - props.onChange(undefined); - }} - /> - - {t("clear_selection")} - - - )} - - {props.isLoading ? ( - - ) : ( - - )} - - - )} - - - - - {props.minQueryLength && query.length < props.minQueryLength ? ( - - {`Please enter at least ${props.minQueryLength} characters to search`} - - ) : filteredOptions.length === 0 ? ( - - No options found - - ) : ( - filteredOptions.map((option, index) => ( - - {({ focus }) => ( - - {option?.image} - - - {option.label} - {option.icon} - - {option.description && ( - - {option.description} - - )} - - - )} - - )) - )} - - - - - - ); -}; diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx deleted file mode 100644 index f3ad0559e44..00000000000 --- a/src/components/Form/FormFields/FormField.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { FieldError } from "@/components/Form/FieldValidators"; -import { FormFieldBaseProps } from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type LabelProps = { - id?: string | undefined; - required?: boolean; - htmlFor?: string; - children: React.ReactNode; - className?: string | undefined; - noPadding?: boolean; -}; - -export const FieldLabel = (props: LabelProps) => { - return ( - - {props.children} - {props.required && {" *"}} - - ); -}; - -type ErrorProps = { - error: FieldError; - className?: string | undefined; -}; - -export const FieldErrorText = (props: ErrorProps) => { - return ( - - {props.error} - - ); -}; - -/** - * @deprecated use shadcn/ui's solution for form fields instead along with react-hook-form - */ -const FormField = ({ - field, - ...props -}: { - field?: FormFieldBaseProps; - children: React.ReactNode; -}) => { - return ( - - - {field?.label && ( - - {field?.label} - - )} - {field?.labelSuffix && ( - {field?.labelSuffix} - )} - - {props.children} - - - ); -}; - -export default FormField; diff --git a/src/components/Form/FormFields/RadioFormField.tsx b/src/components/Form/FormFields/RadioFormField.tsx deleted file mode 100644 index ca205fcccad..00000000000 --- a/src/components/Form/FormFields/RadioFormField.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { ChangeEventHandler, ReactNode } from "react"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type Props = FormFieldBaseProps & { - options: readonly T[]; - optionLabel: (option: T) => React.ReactNode; - optionValue: (option: T) => V; - containerClassName?: string; - unselectLabel?: string; - layout?: "vertical" | "horizontal" | "grid" | "auto"; -}; - -/** - * @deprecated use shadcn/ui's radio-group instead - */ -const RadioFormField = (props: Props) => { - const field = useFormFieldPropsResolver(props); - return ( - - - {props.unselectLabel && ( - - field.handleChange(null)} - /> - {props.unselectLabel} - - )} - {props.options.map((option) => { - const value = props.optionValue(option); - return ( - field.handleChange(e.target.value as V)} - /> - ); - })} - - - ); -}; - -export default RadioFormField; - -export const RadioInput = (props: { - label?: ReactNode; - id?: string; - name?: string; - value?: string; - checked?: boolean; - onChange?: ChangeEventHandler; -}) => { - return ( - - props.onChange?.(e)} - /> - {props.label} - - ); -}; diff --git a/src/components/Form/FormFields/SelectFormField.tsx b/src/components/Form/FormFields/SelectFormField.tsx deleted file mode 100644 index 6b95550299f..00000000000 --- a/src/components/Form/FormFields/SelectFormField.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import SelectMenuV2 from "@/components/Form/SelectMenuV2"; - -type OptionCallback = (option: T) => R; - -type SelectFormFieldProps = FormFieldBaseProps & { - placeholder?: React.ReactNode; - options: readonly T[]; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - inputClassName?: string; -}; - -/** - * @deprecated use shadcn/ui's select instead - */ -export const SelectFormField = (props: SelectFormFieldProps) => { - const field = useFormFieldPropsResolver(props); - return ( - - field.handleChange(value)} - position={props.position} - placeholder={props.placeholder} - optionLabel={props.optionLabel} - inputClassName={props.inputClassName} - optionSelectedLabel={props.optionSelectedLabel} - optionDescription={props.optionDescription} - optionIcon={props.optionIcon} - optionValue={props.optionValue} - optionDisabled={props.optionDisabled} - requiredError={field.error ? props.required : false} - /> - - ); -}; diff --git a/src/components/Form/FormFields/TextFormField.tsx b/src/components/Form/FormFields/TextFormField.tsx deleted file mode 100644 index 18a4de18341..00000000000 --- a/src/components/Form/FormFields/TextFormField.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { - DetailedHTMLProps, - InputHTMLAttributes, - forwardRef, - useState, -} from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -export type TextFormFieldProps = FormFieldBaseProps & - Omit< - DetailedHTMLProps, HTMLInputElement>, - "onChange" - > & { - inputClassName?: string | undefined; - removeDefaultClasses?: true | undefined; - leading?: React.ReactNode | undefined; - trailing?: React.ReactNode | undefined; - leadingFocused?: React.ReactNode | undefined; - trailingFocused?: React.ReactNode | undefined; - trailingPadding?: string | undefined; - leadingPadding?: string | undefined; - suggestions?: string[]; - clearable?: boolean | undefined; - }; - -/** - * @deprecated use shadcn/ui's Input instead - */ -const TextFormField = forwardRef((props: TextFormFieldProps, ref) => { - const field = useFormFieldPropsResolver(props); - const { leading, trailing } = props; - const leadingFocused = props.leadingFocused || props.leading; - const trailingFocused = props.trailingFocused || props.trailing; - const hasLeading = !!(leading || leadingFocused); - const hasTrailing = !!(trailing || trailingFocused); - const hasIcon = hasLeading || hasTrailing; - const [showPassword, setShowPassword] = useState(false); - - const getPasswordFieldType = () => { - return showPassword ? "text" : "password"; - }; - - const minError = - typeof props.min !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.min}`) > parseFloat(`${field.value}`) - ? `Value can not be smaller than ${props.min}` - : undefined; - const maxError = - typeof props.max !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.max}`) < parseFloat(`${field.value}`) - ? `Value can not be greater than ${props.max}` - : undefined; - - const labelSuffixWithThreshold = ( - - {field.labelSuffix} - - ); - - let child = ( - - } - id={field.id} - className={classNames( - "cui-input-base peer", - hasLeading && (props.leadingPadding || "pl-10"), - hasTrailing && (props.trailingPadding || "pr-10"), - field.error && "border-danger-500", - props.inputClassName, - )} - disabled={field.disabled} - type={props.type === "password" ? getPasswordFieldType() : props.type} - name={field.name} - value={field.value} - required={field.required} - onChange={(e) => field.handleChange(e.target.value)} - /> - {props.clearable && field.value && ( - field.handleChange("")} - aria-label="Clear input" - > - - - )} - - ); - - if (props.type === "password") { - child = ( - - {child} - setShowPassword(!showPassword)} - > - - - - ); - } - - if (hasIcon) { - const _leading = - leading === leadingFocused ? ( - - {leading} - - ) : ( - <> - - {leading} - - - {leadingFocused} - - > - ); - const _trailing = - trailing === trailingFocused ? ( - - {trailing} - - ) : ( - <> - - {trailing} - - - {trailingFocused} - - > - ); - - child = ( - - {(leading || leadingFocused) && _leading} - {child} - {(trailing || trailingFocused) && _trailing} - - ); - } - - if ( - props.suggestions?.length && - !props.suggestions.includes(`${field.value}`) - ) { - child = ( - - {child} - - {props.suggestions.map((suggestion) => ( - field.handleChange(suggestion)} - > - {suggestion} - - ))} - - - ); - } - - return ( - - {child} - - ); -}); -TextFormField.displayName = "TextFormField"; - -export default TextFormField; diff --git a/src/components/Form/FormFields/Utils.ts b/src/components/Form/FormFields/Utils.ts deleted file mode 100644 index 1e88bcbd6a0..00000000000 --- a/src/components/Form/FormFields/Utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { FocusEvent } from "react"; - -import { FieldError } from "@/components/Form/FieldValidators"; - -export type FieldChangeEvent = { name: string; value: T }; -export type FieldChangeEventHandler = (event: FieldChangeEvent) => void; - -/** - * The base props for a form field. - * - * If a form context is provided, the field will be registered with the form - * and the onChange, value, and error props will be ignored. - * - * If a form context is not provided, the field will be treated as a standalone - * field. - * - * @template T The type of the field value. - * @template Form The type of the form details. - */ -export type FormFieldBaseProps = { - label?: React.ReactNode; - labelSuffix?: React.ReactNode; - disabled?: boolean; - className?: string; - required?: boolean; - labelClassName?: string; - errorClassName?: string; - name: string; - validate?: undefined; - id?: string; - onChange: FieldChangeEventHandler; - value?: T; - error?: FieldError; - onFocus?: (event: FocusEvent) => void; - onBlur?: (event: FocusEvent) => void; -}; - -/** - * Resolves the props for a form field. - * If a form context is provided, the field will be registered with the form. - * Otherwise, the field will be treated as a standalone field. - * - * @param props The props for the field. - * @returns The resolved props along with a handleChange function. - */ -export const useFormFieldPropsResolver = (props: FormFieldBaseProps) => { - const handleChange = (value: T) => - props.onChange({ name: props.name, value }); - - return { - ...props, - id: props.id ?? props.name, - name: props.name, - onChange: props.onChange, - value: props.value, - error: props.error, - handleChange, - }; -}; diff --git a/src/components/Form/MultiSelectMenuV2.tsx b/src/components/Form/MultiSelectMenuV2.tsx deleted file mode 100644 index b67da3656f4..00000000000 --- a/src/components/Form/MultiSelectMenuV2.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { classNames } from "@/Utils/utils"; - -interface MultiSelectOptionChipProps { - label: ReactNode; - onRemove?: () => void; -} - -export const MultiSelectOptionChip = ({ - label, - onRemove, -}: MultiSelectOptionChipProps) => { - return ( - - {label} - {onRemove && ( - { - e.stopPropagation(); - onRemove(); - }} - > - - - )} - - ); -}; - -interface OptionRenderPropArg { - focus: boolean; - selected: boolean; - disabled: boolean; -} - -export const dropdownOptionClassNames = ({ - focus, - selected, - disabled, -}: OptionRenderPropArg) => { - return classNames( - "group/option relative w-full cursor-default select-none p-4 text-sm transition-colors duration-75 ease-in-out", - !disabled && focus && "bg-primary-500 text-white", - !disabled && !focus && selected && "text-primary-500", - !disabled && !focus && !selected && "text-secondary-900", - disabled && "cursor-not-allowed text-secondary-600", - selected ? "font-semibold" : "font-normal", - ); -}; diff --git a/src/components/Form/SelectMenuV2.tsx b/src/components/Form/SelectMenuV2.tsx deleted file mode 100644 index a13d03607c6..00000000000 --- a/src/components/Form/SelectMenuV2.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { - Label, - Listbox, - ListboxButton, - ListboxOption, - ListboxOptions, -} from "@headlessui/react"; -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type SelectMenuProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: ReactNode; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - showIconWhenSelected?: boolean; - showChevronIcon?: boolean; - className?: string; - inputClassName?: string; - requiredError?: boolean; - onFocus?: () => void; - onBlur?: () => void; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `SelectFormField` instead as its API - * is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -const SelectMenuV2 = (props: SelectMenuProps) => { - const valueOptions = props.options.map((option) => { - const label = props.optionLabel(option); - return { - label, - selectedLabel: props.optionSelectedLabel - ? props.optionSelectedLabel(option) - : label, - description: props.optionDescription?.(option), - icon: props.optionIcon?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const showChevronIcon = props.showChevronIcon ?? true; - - const placeholder = - valueOptions?.length > 0 ? (props.placeholder ?? "Select") : "No options"; - const defaultOption = { - label: placeholder, - selectedLabel: ( - {placeholder} - ), - description: undefined, - icon: undefined, - value: undefined, - disabled: undefined, - }; - - const options = props.required - ? valueOptions - : [defaultOption, ...valueOptions]; - - const value = options.find((o) => props.value == o.value) ?? defaultOption; - - return ( - - props.onChange(selection.value)} - > - <> - {props.placeholder} - - - - - {props.showIconWhenSelected && value?.icon && ( - - {value.icon} - - )} - - {value.selectedLabel} - - - {showChevronIcon && ( - - )} - - - - - {options.map((option, index) => ( - - {({ focus, selected }) => ( - - - {option.label} - {props.optionIcon - ? option.icon - : selected && ( - - )} - - {option.description && ( - - {option.description} - - )} - - )} - - ))} - - - - > - - - ); -}; - -export default SelectMenuV2; diff --git a/src/components/Location/LocationSearch.tsx b/src/components/Location/LocationSearch.tsx index bd21bb26273..a27d8e8d1ec 100644 --- a/src/components/Location/LocationSearch.tsx +++ b/src/components/Location/LocationSearch.tsx @@ -20,7 +20,7 @@ import locationApi from "@/types/location/locationApi"; interface LocationSearchProps { facilityId: string; - mode?: "kind" | "location"; + mode?: "kind" | "instance"; onSelect: (location: LocationList) => void; disabled?: boolean; value?: LocationList | null; @@ -44,7 +44,6 @@ export function LocationSearch({ }), enabled: facilityId !== "preview", }); - return ( @@ -57,7 +56,7 @@ export function LocationSearch({ - + = ({ const { data: administrations, refetch: refetchAdministrations } = useQuery({ queryKey: ["medication_administrations", patientId, visibleSlots], - queryFn: query(routes.medicationAdministration.list, { + queryFn: query(medicationAdministrationApi.list, { pathParams: { patientId }, queryParams: { encounter: encounterId, diff --git a/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx b/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx index 3668e4f84de..d8e711e5f2d 100644 --- a/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx +++ b/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx @@ -50,12 +50,9 @@ export const MedicineAdminDialog = ({ }, [initialRequest]); const { mutate: upsertAdministration, isPending } = useMutation({ - mutationFn: mutate( - medicationAdministrationApi.upsertMedicationAdministration, - { - pathParams: { patientId: patientId }, - }, - ), + mutationFn: mutate(medicationAdministrationApi.upsert, { + pathParams: { patientId: patientId }, + }), onSuccess: () => { onOpenChange(false); toast.success(t("medication_administration_saved")); diff --git a/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx b/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx index a2f7ee23111..8b22e738b59 100644 --- a/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx +++ b/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx @@ -122,12 +122,9 @@ export function MedicineAdminSheet({ const formRef = useRef(null); const { mutate: upsertAdministrations, isPending } = useMutation({ - mutationFn: mutate( - medicationAdministrationApi.upsertMedicationAdministration, - { - pathParams: { patientId }, - }, - ), + mutationFn: mutate(medicationAdministrationApi.upsert, { + pathParams: { patientId }, + }), onSuccess: () => { toast.success(t("medication_administration_saved")); handleClose(); diff --git a/src/components/Patient/FileUploadPage.tsx b/src/components/Patient/FileUploadPage.tsx deleted file mode 100644 index 83bce32954c..00000000000 --- a/src/components/Patient/FileUploadPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useTranslation } from "react-i18next"; - -import Page from "@/components/Common/Page"; -import { FileUpload } from "@/components/Files/FileUpload"; - -export default function FileUploadPage(props: { - facilityId: string; - patientId: string; - encounterId?: string; - type: "encounter" | "patient"; -}) { - const { patientId, encounterId, type } = props; - const { t } = useTranslation(); - - return ( - - - - ); -} diff --git a/src/components/Patient/MedicationStatementList.tsx b/src/components/Patient/MedicationStatementList.tsx index c9ea302c21a..112c42414a4 100644 --- a/src/components/Patient/MedicationStatementList.tsx +++ b/src/components/Patient/MedicationStatementList.tsx @@ -24,13 +24,13 @@ import { import { Avatar } from "@/components/Common/Avatar"; -import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; import { formatDateTime, formatName } from "@/Utils/utils"; import { MEDICATION_STATEMENT_STATUS_STYLES, MedicationStatementRead, } from "@/types/emr/medicationStatement"; +import medicationStatementApi from "@/types/emr/medicationStatement/medicationStatementApi"; interface MedicationStatementListProps { patientId: string; @@ -129,7 +129,7 @@ export function MedicationStatementList({ const { data: medications, isLoading } = useQuery({ queryKey: ["medication_statements", patientId], - queryFn: query(routes.medicationStatement.list, { + queryFn: query(medicationStatementApi.list, { pathParams: { patientId }, }), }); diff --git a/src/components/Patient/PatientHome.tsx b/src/components/Patient/PatientHome.tsx index 0807023978a..69df69c4143 100644 --- a/src/components/Patient/PatientHome.tsx +++ b/src/components/Patient/PatientHome.tsx @@ -247,30 +247,6 @@ export const PatientHome = (props: { - - {/* setOpenAssignVolunteerDialog(false)} - description={ - - setAssignedVolunteer(user.value)} - userType={"Volunteer"} - name={"assign_volunteer"} - error={errors.assignedVolunteer} - /> - - } - action={ - assignedVolunteer || !patientData.assigned_to - ? t("assign") - : t("unassign") - } - onConfirm={handleAssignedVolunteer} - /> */} ); }; diff --git a/src/components/Patient/PatientInfoCard.tsx b/src/components/Patient/PatientInfoCard.tsx index d7bfb9d01d5..955de6a9ef6 100644 --- a/src/components/Patient/PatientInfoCard.tsx +++ b/src/components/Patient/PatientInfoCard.tsx @@ -481,6 +481,11 @@ export default function PatientInfoCard(props: PatientInfoCardProps) { + + {t("cancel")} diff --git a/src/components/Patient/PatientRegistration.tsx b/src/components/Patient/PatientRegistration.tsx index 8557b4fab89..1c1113081ef 100644 --- a/src/components/Patient/PatientRegistration.tsx +++ b/src/components/Patient/PatientRegistration.tsx @@ -42,11 +42,7 @@ import DuplicatePatientDialog from "@/components/Facility/DuplicatePatientDialog import useAppHistory from "@/hooks/useAppHistory"; -import { - BLOOD_GROUP_CHOICES, // DOMESTIC_HEALTHCARE_SUPPORT_CHOICES, - GENDER_TYPES, // OCCUPATION_TYPES, - //RATION_CARD_CATEGORY, // SOCIOECONOMIC_STATUS_CHOICES , -} from "@/common/constants"; +import { BLOOD_GROUP_CHOICES, GENDER_TYPES } from "@/common/constants"; import { GENDERS } from "@/common/constants"; import countryList from "@/common/static/countries.json"; @@ -747,10 +743,13 @@ export default function PatientRegistration( {showDuplicate && ( { - handleDialogClose("close"); + onOpenChange={(open) => { + if (!open) { + handleDialogClose("close"); + } }} /> )} diff --git a/src/components/Patient/models.tsx b/src/components/Patient/models.tsx index 14f048191b0..abc717e6120 100644 --- a/src/components/Patient/models.tsx +++ b/src/components/Patient/models.tsx @@ -1,46 +1,11 @@ -import { PatientCategory } from "@/components/Facility/models"; import { UserBareMinimum } from "@/components/Users/models"; import { - APPETITE_CHOICES, - BLADDER_DRAINAGE_CHOICES, - BLADDER_ISSUE_CHOICES, - BOWEL_ISSUE_CHOICES, - CONSCIOUSNESS_LEVEL, DOMESTIC_HEALTHCARE_SUPPORT_CHOICES, - HEARTBEAT_RHYTHM_CHOICES, - HumanBodyRegion, - INSULIN_INTAKE_FREQUENCY_OPTIONS, - LIMB_RESPONSE_OPTIONS, - NURSING_CARE_PROCEDURES, - NUTRITION_ROUTE_CHOICES, OCCUPATION_TYPES, - ORAL_ISSUE_CHOICES, - OXYGEN_MODALITY_OPTIONS, - PressureSoreExudateAmountOptions, - PressureSoreTissueTypeOptions, - RESPIRATORY_SUPPORT, - SLEEP_CHOICES, SOCIOECONOMIC_STATUS_CHOICES, - URINATION_FREQUENCY_CHOICES, - VENTILATOR_MODE_OPTIONS, } from "@/common/constants"; -export interface FlowModel { - id?: number; - status?: string; - created_date?: string; - modified_date?: string; - deleted?: boolean; - notes?: string; - patient_sample?: number; - created_by?: number; -} - -export interface DischargeSummaryModel { - email?: string; -} - export interface AssignedToObjectModel { first_name: string; last_name: string; @@ -56,138 +21,6 @@ export interface PatientMeta { domestic_healthcare_support?: (typeof DOMESTIC_HEALTHCARE_SUPPORT_CHOICES)[number]; } -export const DailyRoundTypes = [ - "NORMAL", - "COMMUNITY_NURSES_LOG", - "DOCTORS_LOG", - "VENTILATOR", - "AUTOMATED", - "TELEMEDICINE", -] as const; - -export type BloodPressure = { - systolic?: number; - diastolic?: number; -}; - -export interface IPainScale { - description: string; - region: HumanBodyRegion; - scale: number; -} - -export type NameQuantity = { name: string; quantity: number }; - -export type IPressureSore = { - region: HumanBodyRegion; - width: number; - length: number; - description: string; - scale: number; - exudate_amount: (typeof PressureSoreExudateAmountOptions)[number]; - tissue_type: (typeof PressureSoreTissueTypeOptions)[number]; -}; -export interface DailyRoundsModel { - spo2?: number; - rhythm?: (typeof HEARTBEAT_RHYTHM_CHOICES)[number]; - rhythm_detail?: string; - bp?: BloodPressure; - pulse?: number; - resp?: number; - temperature?: number; - physical_examination_info?: string; - other_details?: string; - consultation?: number; - action?: string; - review_interval?: number; - id?: string; - admitted_to?: string; - patient_category?: PatientCategory; - recommend_discharge?: boolean; - created_date?: string; - modified_date?: string; - taken_at?: string; - consciousness_level?: (typeof CONSCIOUSNESS_LEVEL)[number]["value"]; - rounds_type?: (typeof DailyRoundTypes)[number]; - last_updated_by_telemedicine?: boolean; - created_by_telemedicine?: boolean; - created_by?: UserBareMinimum; - last_edited_by?: UserBareMinimum; - bed?: string; - pain_scale_enhanced?: IPainScale[]; - in_prone_position?: boolean; - left_pupil_size?: number; - left_pupil_size_detail?: string; - left_pupil_light_reaction?: string; - left_pupil_light_reaction_detail?: string; - right_pupil_size?: number; - right_pupil_size_detail?: string; - right_pupil_light_reaction?: string; - right_pupil_light_reaction_detail?: string; - glasgow_eye_open?: number; - glasgow_motor_response?: number; - glasgow_verbal_response?: number; - limb_response_upper_extremity_right?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_upper_extremity_left?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_lower_extremity_left?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_lower_extremity_right?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - glasgow_total_calculated?: number; - bilateral_air_entry?: boolean; - etco2?: number; - po2?: number; - pco2?: number; - ph?: number; - hco3?: number; - base_excess?: number; - lactate?: number; - sodium?: number; - potassium?: number; - blood_sugar_level?: number; - insulin_intake_dose?: number; - insulin_intake_frequency?: (typeof INSULIN_INTAKE_FREQUENCY_OPTIONS)[number]; - dialysis_fluid_balance?: number; - dialysis_net_balance?: number; - nursing?: { - procedure: (typeof NURSING_CARE_PROCEDURES)[number]; - description: string; - }[]; - feeds?: NameQuantity[]; - infusions?: NameQuantity[]; - iv_fluids?: NameQuantity[]; - output?: NameQuantity[]; - total_intake_calculated?: number; - total_output_calculated?: number; - ventilator_spo2?: number; - ventilator_interface?: (typeof RESPIRATORY_SUPPORT)[number]["value"]; - ventilator_oxygen_modality?: (typeof OXYGEN_MODALITY_OPTIONS)[number]["value"]; - ventilator_oxygen_modality_flow_rate?: number; - ventilator_oxygen_modality_oxygen_rate?: number; - ventilator_fio2?: number; - ventilator_mode?: (typeof VENTILATOR_MODE_OPTIONS)[number]; - ventilator_peep?: number; - ventilator_pip?: number; - ventilator_mean_airway_pressure?: number; - ventilator_resp_rate?: number; - ventilator_pressure_support?: number; - - ventilator_tidal_volume?: number; - pressure_sore?: IPressureSore[]; - bowel_issue?: (typeof BOWEL_ISSUE_CHOICES)[number]; - bladder_drainage?: (typeof BLADDER_DRAINAGE_CHOICES)[number]; - bladder_issue?: (typeof BLADDER_ISSUE_CHOICES)[number]; - is_experiencing_dysuria?: boolean; - urination_frequency?: (typeof URINATION_FREQUENCY_CHOICES)[number]; - sleep?: (typeof SLEEP_CHOICES)[number]; - nutrition_route?: (typeof NUTRITION_ROUTE_CHOICES)[number]; - oral_issue?: (typeof ORAL_ISSUE_CHOICES)[number]; - appetite?: (typeof APPETITE_CHOICES)[number]; -} - -export interface FacilityNameModel { - id?: string; - name?: string; -} - // File Upload Models export type FileCategory = "unspecified" | "xray" | "audio" | "identity_proof"; diff --git a/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx b/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx index ed60dbe4b7e..5d2e1391d6c 100644 --- a/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx +++ b/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx @@ -79,7 +79,7 @@ export function LocationQuestion({ Select Location ; - - const { data: patientData } = useQuery({ - queryKey: ["patient", related_patient], - queryFn: query(routes.patient.getPatient, { - pathParams: { id: String(related_patient) }, - }), - enabled: !!related_patient, - }); - - const form = useForm({ - resolver: zodResolver(resourceFormSchema), - defaultValues: { - category: "", - assigned_facility: null, - emergency: "false" as const, - title: "", - reason: "", - referring_facility_contact_name: "", - referring_facility_contact_number: "", - priority: 1, - }, - }); - - const { mutate: createResource, isPending } = useMutation({ - mutationFn: mutate(routes.createResource), - onSuccess: (data: ResourceRequest) => { - toast.success(t("resource_created_successfully")); - navigate(`/facility/${facilityId}/resource/${data.id}`); - }, - }); - - const onSubmit = (data: ResourceFormValues) => { - createResource({ - status: "PENDING", - category: data.category, - origin_facility: String(props.facilityId), - assigned_facility: data.assigned_facility?.id || null, - approving_facility: null, - emergency: data.emergency === "true", - title: data.title, - reason: data.reason, - referring_facility_contact_name: data.referring_facility_contact_name, - referring_facility_contact_number: data.referring_facility_contact_number, - related_patient: related_patient, - priority: data.priority, - }); - }; - - const { data: facilities } = useQuery({ - queryKey: ["facilities", facilitySearch], - queryFn: query.debounced(facilityApi.getAllFacilities, { - queryParams: { - search_text: facilitySearch, - limit: 50, - }, - }), - }); - - const facilityOptions = facilities?.results.map((facility) => ({ - label: facility.name, - value: facility.id, - })); - - const fillMyDetails = () => { - form.setValue( - "referring_facility_contact_name", - `${authUser.first_name} ${authUser.last_name}`.trim(), - ); - if (authUser.phone_number) { - form.setValue("referring_facility_contact_number", authUser.phone_number); - } - }; - - if (isPending) { - return ; - } - - return ( - - - - - - {patientData && ( - - - - - - {t("linked_patient")}:{" "} - {patientData.name} - - - - - )} - - - - - {t("basic_information")} - - - {t("resource_request_basic_info_description")} - - - - - ( - - - {t("facility_for_care_support")} - - - { - const facility = - facilities?.results.find( - (f) => f.id === value, - ) ?? null; - form.setValue("assigned_facility", facility); - }} - /> - - - {t("select_facility_description")} - - - - )} - /> - - ( - - {t("is_this_an_emergency")} - - - - - - - - {t("yes")} - - - - - - - - {t("no")} - - - - - - {t("emergency_description")} - - - - )} - /> - - - ( - - {t("category")} - - - - - - - - {RESOURCE_CATEGORY_CHOICES.map((category) => ( - - {category.text} - - ))} - - - - {t("category_description")} - - - - )} - /> - - - - - - - - {t("request_details")} - - - {t("resource_request_details_description")} - - - - ( - - {t("request_title")} - - field.onChange(value)} - /> - - - {t("request_title_description")} - - - - )} - /> - - ( - - {t("request_reason")} - - field.onChange(value)} - /> - - - {t("request_reason_description")} - - - - )} - /> - - - - - - - - - {t("contact_information")} - - - {t("contact_information_description")} - - - - - {t("fill_my_details")} - - - - - ( - - {t("contact_person")} - - field.onChange(value)} - /> - - - {t("contact_person_description")} - - - - )} - /> - - ( - - {t("contact_phone")} - - field.onChange(value)} - /> - - - {t("contact_phone_description")} - - - - )} - /> - - - - - goBack()} - > - {t("cancel")} - - - {isPending && ( - - )} - {isPending ? t("submitting") : t("submit")} - - - - - - - - ); -} diff --git a/src/components/Resource/ResourceDetailsUpdate.tsx b/src/components/Resource/ResourceDetailsUpdate.tsx deleted file mode 100644 index e589a83c166..00000000000 --- a/src/components/Resource/ResourceDetailsUpdate.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { useMutation } from "@tanstack/react-query"; -import { t } from "i18next"; -import { navigate, useQueryParams } from "raviger"; -import { useEffect, useReducer, useState } from "react"; -import { toast } from "sonner"; - -import Card from "@/CAREUI/display/Card"; - -import Autocomplete from "@/components/ui/autocomplete"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; - -import CircularProgress from "@/components/Common/CircularProgress"; -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; -import UserAutocomplete from "@/components/Common/UserAutocompleteFormField"; -import RadioFormField from "@/components/Form/FormFields/RadioFormField"; -import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; -import { UserModel } from "@/components/Users/models"; - -import useAppHistory from "@/hooks/useAppHistory"; - -import { RESOURCE_STATUS_CHOICES } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import mutate from "@/Utils/request/mutate"; -import query from "@/Utils/request/query"; -import facilityApi from "@/types/facility/facilityApi"; -import { UpdateResourceRequest } from "@/types/resourceRequest/resourceRequest"; - -interface resourceProps { - id: string; - facilityId: string; -} - -const initForm: Partial = { - assigned_facility: null, - emergency: false, - title: "", - reason: "", - assigned_to: null, -}; - -const requiredFields: any = { - assigned_facility_type: { - errorText: "Please Select Facility Type", - }, -}; - -const initError = Object.assign( - {}, - ...Object.keys(initForm).map((k) => ({ [k]: "" })), -); - -const initialState = { - form: { ...initForm }, - errors: { ...initError }, -}; - -export const ResourceDetailsUpdate = (props: resourceProps) => { - const { goBack } = useAppHistory(); - const [qParams, _] = useQueryParams(); - const [facilitySearch, setFacilitySearch] = useState(""); - const [assignedUser, SetAssignedUser] = useState(); - const resourceFormReducer = (state = initialState, action: any) => { - switch (action.type) { - case "set_form": { - return { - ...state, - form: action.form, - }; - } - case "set_error": { - return { - ...state, - errors: action.errors, - }; - } - default: - return state; - } - }; - - const [state, dispatch] = useReducer(resourceFormReducer, initialState); - const { data, isLoading: assignedUserLoading } = useQuery({ - queryKey: ["user", props.facilityId], - queryFn: query(routes.userList), - }); - - useEffect(() => { - if (data) { - SetAssignedUser(data.results[0]); - } - }, [data]); - - const validateForm = () => { - const errors = { ...initError }; - let isInvalidForm = false; - Object.keys(requiredFields).forEach((field) => { - if (!state.form[field] || !state.form[field].length) { - errors[field] = requiredFields[field].errorText; - isInvalidForm = true; - } - }); - - dispatch({ type: "set_error", errors }); - return isInvalidForm; - }; - - const handleChange = (e: FieldChangeEvent) => { - dispatch({ - type: "set_form", - form: { ...state.form, [e.name]: e.value }, - }); - }; - - const handleOnSelect = (user: any) => { - const form = { ...state.form }; - form["assigned_to"] = user?.value?.id; - SetAssignedUser(user.value); - dispatch({ type: "set_form", form }); - }; - - const setFacility = (selected: any, name: string) => { - const form = { ...state.form }; - form[name] = selected; - dispatch({ type: "set_form", form }); - }; - const { data: resourceDetails } = useQuery({ - queryKey: ["resource", props.facilityId, props.id], - queryFn: query(routes.getResourceDetails, { - pathParams: { id: props.id }, - }), - }); - useEffect(() => { - if (resourceDetails) { - dispatch({ - type: "set_form", - form: { - ...resourceDetails, - status: qParams.status || resourceDetails.status.toLowerCase(), - }, - }); - } - }, [resourceDetails]); - - const { mutate: updateResource, isPending: updateResourceLoading } = - useMutation({ - mutationFn: mutate(routes.updateResource, { - pathParams: { id: props.id }, - }), - onSuccess: (data) => { - dispatch({ type: "set_form", form: data }); - toast.success(t("request_updated_successfully")); - navigate(`/facility/${props.facilityId}/resource/${props.id}`); - }, - }); - - const { data: facilities } = useQuery({ - queryKey: ["facilities", facilitySearch], - queryFn: query.debounced(facilityApi.getAllFacilities, { - queryParams: { - search_text: facilitySearch, - limit: 50, - }, - }), - }); - - const facilityOptions = facilities?.results.map((facility) => ({ - label: facility.name, - value: facility.id, - })); - - const handleSubmit = async () => { - const validForm = validateForm(); - - if (validForm) { - const resourceData: UpdateResourceRequest = { - id: props.id, - status: state.form.status, - origin_facility: state.form.origin_facility?.id, - assigned_facility: state.form?.assigned_facility?.id, - emergency: [true, "true"].includes(state.form.emergency), - title: state.form.title, - reason: state.form.reason, - assigned_to: state.form.assigned_to, - category: state.form.category, - priority: state.form.priority, - referring_facility_contact_number: - state.form.referring_facility_contact_number, - referring_facility_contact_name: - state.form.referring_facility_contact_name, - approving_facility: state.form.approving_facility?.id, - related_patient: state.form.related_patient?.id, - }; - updateResource(resourceData); - } - }; - - if (updateResourceLoading || !resourceDetails) { - return ; - } - - return ( - - - - - - option.text} - onChange={handleChange} - optionLabel={(option) => t(`resource_status__${option.text}`)} - /> - - - - {assignedUserLoading ? ( - - ) : ( - - )} - - - - - - {t("facility_assign_request")} - - - setFacility(selected, "assigned_facility") - } - /> - - - - - - - - - {t("request_reason")} - - - handleChange({ name: e.target.name, value: e.target.value }) - } - /> - {state.errors.reason && ( - - {state.errors.emergency} - - )} - - - - (o ? "Yes" : "No")} - optionValue={(o) => String(o)} - value={String(state.form.emergency)} - error={state.errors.emergency} - /> - - - - goBack()}> - {t("cancel")} - - - {t("submit")} - - - - - - - ); -}; diff --git a/src/components/Resource/ResourceForm.tsx b/src/components/Resource/ResourceForm.tsx new file mode 100644 index 00000000000..fb93e88e316 --- /dev/null +++ b/src/components/Resource/ResourceForm.tsx @@ -0,0 +1,571 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { Link, navigate, useQueryParams } from "raviger"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import * as z from "zod"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { Alert, AlertDescription } from "@/components/ui/alert"; +import Autocomplete from "@/components/ui/autocomplete"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { PhoneInput } from "@/components/ui/phone-input"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Textarea } from "@/components/ui/textarea"; + +import Loading from "@/components/Common/Loading"; +import Page from "@/components/Common/Page"; +import UserSelector from "@/components/Common/UserSelector"; + +import useAppHistory from "@/hooks/useAppHistory"; +import useAuthUser from "@/hooks/useAuthUser"; + +import { RESOURCE_STATUS_CHOICES } from "@/common/constants"; +import { RESOURCE_CATEGORY_CHOICES } from "@/common/constants"; + +import routes from "@/Utils/request/api"; +import mutate from "@/Utils/request/mutate"; +import query from "@/Utils/request/query"; +import { mergeAutocompleteOptions } from "@/Utils/utils"; +import validators from "@/Utils/validators"; +import facilityApi from "@/types/facility/facilityApi"; +import { ResourceRequest } from "@/types/resourceRequest/resourceRequest"; +import { UserBase } from "@/types/user/user"; + +interface ResourceProps { + facilityId: number; + id?: string; +} + +export default function ResourceForm({ facilityId, id }: ResourceProps) { + const [facilitySearch, setFacilitySearch] = useState(""); + const { goBack } = useAppHistory(); + const { t } = useTranslation(); + const [{ related_patient }] = useQueryParams(); + const [assignedToUser, setAssignedToUser] = useState(); + const authUser = useAuthUser(); + + const resourceFormSchema = z.object({ + status: z.string().min(1, { message: t("field_required") }), + category: z.string().min(1, { message: t("field_required") }), + assigned_facility: z + .object({ + id: z.string(), + name: z.string(), + }) + .nullable(), + assigned_to: z.string().min(1, { message: t("field_required") }), + emergency: z.enum(["true", "false"]), + title: z.string().min(1, { message: t("field_required") }), + reason: z.string().min(1, { message: t("field_required") }), + referring_facility_contact_name: z + .string() + .min(1, { message: t("field_required") }), + referring_facility_contact_number: validators().phoneNumber.required, + priority: z.number().default(1), + }); + + type ResourceFormValues = z.infer; + + const { data: patientData } = useQuery({ + queryKey: ["patient", related_patient], + queryFn: query(routes.patient.getPatient, { + pathParams: { id: String(related_patient) }, + }), + enabled: !!related_patient, + }); + + const { data: resourceData } = useQuery({ + queryKey: ["resource_request", id], + queryFn: query(routes.getResourceDetails, { + pathParams: { id: String(id) }, + }), + enabled: !!id, + }); + + const form = useForm({ + resolver: zodResolver(resourceFormSchema), + defaultValues: { + status: "pending", + category: "", + assigned_facility: null, + assigned_to: "", + emergency: "false" as const, + title: "", + reason: "", + referring_facility_contact_name: "", + referring_facility_contact_number: "", + priority: 1, + }, + }); + + useEffect(() => { + if (resourceData) { + form.reset({ + status: resourceData.status, + category: resourceData.category, + assigned_facility: resourceData.assigned_facility, + assigned_to: resourceData.assigned_to?.id, + emergency: resourceData.emergency ? "true" : "false", + title: resourceData.title, + reason: resourceData.reason, + referring_facility_contact_name: + resourceData.referring_facility_contact_name, + referring_facility_contact_number: + resourceData.referring_facility_contact_number, + priority: resourceData.priority, + }); + if (resourceData.assigned_to) { + setAssignedToUser(resourceData.assigned_to); + } else { + setAssignedToUser(undefined); + } + } + }, [resourceData, form]); + + const { mutate: createResource, isPending } = useMutation({ + mutationFn: mutate(routes.createResource), + onSuccess: (data: ResourceRequest) => { + toast.success(t("resource_created_successfully")); + navigate(`/facility/${facilityId}/resource/${data.id}`); + }, + }); + + const { mutate: updateResource, isPending: isUpdatePending } = useMutation({ + mutationFn: mutate(routes.updateResource, { + pathParams: { id: String(id) }, + }), + onSuccess: (data: ResourceRequest) => { + toast.success(t("resource_updated_successfully")); + navigate(`/facility/${facilityId}/resource/${data.id}`); + }, + }); + + const onSubmit = (data: ResourceFormValues) => { + const resourcePayload = { + status: data.status, + category: data.category, + origin_facility: String(facilityId), + assigned_facility: data.assigned_facility?.id || null, + assigned_to: assignedToUser?.id || null, + approving_facility: null, + emergency: data.emergency === "true", + title: data.title, + reason: data.reason, + referring_facility_contact_name: data.referring_facility_contact_name, + referring_facility_contact_number: data.referring_facility_contact_number, + related_patient: related_patient, + priority: data.priority, + }; + + if (id) { + updateResource({ ...resourcePayload, id }); + } else { + createResource(resourcePayload); + } + }; + const { data: facilities } = useQuery({ + queryKey: ["facilities", facilitySearch], + queryFn: query.debounced(facilityApi.getAllFacilities, { + queryParams: { + search_text: facilitySearch, + limit: 50, + }, + }), + }); + + const facilityOptions = facilities?.results.map((facility) => ({ + label: facility.name, + value: facility.id, + })); + + const handleUserChange = (user: UserBase) => { + form.setValue("assigned_to", user.id); + setAssignedToUser(user); + }; + + const fillMyDetails = () => { + form.setValue( + "referring_facility_contact_name", + `${authUser.first_name} ${authUser.last_name}`.trim(), + ); + if (authUser.phone_number) { + form.setValue("referring_facility_contact_number", authUser.phone_number); + } + }; + + if (isPending || isUpdatePending) { + return ; + } + + return ( + + + + + + + {patientData && ( + + + + + + {t("linked_patient")}:{" "} + + {patientData.name} + + + + + + )} + + + + + {t("basic_information")} + + + {t("resource_request_basic_info_description")} + + + + + ( + + + {t("facility_for_care_support")} + + + { + const facility = + facilities?.results.find( + (f) => f.id === value, + ) ?? null; + form.setValue("assigned_facility", facility); + }} + /> + + + {t("select_facility_description")} + + + + )} + /> + + ( + + {t("is_this_an_emergency")} + + + + + + + + {t("yes")} + + + + + + + + {t("no")} + + + + + + {t("emergency_description")} + + + + )} + /> + + ( + + {t("status")} + + + + + + + + {RESOURCE_STATUS_CHOICES.map((option, index) => ( + + {t(`resource_status__${option.text}`)} + + ))} + + + + + + )} + /> + ( + + {t("category")} + + + + + + + + {RESOURCE_CATEGORY_CHOICES.map((category) => ( + + {category.text} + + ))} + + + + + )} + /> + ( + + {t("assigned_to")} + + + + + + )} + /> + + + + + + + + {t("request_details")} + + + {t("resource_request_details_description")} + + + + ( + + {t("request_title")} + + field.onChange(value)} + /> + + + {t("request_title_description")} + + + + )} + /> + + ( + + {t("request_reason")} + + field.onChange(value)} + /> + + + {t("request_reason_description")} + + + + )} + /> + + + + + + + + + {t("contact_information")} + + + {t("contact_information_description")} + + + + + {t("fill_my_details")} + + + + + ( + + {t("contact_person")} + + field.onChange(value)} + /> + + + {t("contact_person_description")} + + + + )} + /> + + ( + + {t("contact_phone")} + + field.onChange(value)} + /> + + + {t("contact_phone_description")} + + + + )} + /> + + + + + goBack()} + > + {t("cancel")} + + + {isPending && ( + + )} + {isPending ? t("submitting") : t("submit")} + + + + + + + + + ); +} diff --git a/src/components/Users/UserAvatar.tsx b/src/components/Users/UserAvatar.tsx index 1d16f4ba8bd..2d81119e30c 100644 --- a/src/components/Users/UserAvatar.tsx +++ b/src/components/Users/UserAvatar.tsx @@ -97,14 +97,14 @@ export default function UserAvatar({ username }: { username: string }) { imageUrl={userData?.profile_picture_url} handleUpload={handleAvatarUpload} handleDelete={handleAvatarDelete} - onClose={() => setEditAvatar(false)} + onOpenChange={(open) => setEditAvatar(open)} /> diff --git a/src/components/Users/UserDeleteDialog.tsx b/src/components/Users/UserDeleteDialog.tsx index 5a2becefe3d..a088c9f2512 100644 --- a/src/components/Users/UserDeleteDialog.tsx +++ b/src/components/Users/UserDeleteDialog.tsx @@ -1,26 +1,51 @@ -import ConfirmDialog from "@/components/Common/ConfirmDialog"; +import { useTranslation } from "react-i18next"; + +import { cn } from "@/lib/utils"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { buttonVariants } from "@/components/ui/button"; interface ConfirmDialogProps { name: string; handleCancel: () => void; handleOk: () => void; + show: boolean; } const UserDeleteDialog = (props: ConfirmDialogProps) => { + const { t } = useTranslation(); return ( - - Are you sure you want to delete user {props.name} ? - - } - action="Delete" - variant="destructive" - show - onConfirm={props.handleOk} - onClose={props.handleCancel} - /> + + + + {t("delete_user")} + + {t("are_you_sure_you_want_to_delete_user")} + {props.name}? + + + + + {t("cancel")} + + + {t("delete")} + + + + ); }; diff --git a/src/components/Users/UserForm.tsx b/src/components/Users/UserForm.tsx index 76010214af9..ee78d887d9f 100644 --- a/src/components/Users/UserForm.tsx +++ b/src/components/Users/UserForm.tsx @@ -145,7 +145,7 @@ export default function UserForm({ last_name: userData.last_name, email: userData.email, phone_number: userData.phone_number || "", - gender: userData.gender, + gender: userData.gender || undefined, }; form.reset(formData); } @@ -155,7 +155,7 @@ export default function UserForm({ const [isUsernameFieldFocused, setIsUsernameFieldFocused] = useState(false); //const userType = form.watch("user_type"); - const usernameInput = form.watch("username"); + const usernameInput = form.watch("username") || ""; const phoneNumber = form.watch("phone_number"); useEffect(() => { diff --git a/src/components/Users/UserFormValidations.tsx b/src/components/Users/UserFormValidations.tsx index f07e08a9718..f87017fa479 100644 --- a/src/components/Users/UserFormValidations.tsx +++ b/src/components/Users/UserFormValidations.tsx @@ -1,8 +1,8 @@ import { Trans } from "react-i18next"; -import CareIcon from "@/CAREUI/icons/CareIcon"; +import { cn } from "@/lib/utils"; -import { classNames } from "@/Utils/utils"; +import CareIcon from "@/CAREUI/icons/CareIcon"; export type UserType = "doctor" | "nurse" | "staff" | "volunteer"; @@ -72,7 +72,7 @@ export const validateRule = ( )}{" "} {showDeleteDialog && ( { @@ -96,6 +97,7 @@ export default function UserSummaryTab({ userData }: { userData?: UserBase }) { }} /> )} + ( { const currentValue = - options.find( - (option) => option.label.toLowerCase() === v.toLowerCase(), - )?.value || ""; - onChange(currentValue === value ? "" : currentValue); + options.find((o) => `${o.label} - ${o.value}` === v)?.value || + ""; + onChange(currentValue); setOpen(false); }} > diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index b76d7c5b131..e2514538690 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -8,7 +8,7 @@ const Input = React.forwardRef>( + - - - setArchiveDialogueOpen(null)} + > + + + + + + + + + {t("archive_file")} + + {t("this_action_is_irreversible")} + + + + + + + { + event.preventDefault(); + handleFileArchive(archiveDialogueOpen); + }} + className="mx-2 my-4 flex w-full flex-col" + > + + + }} + /> + + setArchiveReason(e.target.value)} + className={cn( + archiveReasonError && + "border-red-500 focus-visible:ring-red-500", + )} /> + {archiveReasonError && ( + {archiveReasonError} + )} - - Archive File - - {t("this_action_is_irreversible")} - + + setArchiveDialogueOpen(null)} + > + {t("cancel")} + + + {t("proceed")} + - + + + + setArchiveDialogueOpen(null)} + onOpenChange={() => setArchiveDialogueOpen(null)} > - { - event.preventDefault(); - handleFileArchive(archiveDialogueOpen); - }} - className="mx-2 my-4 flex w-full flex-col" - > - - - }} - /> - - setArchiveReason(e.target.value)} - className={cn( - archiveReasonError && - "border-red-500 focus-visible:ring-red-500", - )} - /> - {archiveReasonError && ( - {archiveReasonError} - )} + + + + {archiveDialogueOpen?.name} {t("archived")} + + + + + {t("this_file_has_been_archived")} + + + {[ + { + label: "File Name", + content: archiveDialogueOpen?.name, + icon: "l-file", + }, + { + label: "Uploaded By", + content: archiveDialogueOpen?.uploaded_by?.username, + icon: "l-user", + }, + { + label: "Uploaded On", + content: formatDateTime(archiveDialogueOpen?.created_date), + icon: "l-clock", + }, + { + label: "Archive Reason", + content: archiveDialogueOpen?.archive_reason, + icon: "l-archive", + }, + { + label: "Archived By", + content: archiveDialogueOpen?.archived_by?.username, + icon: "l-user", + }, + { + label: "Archived On", + content: formatDateTime(archiveDialogueOpen?.archived_datetime), + icon: "l-clock", + }, + ].map((item, index) => ( + + + + + + + {item.label} + + + {item.content} + + + + ))} - + {t("cancel")} - - {t("proceed")} - - - - - {archiveDialogueOpen?.name} (Archived) - - } - fixedWidth={false} - className="md:w-[700px]" - onClose={() => setArchiveDialogueOpen(null)} + + + setEditDialogueOpen(null)} > - - - {t("this_file_has_been_archived")} - - - {[ - { - label: "File Name", - content: archiveDialogueOpen?.name, - icon: "l-file", - }, - { - label: "Uploaded By", - content: archiveDialogueOpen?.uploaded_by?.username, - icon: "l-user", - }, - { - label: "Uploaded On", - content: formatDateTime(archiveDialogueOpen?.created_date), - icon: "l-clock", - }, - { - label: "Archive Reason", - content: archiveDialogueOpen?.archive_reason, - icon: "l-archive", - }, - { - label: "Archived By", - content: archiveDialogueOpen?.archived_by?.username, - icon: "l-user", - }, - { - label: "Archived On", - content: formatDateTime(archiveDialogueOpen?.archived_datetime), - icon: "l-clock", - }, - ].map((item, index) => ( - - - - - - - {item.label} + + + + + + - - {item.content} + + {t("rename_file")} - - ))} - - - setArchiveDialogueOpen(null)} + + + { + event.preventDefault(); + setEditing(true); + if (editDialogueOpen) partialupdateFileName(editDialogueOpen); + }} + className="flex w-full flex-col" > - {t("cancel")} - - - - - - + {t("enter_the_file_name")} + { + setEditDialogueOpen({ + ...editDialogueOpen, + name: e.target.value, + }); + }} /> + {editError && {editError}} - - {t("rename_file")} + + setEditDialogueOpen(null)} + > + {t("cancel")} + + + {t("proceed")} + - - } - onClose={() => setEditDialogueOpen(null)} - > - { - event.preventDefault(); - setEditing(true); - if (editDialogueOpen) partialupdateFileName(editDialogueOpen); - }} - className="flex w-full flex-col" - > - - {t("enter_the_file_name")} - { - setEditDialogueOpen({ - ...editDialogueOpen, - name: e.target.value, - }); - }} - /> - {editError && {editError}} - - - setEditDialogueOpen(null)} - > - {t("cancel")} - - - {t("proceed")} - - - - + + + > ); diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index d53b9497e08..d1b556fbb22 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -363,8 +363,8 @@ export default function useFileUpload( const Dialogues = ( <> setCameraModalOpen(false)} + open={cameraModalOpen} + onOpenChange={(open) => setCameraModalOpen(open)} onCapture={(file) => { setFiles((prev) => [...prev, file]); }} diff --git a/src/hooks/useFilters.tsx b/src/hooks/useFilters.tsx index b6e9022a675..a0c10a7a24e 100644 --- a/src/hooks/useFilters.tsx +++ b/src/hooks/useFilters.tsx @@ -2,12 +2,14 @@ import { QueryParam, setQueryParamsOptions, useQueryParams } from "raviger"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { cn } from "@/lib/utils"; + import GenericFilterBadge from "@/CAREUI/display/FilterBadge"; import PaginationComponent from "@/components/Common/Pagination"; import FiltersCache from "@/Utils/FiltersCache"; -import { classNames, humanizeStrings } from "@/Utils/utils"; +import { humanizeStrings } from "@/Utils/utils"; export type FilterState = Record; @@ -222,7 +224,7 @@ export default function useFilters({ } return ( limit ? "visible" : "invisible", !noMargin && "mt-4", diff --git a/src/index.tsx b/src/index.tsx index 9154b832187..4f8ac291d51 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import careConfig from "@careConfig"; import * as Sentry from "@sentry/browser"; +import React from "react"; import { createRoot } from "react-dom/client"; import { registerSW } from "virtual:pwa-register"; @@ -31,4 +32,8 @@ if (import.meta.env.PROD) { } const root = createRoot(document.getElementById("root") as HTMLElement); -root.render(); +root.render( + + + , +); diff --git a/src/pages/Appointments/AppointmentsPage.tsx b/src/pages/Appointments/AppointmentsPage.tsx index 2a9fd13d298..dfb7c5e1401 100644 --- a/src/pages/Appointments/AppointmentsPage.tsx +++ b/src/pages/Appointments/AppointmentsPage.tsx @@ -399,53 +399,52 @@ export default function AppointmentsPage(props: { facilityId?: string }) { : t("no_results")} - + + setQParams({ + ...qParams, + practitioner: null, + slot: null, + }) + } + className="cursor-pointer w-full" + > + + {t("show_all")} + {!qParams.practitioner && ( + + )} + + + {schedulableUsersQuery.data?.users.map((user) => ( setQParams({ ...qParams, - practitioner: null, + practitioner: user.username, slot: null, }) } - className="cursor-pointer" + className="cursor-pointer w-full" > - {t("show_all")} - {!qParams.practitioner && ( + + + {formatName(user)} + + {user.user_type} + + + {qParams.practitioner === user.username && ( )} - - {schedulableUsersQuery.data?.users.map((user) => ( - - - setQParams({ - ...qParams, - practitioner: user.username, - slot: null, - }) - } - className="cursor-pointer" - > - - - {formatName(user)} - - {user.user_type} - - - {qParams.practitioner === user.username && ( - - )} - - ))} diff --git a/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx b/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx index 0319faf83d2..d0bde50e2ce 100644 --- a/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx +++ b/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx @@ -1,19 +1,19 @@ import { useQuery } from "@tanstack/react-query"; -import { useQueryParams } from "raviger"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; import { Avatar } from "@/components/Common/Avatar"; -import { - CardGridSkeleton, - CardListSkeleton, -} from "@/components/Common/SkeletonLoading"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import { UserStatusIndicator } from "@/components/Users/UserListAndCard"; +import useFilters from "@/hooks/useFilters"; + import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; import AddUserSheet from "@/pages/Organization/components/AddUserSheet"; @@ -29,19 +29,30 @@ interface Props { } export default function FacilityOrganizationUsers({ id, facilityId }: Props) { - const [qParams, setQueryParams] = useQueryParams<{ + const [sheetState, setSheetState] = useState<{ sheet: string; username: string; - }>(); + }>({ + sheet: "", + username: "", + }); + const { qParams, updateQuery, Pagination, resultsPerPage } = useFilters({ + limit: 12, + }); const { t } = useTranslation(); - const openAddUserSheet = qParams.sheet === "add"; - const openLinkUserSheet = qParams.sheet === "link"; + const openAddUserSheet = sheetState.sheet === "add"; + const openLinkUserSheet = sheetState.sheet === "link"; const { data: users, isLoading: isLoadingUsers } = useQuery({ - queryKey: ["facilityOrganizationUsers", facilityId, id], - queryFn: query(routes.facilityOrganization.listUsers, { + queryKey: ["facilityOrganizationUsers", facilityId, id, qParams], + queryFn: query.debounced(routes.facilityOrganization.listUsers, { pathParams: { facilityId, organizationId: id }, + queryParams: { + search_text: qParams.search || undefined, + limit: resultsPerPage, + offset: ((qParams.page || 1) - 1) * resultsPerPage, + }, }), enabled: !!id, }); @@ -50,32 +61,32 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) { return null; } - if (isLoadingUsers) { - return ( - - - - - - - - - ); - } - return ( - - {t("users")} - + + + + { + updateQuery({ search: e.target.value || undefined }); + }} + className="w-full pl-8" + /> + + { - setQueryParams({ sheet: open ? "add" : "", username: "" }); + setSheetState({ sheet: open ? "add" : "", username: "" }); }} onUserCreated={(user) => { - setQueryParams({ sheet: "link", username: user.username }); + setSheetState({ sheet: "link", username: user.username }); }} /> { - setQueryParams({ sheet: open ? "link" : "", username: "" }); + setSheetState({ sheet: open ? "link" : "", username: "" }); }} - preSelectedUsername={qParams.username} + preSelectedUsername={sheetState.username} /> - - {users?.results?.length === 0 ? ( - - - {t("no_users_found")} - - - ) : ( - users?.results?.map((userRole: OrganizationUserRole) => ( - - - - - - - - - - {userRole.user.first_name} {userRole.user.last_name} - - - + {isLoadingUsers ? ( + + + + ) : ( + + + {!users?.results?.length ? ( + + + {t("no_users_found")} + + + ) : ( + users.results.map((userRole: OrganizationUserRole) => ( + + + + + + + + + + {userRole.user.first_name}{" "} + {userRole.user.last_name} + + + + + + {userRole.user.username} - - + + + + {t("role")} + + {userRole.role.name} + + + + + {t("phone_number")} + + + {userRole.user.phone_number} + + + - - - - {t("role")} - - {userRole.role.name} - + + + {t("see_details")} + + } + /> - - {t("phone_number")} - - {userRole.user.phone_number} - - - - - - - - {t("more_details")} - - } - /> - - - - - )) - )} - + + + )) + )} + + + {(users?.results || []).length > 0 && + users?.count && + users.count > resultsPerPage && ( + + + + )} + + )} ); diff --git a/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx b/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx index 0726a6d44fe..741ec7aa046 100644 --- a/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx +++ b/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx @@ -1,4 +1,5 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { t } from "i18next"; import { useState } from "react"; import { toast } from "sonner"; @@ -62,7 +63,7 @@ export default function CreateFacilityOrganizationSheet({ queryClient.invalidateQueries({ queryKey: ["getCurrentUser"], }); - toast.success("Organization created successfully"); + toast.success(t("organization_created_successfully")); setOpen(false); setName(""); setDescription(""); @@ -78,7 +79,7 @@ export default function CreateFacilityOrganizationSheet({ const handleSubmit = () => { if (!name.trim()) { - toast.error("Please enter an organization name"); + toast.error(t("please_enter_organization_name")); return; } @@ -93,16 +94,16 @@ export default function CreateFacilityOrganizationSheet({ return ( - + - Create Department/Team + {t("add_department_team")} - Create Department/Team + {t("create_department_team")} - Create a new department/team in this facility. + {t("create_department_team_description")} @@ -111,18 +112,18 @@ export default function CreateFacilityOrganizationSheet({ setName(e.target.value)} - placeholder="Enter department/team name" + placeholder={t("enter_department_team_name")} /> - Type + {t(`type`)} setOrgType(value)} > - + {ORG_TYPES.map((type) => ( @@ -139,7 +140,7 @@ export default function CreateFacilityOrganizationSheet({ setDescription(e.target.value)} - placeholder="Enter department/team description (optional)" + placeholder={t("enter_department_team_description")} /> @@ -148,7 +149,7 @@ export default function CreateFacilityOrganizationSheet({ onClick={handleSubmit} disabled={isPending || !name.trim()} > - {isPending ? "Creating..." : "Create Organization"} + {isPending ? t("creating") : t("create_organization")} diff --git a/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx b/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx index fd34b3449e2..de12da5794e 100644 --- a/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx +++ b/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx @@ -105,7 +105,9 @@ export default function FacilityOrganizationLayout({ asChild className="text-sm text-gray-900 hover:underline hover:underline-offset-2" > - {parent.name} + + {parent.name} + diff --git a/src/pages/UserDashboard.tsx b/src/pages/UserDashboard.tsx index ab7e8430033..7ba9c365b25 100644 --- a/src/pages/UserDashboard.tsx +++ b/src/pages/UserDashboard.tsx @@ -8,6 +8,7 @@ import { Avatar } from "@/components/Common/Avatar"; import useAuthUser, { useAuthContext } from "@/hooks/useAuthUser"; +import { formatDisplayName } from "@/Utils/utils"; import { getOrgLabel } from "@/types/organization/organization"; export default function UserDashboard() { @@ -22,7 +23,7 @@ export default function UserDashboard() { diff --git a/src/pluginTypes.ts b/src/pluginTypes.ts index f25c3aa7eb3..180df1502a2 100644 --- a/src/pluginTypes.ts +++ b/src/pluginTypes.ts @@ -31,6 +31,10 @@ export type PatientInfoCardActionsComponentType = React.FC<{ className?: string; }>; +export type PatientInfoCardMarkAsCompleteComponentType = React.FC<{ + encounter: Encounter; +}>; + export type FacilityHomeActionsComponentType = React.FC<{ facility: FacilityData; className?: string; @@ -53,6 +57,7 @@ export type SupportedPluginComponents = { Scribe: ScribeComponentType; PatientHomeActions: PatientHomeActionsComponentType; PatientInfoCardActions: PatientInfoCardActionsComponentType; + PatientInfoCardMarkAsComplete: PatientInfoCardMarkAsCompleteComponentType; FacilityHomeActions: FacilityHomeActionsComponentType; PatientRegistrationForm: PatientRegistrationFormComponentType; PatientDetailsTabDemographyGeneralInfo: PatientDetailsTabDemographyGeneralInfoComponentType; diff --git a/src/service-worker.ts b/src/service-worker.ts index 078e4237c28..973ff051736 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -1,6 +1,4 @@ /// - -/* eslint-disable no-restricted-globals */ // This service worker can be customized! // See https://developers.google.com/web/tools/workbox/modules // for the list of available Workbox modules, or add any other @@ -13,7 +11,6 @@ import { precacheAndRoute } from "workbox-precaching"; declare const self: ServiceWorkerGlobalScope; -// eslint-disable-next-line no-restricted-globals const _ignored = self.__WB_MANIFEST.map((_) => { return _; }); diff --git a/src/style/CAREUI.css b/src/style/CAREUI.css index 893d16c2e4e..0e01efa2853 100644 --- a/src/style/CAREUI.css +++ b/src/style/CAREUI.css @@ -1,34 +1,3 @@ -.cui-input-legend::before { - content: " "; - position: absolute; - left: -4px; - right: -4px; - top: calc(50% - 3px); - height: 6px; - background: #fff; - z-index: -1; -} - -.cui-label-required::after { - content: "*"; - color: rgb(255, 81, 0); - font-size: 1.2em; - font-weight: bold; - margin-left: 4px; -} - -.cui-input-base { - @apply text-sm block w-full py-3 px-4 text-black placeholder:text-secondary-600 bg-white disabled:bg-secondary-200 border border-secondary-400 focus:border-primary-400 ring-0 focus:ring-1 ring-primary-400 outline-none focus:outline-none shadow-none rounded transition-colors duration-300 !important -} - -.cui-dropdown-base { - @apply z-40 w-full rounded-b-md xl:rounded-b-lg shadow-lg overflow-auto max-h-96 bg-secondary-100 divide-y divide-secondary-300 ring-1 ring-secondary-400 focus:outline-none -} - -.cui-card { - @apply bg-white p-5 rounded-lg shadow -} - .tooltip { @apply relative } @@ -83,41 +52,6 @@ transform: translateY(0px); } -.cui-input:-webkit-autofill, -.cui-input:-webkit-autofill:hover, -.cui-input:-webkit-autofill:focus, -.cui-input:-webkit-autofill:active { - box-shadow: 0 0 0 40px #f9fafb inset !important; - -webkit-box-shadow: 0 0 0 40px #f9fafb inset !important; -} - -.cui-slideover-x { - @apply w-full md:w-[300px] h-full -} - -.cui-slideover-y { - @apply h-full md:h-[300px] w-full -} - -.dropdown-item-primary { @apply accent-primary-500 hover:bg-primary-100 text-black hover:text-primary-500 } -.dropdown-item-secondary { @apply accent-secondary-200 hover:bg-secondary-200 text-secondary-700 } -.dropdown-item-danger { @apply accent-danger-500 hover:bg-danger-100 text-danger-500 } -.dropdown-item-warning { @apply accent-warning-500 hover:bg-warning-100 text-warning-500 } -.dropdown-item-alert { @apply accent-alert-600 hover:bg-alert-100 text-alert-600 } - -.cui-form-button-group { - @apply flex flex-col-reverse md:flex-row md:justify-end gap-2 w-full md:w-auto -} - -.cui-range-slider { - @apply outline-none bg-black/10 h-2 w-full appearance-none transition-all -} -.cui-range-slider::-webkit-slider-thumb { - @apply appearance-none w-5 aspect-square bg-black border border-black rounded-full cursor-pointer hover:scale-125 transition-all -} -.cui-range-slider::-moz-range-thumb { - @apply appearance-none w-5 aspect-square bg-black border border-black rounded-full cursor-pointer -} hr { @apply border border-secondary-300 } diff --git a/src/style/index.css b/src/style/index.css index 2f14f673a18..4c7410e732b 100644 --- a/src/style/index.css +++ b/src/style/index.css @@ -10,23 +10,7 @@ @tailwind base; @tailwind components; - -@keyframes indeterminateAnimation { - 0% { - transform: translateX(0) scaleX(0); - } - 40% { - transform: translateX(0) scaleX(0.4); - } - 100% { - transform: translateX(100%) scaleX(0.5); - } -} - -.indeterminate { - animation: indeterminateAnimation 1s infinite linear; - transform-origin: 0% 50%; -} +@tailwind utilities; html { @@ -72,363 +56,12 @@ h6 { font-size: 0.875rem; } -.btn { - @apply inline-flex items-center justify-center whitespace-nowrap text-sm font-semibold py-2 px-4 rounded cursor-pointer; -} - -.btn:focus { - @apply outline-none shadow-inner; -} - -[type="text"]:focus { - @apply border-black ring-white; -} - - -/* Button Styles */ - -.btn-large { - @apply py-2 px-5 text-base h-10; -} - -a { - @apply text-blue-800; -} - -a:hover { - @apply text-blue-600; -} - -.bg-primary { - background-image: linear-gradient( 135deg, rgba(56, 161, 105, 1) 0%, rgba(47, 133, 90, 1) 100%); -} - - -/* Button */ - -button:focus { - outline: none; -} - -.btn { - @apply inline-flex items-center justify-center whitespace-nowrap text-sm font-semibold py-2 px-4 rounded cursor-pointer; -} - -.btn:focus { - @apply outline-none shadow-inner; -} - - -/* Button Styles */ - -.btn-default { - @apply border text-secondary-800; -} - -.btn-default:hover { - @apply bg-secondary-100 text-secondary-600; -} - -.btn-default:focus { - @apply bg-secondary-400 text-secondary-700; -} - -.btn-subtle { - @apply bg-secondary-200 text-secondary-800; -} - -.btn-subtle:hover { - @apply bg-secondary-300 text-secondary-900; -} - -.btn-subtle:focus { - @apply bg-secondary-400 text-secondary-900; -} - -.btn-primary-ghost { - @apply bg-white text-primary-500 border border-primary-500; -} - -.btn-primary-ghost:hover { - @apply bg-primary-100 text-primary-600 border-primary-400; -} - -.btn-primary-ghost:focus { - @apply bg-primary-800 text-white border-primary-400; -} - -.btn-primary { - @apply bg-primary-500 text-white; -} - -.btn-primary:hover { - @apply bg-primary-600; -} - -.btn-primary:focus { - @apply bg-primary-800; - background-image: none; -} - -.btn-warning { - @apply bg-yellow-500 text-white; - background-image: linear-gradient( 135deg, rgba(237, 137, 54, 1) 0%, rgba(221, 107, 32, 1) 100%); -} - -.btn-warning:hover { - @apply bg-yellow-600; - background-image: linear-gradient( 135deg, rgba(221, 107, 32, 1) 0%, rgba(192, 86, 33, 1) 100%); -} - -.btn-warning:focus { - @apply bg-yellow-800; - background-image: none; -} - -.btn-danger { - @apply bg-red-500 text-white; - background-image: linear-gradient( 135deg, rgba(245, 101, 101, 1) 0%, rgba(229, 62, 62, 1) 100%); -} - -.btn-danger:hover { - @apply bg-red-600; - background-image: linear-gradient( 135deg, rgba(229, 62, 62, 1) 0%, rgba(197, 48, 48, 1) 100%); -} - -.btn-danger:focus { - @apply bg-red-800; - background-image: none; -} - - -.primary-button { - @apply focus:outline-none text-white bg-primary-500 hover:bg-primary-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-6 py-2.5 mr-2 mb-2 transition-all ease-in-out duration-200 !important; -} - -.secondary-button { - @apply text-secondary-900 bg-white border border-secondary-300 focus:outline-none hover:bg-secondary-100 focus:ring-4 focus:ring-secondary-200 font-medium rounded-lg text-sm px-6 py-2.5 mr-2 mb-2 transition-all ease-in-out duration-200 !important; -} - -button:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-300 text-secondary-500 shadow-none border-transparent; - background-image: none; -} - -button:disabled:hover, -.disabled:hover, -button:disabled:focus, -.disabled:focus { - @apply bg-secondary-300 text-secondary-500 border-transparent shadow-none; - background-image: none; -} - -.btn-small { - @apply py-1 px-3 text-xs h-7 leading-normal; -} - -.btn-normal { - @apply py-1 px-4 text-sm h-8; -} - -.btn-large { - @apply py-2 px-5 text-base h-10; -} - -.button-xl { - @apply py-2 px-6 text-base h-12; -} - -@tailwind utilities; -.multiselect-dropdown__search-dropdown { - max-height: 20rem; - overflow: auto; -} - -.max-height-dropdown { - max-height: 20rem; - overflow: auto; -} - - -/* Radio */ - -.radio-label { - @apply select-none cursor-pointer; -} - -.radio-label span { - @apply inline-block align-middle; - transform: translate3d(0, 0, 0); -} - -.radio-label span:first-child { - @apply relative rounded-full align-middle border border-secondary-500 bg-white mr-3; - width: 1.125rem; - height: 1.125rem; - transform: scale(1); - transition: all 0.2s ease; -} - -.radio-label span:first-child svg { - position: absolute; - top: 0; - left: 0; - fill: none; - stroke: #ffffff; - stroke-width: 2; - stroke-linecap: round; - stroke-linejoin: round; - transition: all 0.3s ease; - transition-delay: 0.1s; -} - -.radio-label span:first-child:before { - content: ""; - @apply bg-primary-500 w-full h-full block opacity-100 rounded-full; - transform: scale(0); -} - -.radio-label:hover span:first-child { - @apply bg-primary-100 border-primary-500; -} - -.radio-input:checked+.radio-label span:first-child { - @apply bg-primary-500 border-primary-500; - animation: wave 0.4s ease; -} - -.radio-input:checked+.radio-label span:first-child:before { - transform: scale(3.5); - opacity: 0; - transition: all 0.6s ease; -} - - -/* Checkbox */ - -.checkbox__label { - @apply select-none cursor-pointer; -} - -.checkbox__label span { - @apply inline-block align-middle; - transform: translate3d(0, 0, 0); -} - -.checkbox__label span:first-child { - @apply relative rounded align-middle border border-secondary-500 bg-white w-4 h-4 mr-3; - transform: scale(1); - transition: all 0.2s ease; -} - -.checkbox__label span:first-child svg { - position: absolute; - top: 3px; - left: 2px; - fill: none; - stroke: #ffffff; - stroke-width: 2; - stroke-linecap: round; - stroke-linejoin: round; - stroke-dasharray: 16px; - stroke-dashoffset: 16px; - transition: all 0.3s ease; - transition-delay: 0.1s; - transform: translate3d(0, 0, 0); -} - -.checkbox__label span:first-child:before { - content: ""; - @apply bg-primary-500 w-full h-full block opacity-100 rounded-full; - transform: scale(0); -} - -.checkbox__label:hover span:first-child { - @apply bg-primary-100 border-primary-500; -} - -.checkbox__input:checked+.checkbox__label span:first-child { - @apply bg-primary-500 border-primary-500; - animation: wave 0.4s ease; -} - -.checkbox__input:checked+.checkbox__label span:first-child svg { - stroke-dashoffset: 0; -} - -.checkbox__input:checked+.checkbox__label span:first-child:before { - transform: scale(3.5); - opacity: 0; - transition: all 0.6s ease; -} - @keyframes wave { 50% { transform: scale(0.9); } } -label { - @apply block text-secondary-700 text-sm font-medium; -} - -input { - @apply border-secondary-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 transition-colors duration-300 !important; -} - -input:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-200 border-secondary-400 text-secondary-700; -} - -textarea { - @apply border-secondary-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 transition-colors duration-300; -} - -button:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-300 text-secondary-500 shadow-none border-transparent; - background-image: none; -} - - -/* Styling skeleton loading */ - -.skeleton-placeholder__line-sm { - @apply rounded-full bg-secondary-100; - height: 0.625rem; -} - -.skeleton-placeholder__line-md { - @apply rounded-full bg-secondary-100; - height: 1rem; -} - -.skeleton-placeholder__image { - @apply mt-5 h-48 rounded-lg bg-secondary-100; -} - -.skeleton-animate { - animation: skeletonShimmer 3s infinite linear; - background: linear-gradient(to right, #f5f3f7 10%, #fbfafc 40%, #f5f3f7 70%); - background-size: 1000px 100%; -} - -@keyframes skeletonShimmer { - 0% { - background-position: -1000px 0; - } - 100% { - background-position: 1000px 0; - } -} - -.skeleton-animate-alpha { - animation: skeletonShimmer 3s infinite linear; - background: linear-gradient(to right, rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.1) 70%); - background-size: 1000px 100%; -} @media print { @page { @@ -449,20 +82,6 @@ button:disabled, } } -.header-section .appBar { - z-index: 1201; -} - -.header-section .toolbar { - padding-top: 72px; -} - -@media only screen and (min-width: 601px) { - .header-section .menuButton { - display: none; - } -} - .App { text-align: center; } @@ -497,87 +116,10 @@ button:disabled, 100% {opacity: 0;} } -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 12px; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; - transition: color 0.15s; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #007bff; -} - -.badge-secondary { - color: #fff; - background-color: #6c757d; -} - -.badge-success { - color: #fff; - background-color: #28a745; -} - -.badge-warning { - color: #212529; - background-color: #ffc107; -} - -.badge-danger { - color: #fff; - background-color: #dc3545; -} - -.badge-dark { - color: #fff; - background-color: #343a40; -} - .dropdown:hover .dropdown-menu { display: block; } -.border-primary { - border-color: #28a745; -} - -.form-input { - padding-top: 6px; - padding-bottom: 6px; - width: 100%; - border-radius: 4px; - height: 38px; -} - -.form-input:focus { - border-color: #28a745 !important; - box-shadow: 0 0 0 1px #28a745 !important; -} - -.copied-to-cb { - color: #28a745; - font-size: 13px; - padding-left: 5px; - font-style: italic; -} - -.copy-to-cb { - padding-left: 5px; -} - ::-webkit-scrollbar { width: 10px; height: 8px; @@ -596,166 +138,6 @@ button:disabled, background: rgba(0, 0, 0, 0.4); } -@media (min-width:1000px) { - .manualGrid { - display: grid !important - } -} - -.scrollbar-hide::-webkit-scrollbar { - width: 0px; - height: 0px; -} - -/* - Range Sliders for Camera Feed -*/ - -#feed-range { - -webkit-appearance: none; - appearance: none; - margin: 18px 0; - width: 100%; -} - -#feed-range:focus { - outline: none; -} - -#feed-range::-webkit-slider-runnable-track { - width: 100%; - height: 1px; - cursor: pointer; - background: transparent; - border-radius: 5px; -} - -#feed-range::-webkit-slider-thumb { - height: 16px; - width: 16px; - border-radius: 3px; - border: 1px solid white; - background: black; - cursor: pointer; - -webkit-appearance: none; - margin-top: -8px; - border-radius: 50%; -} - -@media (min-width:471px) { - .csv-input { - display: block; - font-size: 0.875rem; - line-height: 1.25rem; - margin: auto; - width: 50%; - } -} - -@media (max-width:470px) { - .csv-input { - display: block; - font-size: 0.875rem; - line-height: 1.25rem; - margin: auto; - width: 100%; - } -} - -[type="checkbox"] { - @apply text-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none; -} - - - -.csv-input::file-selector-button:hover { - background-color: #84e1bc; -} - - - -/* for gmaps search dropdown */ -.pac-container { - z-index: 100000 !important; -} - -.csv-input::file-selector-button { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; - font-weight: 600; - border-width: 0; - background-color: #def7ec; - color: #046c4e; -} - -.csv-input::file-selector-button:hover { - background-color: #84e1bc; -} - -/* Patient Category Styles */ - -.patient-comfort { - @apply bg-patient-comfort text-patient-comfort-fore -} -.patient-stable { - @apply bg-patient-stable text-patient-stable-fore -} -.patient-abnormal { - @apply bg-patient-abnormal text-patient-abnormal-fore -} -.patient-critical { - @apply bg-patient-critical text-patient-critical-fore -} -.patient-unknown { - @apply bg-patient-unknown text-patient-unknown-fore -} - -.patient-activelydying { - @apply bg-patient-activelydying text-patient-activelydying-fore -} - -.patient-comfort-ring { - @apply ring-patient-comfort -} -.patient-stable-ring { - @apply ring-patient-stable -} -.patient-abnormal-ring { - @apply ring-patient-abnormal -} -.patient-critical-ring { - @apply ring-patient-critical -} -.patient-unknown-ring { - @apply ring-patient-unknown -} -.patient-activelydying-ring { - @apply ring-patient-activelydying -} - -.patient-comfort-profile { - @apply border-2 border-patient-comfort rounded-t -} -.patient-stable-profile { - @apply border-2 border-patient-stable rounded-t -} -.patient-abnormal-profile { - @apply border-2 border-patient-abnormal rounded-t -} -.patient-critical-profile { - @apply border-2 border-patient-critical rounded-t -} -.patient-unknown-profile { - @apply border border-patient-unknown rounded -} -.patient-activelydying-profile { - @apply border-2 border-patient-activelydying rounded-t -} - /* for gmaps search dropdown */ .pac-container { z-index: 100000 !important; @@ -855,468 +237,6 @@ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-results-button, input[type="search"]::-webkit-search-results-decoration { display: none; } -.service-panel{ - @apply flex-row -} - -@media screen and (max-width: 920px) { - .service-panel{ - @apply flex-col - } -} -/* styles for files in src/Components/CriticalCareRecording/PressureSore */ - -input[type="checkbox"]:checked~.dot { - transform: translateX(100%); -} - - -/* styles for files in src/Components/CriticalCareRecording/components */ - -.range { - position: relative; - width: 644px; - height: 5px; -} - -.range input { - width: 100%; - position: absolute; - top: 2px; - height: 0; - -webkit-appearance: none; -} - -.range input::-webkit-slider-thumb { - -webkit-appearance: none; - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-moz-range-thumb { - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-ms-thumb { - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-webkit-slider-runnable-track { - width: 100%; - height: 2px; - cursor: pointer; - background: #b2b2b2; - z-index: 1; -} - -.range input::-moz-range-track { - width: 100%; - height: 2px; - cursor: pointer; - background: black; - z-index: 1; -} - -.range input::-ms-track { - width: 100%; - height: 2px; - cursor: pointer; - background: #b2b2b2; - z-index: 1; -} - -.range input:focus { - background: none; - outline: none; -} - -.range input::-ms-track { - width: 100%; - cursor: pointer; - background: transparent; - border-color: transparent; - color: transparent; -} - -.range-labels { - margin: 18px -41px 0; - padding: 0; - list-style: none; -} - -.range-labels li { - position: relative; - float: left; - width: 90.25px; - text-align: center; - color: black; - font-size: 14px; - cursor: pointer; -} - -.range-labels .label::before { - position: absolute; - top: -25px; - right: 0; - left: 0; - content: ""; - margin: 0 auto; - width: 9px; - height: 9px; - background: #b2b2b2; - border-radius: 50%; -} - -.double-range-labels { - margin: 18px -41px 0; - padding: 0; - list-style: none; -} - -.double-range-labels li { - position: relative; - float: left; - width: 45.25px; - text-align: center; - color: black; - font-size: 14px; - cursor: pointer; -} - -.double-range-labels .label::before { - position: absolute; - top: -25px; - right: 0; - left: 0; - content: ""; - margin: 0 auto; - width: 9px; - height: 9px; - background: #b2b2b2; - border-radius: 50%; -} - -.upper-label { - height: 10px; - margin-bottom: 10px; -} - -.pupil1 { - bottom: 0; - margin: 0 auto; - width: 9px; - height: 9px; - border-radius: 50%; - background-color: black; -} - -.pupil2 { - bottom: 0; - margin: 0 auto; - width: 15px; - height: 15px; - border-radius: 50%; - background-color: black; -} - -.pupil3 { - bottom: 0; - margin: 0 auto; - width: 21px; - height: 21px; - border-radius: 50%; - background-color: black; -} - -.pupil4 { - bottom: 0; - margin: 0 auto; - width: 25px; - height: 25px; - border-radius: 50%; - background-color: black; -} - -.pupil5 { - bottom: 0; - margin: 0 auto; - width: 30px; - height: 30px; - border-radius: 50%; - background-color: black; -} - -.pupil6 { - bottom: 0; - margin: 0 auto; - width: 35px; - height: 35px; - border-radius: 50%; - background-color: black; -} - -.pupil7 { - bottom: 0; - margin: 0 auto; - width: 38px; - height: 38px; - border-radius: 50%; - background-color: black; -} - -.pupil8 { - width: 41px; - height: 41px; - border-radius: 50%; - background-color: black; -} - -.range-labels .active { - color: rgb(7, 109, 243); -} - -.range-labels .selected::before { - background: rgb(7, 109, 243); -} - -.range-labels .active.selected::before { - display: none; -} - -.align-circles { - position: relative; - display: flex; - justify-content: center; - height: 41px; - vertical-align: baseline; -} - -input[type="number"] { - background-color: #fbfafc; -} - - -/* Copied CSS */ - -.slider { - position: relative; - width: 200px; -} - -.slider__track, -.slider__range, -.slider__left-value, -.slider__right-value { - position: absolute; -} - -.slider__track, -.slider__range { - border-radius: 3px; - height: 5px; -} - -.slider__track { - background-color: #1476d8; - width: 100%; - z-index: 1; -} - -.slider__range { - background-color: #19ebe0; - z-index: 2; -} - -.slider__left-value, -.slider__right-value { - color: #1c85ee; - font-size: 12px; - margin-top: 20px; -} - -.slider__left-value { - left: 6px; -} - -.slider__right-value { - right: -4px; -} - - -/* Removing the default appearance */ - -.thumb input { - pointer-events: none; - position: absolute; - height: 0; - width: 200px; - outline: none; -} - -.thumb--left { - z-index: 3; -} - -.thumb--right { - z-index: 4; -} - - -/* For Chrome browsers */ - -.thumb input::-webkit-slider-thumb { - background-color: #242525; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #0b2846; - cursor: pointer; - height: 18px; - width: 18px; - margin-top: 4px; - pointer-events: all; - position: relative; -} - - -/* For Firefox browsers */ - -.thumb input::-moz-range-thumb { - background-color: #272b2c; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #0d2d4d; - cursor: pointer; - height: 18px; - width: 18px; - margin-top: 4px; - pointer-events: all; - position: relative; -} - -.slider { - appearance: none; - background-color: #dee0e4; - width: 100%; - height: 4px; - margin: 0; - padding: 0; - z-index: 2; -} - -.slider::-webkit-slider-thumb { - cursor: pointer; -} - -.slider-container { - position: relative; - width: 100%; - /* max-width: 700px; */ - display: flex; - justify-content: center; -} - -.indicators { - position: absolute; - top: -200%; - width: 100%; - height: 20px; - border-left: 2px solid #dee0e4; -} - -.tick { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: linear-gradient(to left, #dee0e4 2px, transparent 2px); -} - -.slider-box { - width: 100%; - /* max-width: 700px; */ - padding: 20px 10px 60px 10px; - border-radius: 12px; -} - -.slider-head { - font-family: sans-serif; - margin-bottom: 2rem; -} - -.slider-head>div>h1 { - font-size: 1rem; - font-weight: bold; -} - -.slider-head input { - padding: 0.5rem 0.7rem; - border-radius: 8px; - border: 1px solid #979aa0; - margin-left: 12px; - font-size: 1.1rem; - color: #383a3e; - letter-spacing: 1px; -} - -.slider-head label { - font-weight: 700; - color: #2856ff; - font-size: 1rem; -} - - -.grid-2-col, .grid-3-col, .grid-2-1-col, .grid-3-1-col, .grid-1-2-col { - display: grid; - grid-template-columns: 1fr; - gap: 1.5rem; -} - -@media (min-width: 768px) { - .grid-2-col { - grid-template-columns: repeat(2, 1fr); - } - .grid-3-col { - grid-template-columns: repeat(3, 1fr); - } - .grid-2-1-col { - grid-template-columns: 2fr 1fr; - } - .grid-1-2-col { - grid-template-columns: 1fr 2fr; - } -} - -.input-field-group { - display: flex; - flex-direction: column; - gap: 0.5rem; -} -.col-span-all { - grid-column: 1 / -1; -} - @layer base { :root { --radius: 0.5rem; diff --git a/src/types/emr/medicationAdministration/medicationAdministration.ts b/src/types/emr/medicationAdministration/medicationAdministration.ts index db45449679f..caf6f254711 100644 --- a/src/types/emr/medicationAdministration/medicationAdministration.ts +++ b/src/types/emr/medicationAdministration/medicationAdministration.ts @@ -92,4 +92,6 @@ export interface MedicationAdministrationRead { dose?: DosageQuantity; rate?: Quantity; }; + created_by: UserBareMinimum; + updated_by: UserBareMinimum; } diff --git a/src/types/emr/medicationAdministration/medicationAdministrationApi.ts b/src/types/emr/medicationAdministration/medicationAdministrationApi.ts index 77e37c391e2..35b980b0349 100644 --- a/src/types/emr/medicationAdministration/medicationAdministrationApi.ts +++ b/src/types/emr/medicationAdministration/medicationAdministrationApi.ts @@ -7,12 +7,12 @@ import { } from "./medicationAdministration"; export default { - listMedicationAdministrations: { + list: { path: "/api/v1/patient/{patientId}/medication/administration/", method: HttpMethod.GET, TRes: Type>(), }, - upsertMedicationAdministration: { + upsert: { path: "/api/v1/patient/{patientId}/medication/administration/upsert/", method: HttpMethod.POST, TRes: Type, diff --git a/src/types/emr/medicationStatement/medicationStatementApi.ts b/src/types/emr/medicationStatement/medicationStatementApi.ts index 68736e4ab13..7f65dc5f3c8 100644 --- a/src/types/emr/medicationStatement/medicationStatementApi.ts +++ b/src/types/emr/medicationStatement/medicationStatementApi.ts @@ -1,12 +1,12 @@ import { Type } from "@/Utils/request/api"; import { PaginatedResponse } from "@/Utils/request/types"; -import { MedicationStatement } from "@/types/emr/medicationStatement"; +import { MedicationStatementRead } from "@/types/emr/medicationStatement"; const medicationStatementApi = { list: { path: "/api/v1/patient/{patientId}/medication/statement/", method: "GET", - TRes: Type>(), + TRes: Type>(), }, } as const; diff --git a/src/types/resourceRequest/resourceRequest.ts b/src/types/resourceRequest/resourceRequest.ts index 7594766017c..07bfdbd2479 100644 --- a/src/types/resourceRequest/resourceRequest.ts +++ b/src/types/resourceRequest/resourceRequest.ts @@ -2,6 +2,7 @@ import { FacilityModel } from "@/components/Facility/models"; import { UserBareMinimum } from "@/components/Users/models"; import { PatientModel } from "@/types/emr/patient"; +import { UserBase } from "@/types/user/user"; export interface ResourceRequest { approving_facility: FacilityModel | null; @@ -17,9 +18,9 @@ export interface ResourceRequest { requested_quantity: number; status: string; title: string; - assigned_to: UserBareMinimum | null; - created_by: UserBareMinimum; - updated_by: UserBareMinimum; + assigned_to: UserBase | null; + created_by: UserBase; + updated_by: UserBase; created_date: string; modified_date: string; related_patient: PatientModel | null; @@ -32,6 +33,7 @@ export interface CreateResourceRequest { referring_facility_contact_name: string; referring_facility_contact_number: string; approving_facility: string | null; + assigned_to: string | null; assigned_facility: string | null; origin_facility: string; related_patient: string; diff --git a/src/types/user/userApi.ts b/src/types/user/userApi.ts index f1f776cece0..dd11f7e2ae2 100644 --- a/src/types/user/userApi.ts +++ b/src/types/user/userApi.ts @@ -30,4 +30,4 @@ export default { TRes: Type(), TBody: Type>(), }, -}; +} as const; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index defba258b80..5e2066155a8 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -19,11 +19,7 @@ interface ImportMetaEnv { readonly REACT_CUSTOM_LOGO_ALT?: string; readonly REACT_CUSTOM_DESCRIPTION?: string; readonly REACT_GMAPS_API_KEY?: string; - readonly REACT_GOV_DATA_API_KEY?: string; readonly REACT_RECAPTCHA_SITE_KEY?: string; - readonly REACT_WARTIME_SHIFTING?: string; - readonly REACT_STILL_WATCHING_IDLE_TIMEOUT?: string; - readonly REACT_STILL_WATCHING_PROMPT_DURATION?: string; readonly REACT_JWT_TOKEN_REFRESH_INTERVAL?: string; readonly REACT_MIN_ENCOUNTER_DATE?: string; readonly REACT_DEFAULT_ENCOUNTER_TYPE?: string; diff --git a/vite.config.mts b/vite.config.mts index 749f188002c..308c942b7ad 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -288,6 +288,7 @@ export default defineConfig(({ mode }) => { server: { port: 4000, host: "0.0.0.0", + allowedHosts: true, }, preview: { headers: {
- {hintMessage} -
+ {hintMessage} +
- {dragProps.fileDropError !== "" - ? dragProps.fileDropError - : `${t("drag_drop_image_to_upload")}`} -
- {t("no_image_found")}. {hintMessage} -
+ {dragProps.fileDropError !== "" + ? dragProps.fileDropError + : `${t("drag_drop_image_to_upload")}`} +
+ {t("no_image_found")}. {hintMessage} +
- {description} -
- {fileNameTooltip} -
- {fileName} + !open && handleClose()}> + + + + {t("file_preview")} + + + + {fileUrl ? ( + <> + + + + + + + {fileNameTooltip} + + + + + {fileName} + + + + + {uploadedFiles && + uploadedFiles[index] && + uploadedFiles[index].created_date && ( + + {t("created_on")}{" "} + {new Date( + uploadedFiles[index].created_date!, + ).toLocaleString("en-US", { + dateStyle: "long", + timeStyle: "short", + })} - - - - {uploadedFiles && - uploadedFiles[index] && - uploadedFiles[index].created_date && ( - - {t("created_on")}{" "} - {new Date( - uploadedFiles[index].created_date!, - ).toLocaleString("en-US", { - dateStyle: "long", - timeStyle: "short", - })} - + )} + + + {downloadURL && downloadURL.length > 0 && ( + + + + {t("download")} + + )} + + {t("close")} + + - - {downloadURL && downloadURL.length > 0 && ( - - - - {t("download")} - + + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index - 1)} + disabled={index <= 0} + aria-label="Previous file" + > + )} - - {t("close")} - - - - - {uploadedFiles && uploadedFiles.length > 1 && ( - handleNext(index - 1)} - disabled={index <= 0} - aria-label="Previous file" - > - - - )} - - {file_state.isImage ? ( - - ) : file_state.extension === "pdf" ? ( - }> - { - setPage(1); - setNumPages(numPages); - }} - pageNumber={page} - scale={scale} + + {file_state.isImage ? ( + - - ) : previewExtensions.includes(file_state.extension) ? ( - - ) : ( - - }> + { + setPage(1); + setNumPages(numPages); + }} + pageNumber={page} + scale={scale} + /> + + ) : previewExtensions.includes(file_state.extension) ? ( + - {t("file_preview_not_supported")} - - )} - + ) : ( + + + {t("file_preview_not_supported")} + + )} + - {uploadedFiles && uploadedFiles.length > 1 && ( - handleNext(index + 1)} - disabled={index >= uploadedFiles.length - 1} - aria-label="Next file" - > - - - )} - - - - {file_state.isImage && ( - <> - {[ - [ - t("Zoom In"), - "l-search-plus", - handleZoomIn, - file_state.zoom === zoom_values.length, - ], - [ - `${25 * file_state.zoom}%`, - false, - () => { - setFileState({ ...file_state, zoom: 4 }); - }, - false, - ], - [ - t("Zoom Out"), - "l-search-minus", - handleZoomOut, - file_state.zoom === 1, - ], - [ - t("Rotate Left"), - "l-corner-up-left", - () => handleRotate(-90), - false, - ], - [ - t("Rotate Right"), - "l-corner-up-right", - () => handleRotate(90), - false, - ], - ].map((button, index) => ( - void} - className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" - disabled={button[3] as boolean} - > - {button[1] && ( - - )} - {button[0] as string} - - ))} - > - )} - {file_state.extension === "pdf" && ( - <> - {[ - ["Zoom In", "l-search-plus", handleZoomIn, scale >= 2], - [`${Math.round(scale * 100)}%`, false, () => {}, false], - ["Zoom Out", "l-search-minus", handleZoomOut, scale <= 0.5], - [ - "Previous", - "l-arrow-left", - () => setPage((prev) => prev - 1), - page === 1, - ], - [`${page}/${numPages}`, false, () => ({}), false], - [ - "Next", - "l-arrow-right", - () => setPage((prev) => prev + 1), - page === numPages, - ], - ].map((button, index) => ( - void} - className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" - disabled={button[3] as boolean} - > - {button[1] && ( - - )} - {button[0] as string} - - ))} - > + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index + 1)} + disabled={index >= uploadedFiles.length - 1} + aria-label={t("next_file")} + > + + )} + + + {file_state.isImage && ( + <> + {[ + [ + t("zoom_in"), + "l-search-plus", + handleZoomIn, + file_state.zoom === zoom_values.length, + ], + [ + `${25 * file_state.zoom}%`, + false, + () => { + setFileState({ ...file_state, zoom: 4 }); + }, + false, + ], + [ + t("zoom_out"), + "l-search-minus", + handleZoomOut, + file_state.zoom === 1, + ], + [ + t("rotate_left"), + "l-corner-up-left", + () => handleRotate(-90), + false, + ], + [ + t("rotate_right"), + "l-corner-up-right", + () => handleRotate(90), + false, + ], + ].map((button, index) => ( + void} + className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" + disabled={button[3] as boolean} + > + {button[1] && ( + + )} + {button[0] as string} + + ))} + > + )} + {file_state.extension === "pdf" && ( + <> + {[ + [t("zoom_in"), "l-search-plus", handleZoomIn, scale >= 2], + [`${Math.round(scale * 100)}%`, false, () => {}, false], + [ + t("zoom_out"), + "l-search-minus", + handleZoomOut, + scale <= 0.5, + ], + [ + t("previous"), + "l-arrow-left", + () => setPage((prev) => prev - 1), + page === 1, + ], + [`${page}/${numPages}`, false, () => ({}), false], + [ + t("next"), + "l-arrow-right", + () => setPage((prev) => prev + 1), + page === numPages, + ], + ].map((button, index) => ( + void} + className="z-50 rounded bg-white/60 px-4 py-2 text-black backdrop-blur transition hover:bg-white/70" + disabled={button[3] as boolean} + > + {button[1] && ( + + )} + {button[0] as string} + + ))} + > + )} + + + > + ) : ( + + - > - ) : ( - - - - )} - + )} + + ); }; diff --git a/src/components/Common/HelperComponents.tsx b/src/components/Common/HelperComponents.tsx deleted file mode 100644 index 5100476284e..00000000000 --- a/src/components/Common/HelperComponents.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Transition, TransitionEvents } from "@headlessui/react"; -import { ReactNode } from "react"; - -type DropdownMenuTransitionProps = { - show?: boolean | undefined; - children: ReactNode; -} & TransitionEvents; - -export const DropdownTransition = ({ - show, - children, - ...transitionEvents -}: DropdownMenuTransitionProps) => ( - - {children} - -); diff --git a/src/components/Common/LanguageSelector.tsx b/src/components/Common/LanguageSelector.tsx index e1f3446eca0..a3b9d5c3d5d 100644 --- a/src/components/Common/LanguageSelector.tsx +++ b/src/components/Common/LanguageSelector.tsx @@ -2,7 +2,9 @@ import careConfig from "@careConfig"; import { useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { classNames, keysOf } from "@/Utils/utils"; +import { cn } from "@/lib/utils"; + +import { keysOf } from "@/Utils/utils"; import { LANGUAGES } from "@/i18n"; export const LanguageSelector = (props: any) => { @@ -27,7 +29,7 @@ export const LanguageSelector = (props: any) => { return ( { @@ -32,7 +34,7 @@ export const LanguageSelectorLogin = () => { handleLanguage(e)} - className={classNames( + className={cn( "text-primary-400 hover:text-primary-600", (i18n.language === e || (i18n.language === "en-US" && e === "en")) && diff --git a/src/components/Common/Tabs.tsx b/src/components/Common/Tabs.tsx deleted file mode 100644 index 9c375db618e..00000000000 --- a/src/components/Common/Tabs.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { type ReactNode, useEffect, useRef } from "react"; - -import useWindowDimensions from "@/hooks/useWindowDimensions"; - -import { classNames } from "@/Utils/utils"; - -export default function Tabs(props: { - className?: string; - currentTab: string | number; - onTabChange: (value: string | number) => void; - tabs: { text: ReactNode; value: string | number; id?: string }[]; -}) { - const { className, currentTab, onTabChange, tabs } = props; - const ref = useRef(null); - const tabSwitcherRef = useRef(null); - - const dimensions = useWindowDimensions(); - - useEffect(() => { - const currentTabIndex = tabs.findIndex((t) => t.value === currentTab); - if ( - typeof currentTabIndex != "number" || - !ref.current || - !tabSwitcherRef.current - ) - return; - const tabButton = ref.current.querySelectorAll("button")[currentTabIndex]; - if (!tabButton) return; - tabSwitcherRef.current.style.width = tabButton.clientWidth + "px"; - tabSwitcherRef.current.style.left = - tabButton.getBoundingClientRect().left - - ref.current.getBoundingClientRect().left + - ref.current.scrollLeft + - "px"; - }, [currentTab, tabSwitcherRef.current, ref.current, dimensions]); - - return ( - - - {/* There has to be a better way of handling this... */} - {tabs.map((tab, i) => ( - - {tab.text} - - ))} - - {tabs.map((tab, i) => ( - onTabChange(tab.value)} - className={`${currentTab === tab.value ? "text-white" : "text-primary-500 hover:text-primary-600"} flex-1 whitespace-nowrap px-6 py-2 text-sm font-semibold transition-all`} - > - {tab.text} - - ))} - - - ); -} diff --git a/src/components/Common/UpdatableApp.tsx b/src/components/Common/UpdatableApp.tsx index 814779a99d3..3a383d73408 100644 --- a/src/components/Common/UpdatableApp.tsx +++ b/src/components/Common/UpdatableApp.tsx @@ -1,12 +1,12 @@ import { Popover, Transition } from "@headlessui/react"; import { ReactNode, useEffect, useState } from "react"; +import { cn } from "@/lib/utils"; + import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; -import { classNames } from "@/Utils/utils"; - const META_URL = "/build-meta.json"; const APP_VERSION_KEY = "app-version"; const APP_UPDATED_KEY = "app-updated"; @@ -120,10 +120,7 @@ const UpdateAppPopup = ({ onUpdate }: UpdateAppPopupProps) => { Software Update diff --git a/src/components/Common/UserAutocompleteFormField.tsx b/src/components/Common/UserAutocompleteFormField.tsx deleted file mode 100644 index 90addafb702..00000000000 --- a/src/components/Common/UserAutocompleteFormField.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { useEffect, useState } from "react"; - -import { Autocomplete } from "@/components/Form/FormFields/Autocomplete"; -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import { UserType } from "@/components/Users/UserFormValidations"; -import { UserBareMinimum } from "@/components/Users/models"; - -import routes from "@/Utils/request/api"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; -import { - classNames, - formatName, - isUserOnline, - mergeQueryOptions, -} from "@/Utils/utils"; - -import { Avatar } from "./Avatar"; - -type BaseProps = FormFieldBaseProps & { - placeholder?: string; - userType?: UserType; - noResultsError?: string; -}; - -type UserSearchProps = BaseProps & { - facilityId?: undefined; - homeFacility?: string; -}; - -export default function UserAutocomplete(props: UserSearchProps) { - const field = useFormFieldPropsResolver(props); - const [query, setQuery] = useState(""); - const [disabled, setDisabled] = useState(false); - - const { data, loading } = useTanStackQueryInstead(routes.userList, { - query: { - home_facility: props.homeFacility, - user_type: props.userType, - search_text: query, - limit: 50, - offset: 0, - }, - }); - - useEffect(() => { - if ( - loading || - query || - !field.required || - !props.noResultsError || - !data?.results - ) { - return; - } - - if (data.results.length === 0) { - setDisabled(true); - field.handleChange(undefined as unknown as UserBareMinimum); - } - }, [loading, field.required, data?.results, props.noResultsError]); - - const getAvatar = (option: UserBareMinimum) => { - return ( - - ); - }; - - return ( - - obj.username, - )} - optionLabel={formatName} - optionIcon={userOnlineDot} - optionImage={getAvatar} - optionDescription={(option) => - `${option.user_type} - ${option.username}` - } - optionValue={(option) => option} - onQuery={setQuery} - isLoading={loading} - /> - - ); -} - -const userOnlineDot = (user: UserBareMinimum) => ( - -); diff --git a/src/components/Facility/DuplicatePatientDialog.tsx b/src/components/Facility/DuplicatePatientDialog.tsx index 2e795092042..beac625f852 100644 --- a/src/components/Facility/DuplicatePatientDialog.tsx +++ b/src/components/Facility/DuplicatePatientDialog.tsx @@ -4,6 +4,13 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { Table, TableBody, @@ -13,120 +20,125 @@ import { TableRow, } from "@/components/ui/table"; -import DialogModal from "@/components/Common/Dialog"; - import { PartialPatientModel } from "@/types/emr/newPatient"; interface Props { + open: boolean; + onOpenChange: (open: boolean) => void; patientList: Array; handleOk: (action: string) => void; - handleCancel: () => void; } const DuplicatePatientDialog = (props: Props) => { const { t } = useTranslation(); - const { patientList, handleOk, handleCancel } = props; + const { open, onOpenChange, patientList, handleOk } = props; const [action, setAction] = useState(""); return ( - - - - - {t("patient_records_found_description")}( - {patientList[0].phone_number}) - - - - - - - - {[`${t("patient_name")} / ID`, t("gender")].map( - (heading, i) => ( - {heading} - ), - )} - - - - {patientList.map((patient, i) => { - return ( - - - - {patient.name} - - - ID : {patient.id} - - - {patient.gender} - - ); - })} - - + + + + {t("patient_records_found")} + + + + + {t("patient_records_found_description")}( + {patientList[0].phone_number}) + - - - - - setAction(e.target.value)} - /> - {t("duplicate_patient_record_confirmation")} - + + + + + + {[`${t("patient_name")} / ID`, t("gender")].map( + (heading, i) => ( + {heading} + ), + )} + + + + {patientList.map((patient, i) => { + return ( + + + + {patient.name} + + + ID : {patient.id} + + + {patient.gender} + + ); + })} + + + + + + + setAction(e.target.value)} + /> + {t("duplicate_patient_record_confirmation")} + + - - - setAction(e.target.value)} - /> - {t("duplicate_patient_record_rejection")} - - + + + setAction(e.target.value)} + /> + {t("duplicate_patient_record_rejection")} + + - {t("duplicate_patient_record_birth_unknown")} + {t("duplicate_patient_record_birth_unknown")} + - - - - - {t("close")} - - handleOk(action)} - disabled={!action} - variant={"primary"} - > - - {t("continue")} - - - + + + onOpenChange(false)} + className="gap-1" + variant={"secondary"} + > + + {t("close")} + + handleOk(action)} + disabled={!action} + variant={"primary"} + > + + {t("continue")} + + + + + ); }; diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx index 547d1b209d6..195e4d700bf 100644 --- a/src/components/Facility/FacilityHome.tsx +++ b/src/components/Facility/FacilityHome.tsx @@ -7,14 +7,26 @@ import { } from "@radix-ui/react-tooltip"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation } from "@tanstack/react-query"; -import { Hospital, MapPin, MoreVertical, Settings } from "lucide-react"; +import { Edit2, Hospital, MapPin, MoreVertical, Settings } from "lucide-react"; import { navigate } from "raviger"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; +import { cn } from "@/lib/utils"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import { Button, buttonVariants } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { DropdownMenu, @@ -26,7 +38,6 @@ import { Markdown } from "@/components/ui/markdown"; import { Avatar } from "@/components/Common/Avatar"; import AvatarEditModal from "@/components/Common/AvatarEditModal"; -import ConfirmDialog from "@/components/Common/ConfirmDialog"; import ContactLink from "@/components/Common/ContactLink"; import Loading from "@/components/Common/Loading"; @@ -96,7 +107,6 @@ const renderGeoOrganizations = (geoOrg: Organization) => { export const FacilityHome = ({ facilityId }: Props) => { const { t } = useTranslation(); - const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const [editCoverImage, setEditCoverImage] = useState(false); const queryClient = useQueryClient(); @@ -106,13 +116,14 @@ export const FacilityHome = ({ facilityId }: Props) => { pathParams: { id: facilityId }, }), }); - const { mutate: deleteFacility } = useMutation({ + + const { mutate: deleteFacility, isPending: isDeleting } = useMutation({ mutationFn: mutate(routes.deleteFacility, { pathParams: { id: facilityId }, }), onSuccess: () => { toast.success( - t("entity_deleted_successfully", { name: facilityData?.name }), + t("facility_deleted_successfully", { name: facilityData?.name }), ); navigate("/facility"); }, @@ -184,26 +195,13 @@ export const FacilityHome = ({ facilityId }: Props) => { return ( - - {t("are_you_sure_want_to_delete", { name: facilityData?.name })} - - } - action="Delete" - variant="destructive" - show={openDeleteDialog} - onClose={() => setOpenDeleteDialog(false)} - onConfirm={() => deleteFacility()} - /> setEditCoverImage(false)} + onOpenChange={(open) => setEditCoverImage(open)} hint={coverImageHint} /> @@ -267,7 +265,7 @@ export const FacilityHome = ({ facilityId }: Props) => { className="cursor-pointer" onClick={() => setEditCoverImage(true)} > - + {t("edit_cover_photo")} )} @@ -276,7 +274,7 @@ export const FacilityHome = ({ facilityId }: Props) => { facilityId={facilityId} trigger={ { e.preventDefault(); }} @@ -286,16 +284,45 @@ export const FacilityHome = ({ facilityId }: Props) => { } /> - {/* TODO: get permissions from backend */} - {/* {hasPermissionToDeleteFacility && ( - setOpenDeleteDialog(true)} - > - - {t("delete_facility")} - - )} */} + + {/* TODO: add delete facility */} + {/* + e.preventDefault()} + > + + {t("delete_facility")} + + */} + + + + {t("delete_facility")} + + + {t("delete_facility_confirmation", { + name: facilityData?.name, + })} + + + + + {t("cancel")} + + deleteFacility()} + className={cn( + buttonVariants({ variant: "destructive" }), + )} + disabled={isDeleting} + > + {isDeleting ? t("deleting") : t("delete")} + + + + + ; export interface CommentModel { diff --git a/src/components/Files/CameraCaptureDialog.tsx b/src/components/Files/CameraCaptureDialog.tsx index 54b9debc21e..8ee66a313d6 100644 --- a/src/components/Files/CameraCaptureDialog.tsx +++ b/src/components/Files/CameraCaptureDialog.tsx @@ -6,21 +6,25 @@ import { toast } from "sonner"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; - -import DialogModal from "@/components/Common/Dialog"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import useBreakpoints from "@/hooks/useBreakpoints"; export interface CameraCaptureDialogProps { - show: boolean; - onHide: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; onCapture: (file: File, fileName: string) => void; onResetCapture: () => void; setPreview?: (isPreview: boolean) => void; } export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { - const { show, onHide, onCapture, onResetCapture, setPreview } = props; + const { open, onOpenChange, onCapture, onResetCapture, setPreview } = props; const isLaptopScreen = useBreakpoints({ lg: true, default: false }); const [cameraFacingMode, setCameraFacingMode] = useState( @@ -34,8 +38,9 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { height: { ideal: 2160 }, facingMode: cameraFacingMode, }; + useEffect(() => { - if (!show) return; + if (!open) return; let stream: MediaStream | null = null; navigator.mediaDevices @@ -45,7 +50,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }) .catch(() => { toast.warning(t("camera_permission_denied")); - onHide(); + onOpenChange(false); }); return () => { @@ -55,7 +60,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }); } }; - }, [show, cameraFacingMode, onHide]); + }, [open, cameraFacingMode, onOpenChange]); const handleSwitchCamera = useCallback(async () => { const devices = await navigator.mediaDevices.enumerateDevices(); @@ -87,130 +92,62 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }; return ( - - - - - - {t("camera")} - - - } - className="max-w-2xl" - onClose={onHide} - > - - {!previewImage ? ( - - - - ) : ( - - - - )} - + + + + + + + + + + + {t("camera")} + + + + - {/* buttons for mobile screens */} - - - {!previewImage ? ( - - {t("switch")} - - ) : ( - <>> - )} - {!previewImage ? ( - <> - - { - captureImage(); - setPreview?.(true); - }} - className="m-2" - > - {t("capture")} - - - > + + + ) : ( - <> - - { - setPreviewImage(null); - onResetCapture(); - setPreview?.(false); - }} - className="m-2" - > - {t("retake")} - - { - setPreviewImage(null); - onHide(); - setPreview?.(false); - }} - className="m-2" - > - {t("submit")} - - - > + + + )} - - { - setPreviewImage(null); - onResetCapture(); - onHide(); - }} - className="m-2" - > - {t("close")} - - - - {/* buttons for laptop screens */} - - - - - {`${t("switch")} ${t("camera")}`} - - - + {/* buttons for mobile and tablet screens */} + + + {!previewImage ? ( + + {t("switch")} + + ) : ( + <>> + )} + {!previewImage ? ( <> @@ -221,8 +158,8 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { captureImage(); setPreview?.(true); }} + className="m-2" > - {t("capture")} @@ -237,16 +174,18 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { onResetCapture(); setPreview?.(false); }} + className="m-2" > {t("retake")} { - onHide(); setPreviewImage(null); + onOpenChange(false); setPreview?.(false); }} + className="m-2" > {t("submit")} @@ -254,20 +193,82 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { > )} - - { - setPreviewImage(null); - onResetCapture(); - onHide(); - setPreview?.(false); - }} - > - {`${t("close")} ${t("camera")}`} - + + { + setPreviewImage(null); + onResetCapture(); + onOpenChange(false); + }} + className="m-2" + > + {t("close")} + + + + + {/* buttons for laptop screens */} + + + + {!previewImage ? ( + <> + + { + captureImage(); + setPreview?.(true); + }} + > + + {t("capture")} + + + > + ) : ( + <> + + { + setPreviewImage(null); + onResetCapture(); + setPreview?.(false); + }} + > + {t("retake")} + + { + onOpenChange(false); + setPreviewImage(null); + setPreview?.(false); + }} + > + {t("submit")} + + + > + )} + + + { + setPreviewImage(null); + onResetCapture(); + onOpenChange(false); + setPreview?.(false); + }} + > + {`${t("close")} ${t("camera")}`} + + - - + + ); } diff --git a/src/components/Files/FileBlock.tsx b/src/components/Files/FileBlock.tsx deleted file mode 100644 index 1b9cb9f8959..00000000000 --- a/src/components/Files/FileBlock.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import dayjs from "dayjs"; -import { t } from "i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; - -import { FileUploadModel } from "@/components/Patient/models"; - -import { FileManagerResult } from "@/hooks/useFileManager"; - -import { FILE_EXTENSIONS } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export interface FileBlockProps { - file: FileUploadModel; - fileManager: FileManagerResult; - associating_id: string; - editable: boolean; - archivable?: boolean; -} - -export default function FileBlock(props: FileBlockProps) { - const { - file, - fileManager, - associating_id, - editable = false, - archivable = false, - } = props; - - const filetype = fileManager.getFileType(file); - - const { data: fileData } = useQuery({ - queryKey: ["file", { id: file.id, type: fileManager.type, associating_id }], - queryFn: query(routes.retrieveUpload, { - queryParams: { file_type: fileManager.type, associating_id }, - pathParams: { id: file.id || "" }, - }), - enabled: filetype === "AUDIO" && !file.is_archived, - }); - - const icons: Record = { - AUDIO: "l-volume", - IMAGE: "l-image", - PRESENTATION: "l-presentation-play", - VIDEO: "l-video", - UNKNOWN: "l-file-medical", - DOCUMENT: "l-file-medical", - }; - - const archived = file.is_archived; - - return ( - - - - - - - - {file.name} - {file.extension} {file.is_archived && "(Archived)"} - - - {dayjs( - file.is_archived ? file.archived_datetime : file.created_date, - ).format("DD MMM YYYY, hh:mm A")}{" "} - by{" "} - {file.is_archived - ? file.archived_by?.username - : file.uploaded_by?.username} - - - - - {filetype === "AUDIO" && !file.is_archived && ( - - - - )} - {!file.is_archived && - (fileManager.isPreviewable(file) ? ( - fileManager.viewFile(file, associating_id)} - className="w-full md:w-auto" - > - - {t("view")} - - ) : ( - fileManager.downloadFile(file, associating_id)} - className="w-full md:w-auto" - > - - {t("download")} - - ))} - - {!file.is_archived && editable && ( - fileManager.editFile(file, associating_id)} - className="flex-1 md:flex-auto" - > - - {t("rename")} - - )} - {(file.is_archived || editable) && archivable && ( - fileManager.archiveFile(file, associating_id)} - className="flex-1 md:flex-auto" - > - - {file.is_archived ? t("more_info") : t("archive")} - - )} - - - - ); -} diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx deleted file mode 100644 index 54881c80c0a..00000000000 --- a/src/components/Files/FileUpload.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { Loader2 } from "lucide-react"; -import { ReactNode, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; - -import Pagination from "@/components/Common/Pagination"; -import Tabs from "@/components/Common/Tabs"; -import FileBlock from "@/components/Files/FileBlock"; -import { FileUploadModel } from "@/components/Patient/models"; - -import useAuthUser from "@/hooks/useAuthUser"; -import useFileManager from "@/hooks/useFileManager"; -import useFileUpload from "@/hooks/useFileUpload"; - -import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; - -export const LinearProgressWithLabel = (props: { value: number }) => { - return ( - - - - - - - - {`${Math.round(props.value)}%`} - - - ); -}; - -interface FileUploadProps { - type: string; - patientId?: string; - encounterId?: string; - consentId?: string; - allowAudio?: boolean; - sampleId?: string; - claimId?: string; - className?: string; - hideUpload?: boolean; -} - -export interface ModalDetails { - name?: string; - id?: string; - reason?: string; - userArchived?: string; - archiveTime?: string; - associatedId?: string; -} - -export interface StateInterface { - open: boolean; - isImage: boolean; - name: string; - extension: string; - zoom: number; - isZoomInDisabled: boolean; - isZoomOutDisabled: boolean; - rotation: number; - id?: string; - associating_id?: string; -} - -export const FileUpload = (props: FileUploadProps) => { - const { t } = useTranslation(); - const { - encounterId, - patientId, - consentId, - type, - sampleId, - claimId, - allowAudio, - hideUpload, - } = props; - const [currentPage, setCurrentPage] = useState(1); - const [offset, setOffset] = useState(0); - const [tab, setTab] = useState("UNARCHIVED"); - const authUser = useAuthUser(); - const queryClient = useQueryClient(); - - const handlePagination = (page: number, limit: number) => { - const offset = (page - 1) * limit; - setCurrentPage(page); - setOffset(offset); - }; - - const UPLOAD_HEADING: { [index: string]: string } = { - PATIENT: t("upload_headings__patient"), - CONSULTATION: t("upload_headings__consultation"), - SAMPLE_MANAGEMENT: t("upload_headings__sample_report"), - CLAIM: t("upload_headings__supporting_info"), - }; - const VIEW_HEADING: { [index: string]: string } = { - PATIENT: t("file_list_headings__patient"), - CONSULTATION: t("file_list_headings__consultation"), - SAMPLE_MANAGEMENT: t("file_list_headings__sample_report"), - CLAIM: t("file_list_headings__supporting_info"), - }; - - const associatedId = - { - PATIENT: patientId, - CONSENT_RECORD: consentId, - ENCOUNTER: encounterId, - SAMPLE_MANAGEMENT: sampleId, - CLAIM: claimId, - }[type] || ""; - - const refetchAll = () => { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "active", type, associatedId], - }); - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "archived", type, associatedId], - }); - if (type === "consultation") { - queryClient.invalidateQueries({ - queryKey: ["viewUpload", "discharge_summary", associatedId], - }); - } - }; - - const { data: activeFiles, isLoading: activeFilesLoading } = useQuery({ - queryKey: ["viewUpload", "active", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: archivedFiles, isLoading: archivedFilesLoading } = useQuery({ - queryKey: ["viewUpload", "archived", type, associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: type, - associating_id: associatedId, - is_archived: true, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - }, - }), - }); - - const { data: dischargeSummary, isLoading: dischargeSummaryLoading } = - useQuery({ - queryKey: ["viewUpload", "discharge_summary", associatedId, offset], - queryFn: query(routes.viewUpload, { - queryParams: { - file_type: "discharge_summary", - associating_id: associatedId, - is_archived: false, - limit: RESULTS_PER_PAGE_LIMIT, - offset: offset, - silent: true, - }, - }), - enabled: type === "consultation", - }); - - const queries = { - UNARCHIVED: { data: activeFiles, isLoading: activeFilesLoading }, - ARCHIVED: { data: archivedFiles, isLoading: archivedFilesLoading }, - DISCHARGE_SUMMARY: { - data: dischargeSummary, - isLoading: dischargeSummaryLoading, - }, - }; - - const loading = Object.values(queries).some((q) => q.isLoading); - const fileQuery = queries[tab as keyof typeof queries]; - - const tabs = [ - { text: "Active Files", value: "UNARCHIVED" }, - { text: "Archived Files", value: "ARCHIVED" }, - ...(dischargeSummary?.results?.length - ? [ - { - text: "Discharge Summary", - value: "DISCHARGE_SUMMARY", - }, - ] - : []), - ]; - - const fileUpload = useFileUpload({ - type, - allowedExtensions: [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "mp4", - "mov", - "avi", - "wmv", - "mp3", - "wav", - "ogg", - "txt", - "csv", - "rtf", - "doc", - "odt", - "pdf", - "xls", - "xlsx", - "ods", - "pdf", - ], - allowNameFallback: false, - onUpload: refetchAll, - }); - - const fileManager = useFileManager({ - type, - onArchive: refetchAll, - onEdit: refetchAll, - uploadedFiles: - fileQuery?.data?.results - .slice() - .reverse() - .map((file) => ({ - ...file, - associating_id: associatedId, - })) || [], - }); - const dischargeSummaryFileManager = useFileManager({ - type: "DISCHARGE_SUMMARY", - onArchive: refetchAll, - onEdit: refetchAll, - }); - - const uploadButtons: { - name: string; - icon: IconName; - onClick?: () => void; - children?: ReactNode; - show?: boolean; - id: string; - }[] = [ - { - name: t("choose_file"), - icon: "l-file-upload-alt", - children: , - id: "upload-file", - }, - { - name: t("open_camera"), - icon: "l-camera", - onClick: fileUpload.handleCameraCapture, - id: "open-webcam", - }, - { - name: t("record"), - icon: "l-microphone", - onClick: fileUpload.handleAudioCapture, - show: allowAudio, - id: "record-audio", - }, - ]; - return ( - - {fileUpload.Dialogues} - {fileManager.Dialogues} - {dischargeSummaryFileManager.Dialogues} - {!hideUpload && ( - <> - {UPLOAD_HEADING[type]} - {fileUpload.files[0] ? ( - - - - - {fileUpload.files[0].name} - - - - - - {t("enter_file_name")} - fileUpload.setFileName(e.target.value)} - /> - {fileUpload.error && ( - {fileUpload.error} - )} - - fileUpload.handleFileUpload(associatedId)} - disabled={fileUpload.uploading} // Disable the button when loading - className={`w-full ${fileUpload.uploading ? "opacity-50" : ""}`} - id="upload_file_button" - > - {fileUpload.uploading ? ( - - ) : ( - - )} - {t("upload")} - - - - {t("discard")} - - - {!!fileUpload.progress && ( - - )} - - ) : ( - - {uploadButtons - .filter((b) => b.show !== false) - .map((button, i) => ( - - - {button.name} - {button.children} - - ))} - - )} - > - )} - - {VIEW_HEADING[type]} - setTab(v.toString())} - currentTab={tab} - /> - - - {!(fileQuery?.data?.results || []).length && loading && ( - - )} - {fileQuery?.data?.results.map((item: FileUploadModel) => ( - - ))} - {!(fileQuery?.data?.results || []).length && ( - - - {t("no_files_found", { type: tab.toLowerCase() })} - - - )} - - {(fileQuery?.data?.count ?? 0) > RESULTS_PER_PAGE_LIMIT && ( - - - - )} - - ); -}; diff --git a/src/components/Form/FieldValidators.tsx b/src/components/Form/FieldValidators.tsx deleted file mode 100644 index 989a7aa6688..00000000000 --- a/src/components/Form/FieldValidators.tsx +++ /dev/null @@ -1 +0,0 @@ -export type FieldError = string | undefined; diff --git a/src/components/Form/FormFields/Autocomplete.tsx b/src/components/Form/FormFields/Autocomplete.tsx deleted file mode 100644 index 7e00c89fc7c..00000000000 --- a/src/components/Form/FormFields/Autocomplete.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { - Combobox, - ComboboxButton, - ComboboxInput, - ComboboxOption, - ComboboxOptions, -} from "@headlessui/react"; -import { ReactNode, useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { DropdownTransition } from "@/components/Common/HelperComponents"; -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type AutocompleteProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: string; - optionLabel: OptionCallback; - optionIcon?: OptionCallback; - optionImage?: OptionCallback; - optionValue?: OptionCallback; - optionDescription?: OptionCallback; - optionDisabled?: OptionCallback; - className?: string; - minQueryLength?: number; - onQuery?: (query: string) => void; - requiredError?: boolean; - isLoading?: boolean; - allowRawInput?: boolean; - error?: string; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `AutocompleteFormField` instead as - * its API is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -export const Autocomplete = (props: AutocompleteProps) => { - const { t } = useTranslation(); - const [query, setQuery] = useState(""); // Ensure lower case - - useEffect(() => { - props.onQuery?.(query); - }, [query]); - - const mappedOptions = props.options.map((option) => { - const label = props.optionLabel(option); - const description = props.optionDescription?.(option); - return { - label, - description, - search: label.toLowerCase(), - icon: props.optionIcon?.(option), - image: props.optionImage?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const getOptions = () => { - if (!query) return mappedOptions; - - const knownOption = mappedOptions.find( - (o) => o.value == props.value || o.label == props.value, - ); - - if (knownOption) return mappedOptions; - return [ - { - label: query, - description: undefined, - search: query.toLowerCase(), - icon: , - image: undefined, - value: query, - disabled: undefined, - }, - ...mappedOptions, - ]; - }; - - const options = props.allowRawInput ? getOptions() : mappedOptions; - - const value = options.find((o) => props.value == o.value); - - const filteredOptions = - props.onQuery === undefined - ? options.filter((o) => o.search.includes(query)) - : options; - - return ( - - props.onChange(selection?.value)} - > - - - value?.label || ""} - onChange={(event) => setQuery(event.target.value.toLowerCase())} - onBlur={() => value && setQuery("")} - autoComplete="off" - /> - {!props.disabled && ( - - - {value?.icon} - - {value && !props.isLoading && !props.required && ( - - { - e.preventDefault(); - props.onChange(undefined); - }} - /> - - {t("clear_selection")} - - - )} - - {props.isLoading ? ( - - ) : ( - - )} - - - )} - - - - - {props.minQueryLength && query.length < props.minQueryLength ? ( - - {`Please enter at least ${props.minQueryLength} characters to search`} - - ) : filteredOptions.length === 0 ? ( - - No options found - - ) : ( - filteredOptions.map((option, index) => ( - - {({ focus }) => ( - - {option?.image} - - - {option.label} - {option.icon} - - {option.description && ( - - {option.description} - - )} - - - )} - - )) - )} - - - - - - ); -}; diff --git a/src/components/Form/FormFields/FormField.tsx b/src/components/Form/FormFields/FormField.tsx deleted file mode 100644 index f3ad0559e44..00000000000 --- a/src/components/Form/FormFields/FormField.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { FieldError } from "@/components/Form/FieldValidators"; -import { FormFieldBaseProps } from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type LabelProps = { - id?: string | undefined; - required?: boolean; - htmlFor?: string; - children: React.ReactNode; - className?: string | undefined; - noPadding?: boolean; -}; - -export const FieldLabel = (props: LabelProps) => { - return ( - - {props.children} - {props.required && {" *"}} - - ); -}; - -type ErrorProps = { - error: FieldError; - className?: string | undefined; -}; - -export const FieldErrorText = (props: ErrorProps) => { - return ( - - {props.error} - - ); -}; - -/** - * @deprecated use shadcn/ui's solution for form fields instead along with react-hook-form - */ -const FormField = ({ - field, - ...props -}: { - field?: FormFieldBaseProps; - children: React.ReactNode; -}) => { - return ( - - - {field?.label && ( - - {field?.label} - - )} - {field?.labelSuffix && ( - {field?.labelSuffix} - )} - - {props.children} - - - ); -}; - -export default FormField; diff --git a/src/components/Form/FormFields/RadioFormField.tsx b/src/components/Form/FormFields/RadioFormField.tsx deleted file mode 100644 index ca205fcccad..00000000000 --- a/src/components/Form/FormFields/RadioFormField.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { ChangeEventHandler, ReactNode } from "react"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -type Props = FormFieldBaseProps & { - options: readonly T[]; - optionLabel: (option: T) => React.ReactNode; - optionValue: (option: T) => V; - containerClassName?: string; - unselectLabel?: string; - layout?: "vertical" | "horizontal" | "grid" | "auto"; -}; - -/** - * @deprecated use shadcn/ui's radio-group instead - */ -const RadioFormField = (props: Props) => { - const field = useFormFieldPropsResolver(props); - return ( - - - {props.unselectLabel && ( - - field.handleChange(null)} - /> - {props.unselectLabel} - - )} - {props.options.map((option) => { - const value = props.optionValue(option); - return ( - field.handleChange(e.target.value as V)} - /> - ); - })} - - - ); -}; - -export default RadioFormField; - -export const RadioInput = (props: { - label?: ReactNode; - id?: string; - name?: string; - value?: string; - checked?: boolean; - onChange?: ChangeEventHandler; -}) => { - return ( - - props.onChange?.(e)} - /> - {props.label} - - ); -}; diff --git a/src/components/Form/FormFields/SelectFormField.tsx b/src/components/Form/FormFields/SelectFormField.tsx deleted file mode 100644 index 6b95550299f..00000000000 --- a/src/components/Form/FormFields/SelectFormField.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; -import SelectMenuV2 from "@/components/Form/SelectMenuV2"; - -type OptionCallback = (option: T) => R; - -type SelectFormFieldProps = FormFieldBaseProps & { - placeholder?: React.ReactNode; - options: readonly T[]; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - inputClassName?: string; -}; - -/** - * @deprecated use shadcn/ui's select instead - */ -export const SelectFormField = (props: SelectFormFieldProps) => { - const field = useFormFieldPropsResolver(props); - return ( - - field.handleChange(value)} - position={props.position} - placeholder={props.placeholder} - optionLabel={props.optionLabel} - inputClassName={props.inputClassName} - optionSelectedLabel={props.optionSelectedLabel} - optionDescription={props.optionDescription} - optionIcon={props.optionIcon} - optionValue={props.optionValue} - optionDisabled={props.optionDisabled} - requiredError={field.error ? props.required : false} - /> - - ); -}; diff --git a/src/components/Form/FormFields/TextFormField.tsx b/src/components/Form/FormFields/TextFormField.tsx deleted file mode 100644 index 18a4de18341..00000000000 --- a/src/components/Form/FormFields/TextFormField.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import { - DetailedHTMLProps, - InputHTMLAttributes, - forwardRef, - useState, -} from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; - -import { classNames } from "@/Utils/utils"; - -export type TextFormFieldProps = FormFieldBaseProps & - Omit< - DetailedHTMLProps, HTMLInputElement>, - "onChange" - > & { - inputClassName?: string | undefined; - removeDefaultClasses?: true | undefined; - leading?: React.ReactNode | undefined; - trailing?: React.ReactNode | undefined; - leadingFocused?: React.ReactNode | undefined; - trailingFocused?: React.ReactNode | undefined; - trailingPadding?: string | undefined; - leadingPadding?: string | undefined; - suggestions?: string[]; - clearable?: boolean | undefined; - }; - -/** - * @deprecated use shadcn/ui's Input instead - */ -const TextFormField = forwardRef((props: TextFormFieldProps, ref) => { - const field = useFormFieldPropsResolver(props); - const { leading, trailing } = props; - const leadingFocused = props.leadingFocused || props.leading; - const trailingFocused = props.trailingFocused || props.trailing; - const hasLeading = !!(leading || leadingFocused); - const hasTrailing = !!(trailing || trailingFocused); - const hasIcon = hasLeading || hasTrailing; - const [showPassword, setShowPassword] = useState(false); - - const getPasswordFieldType = () => { - return showPassword ? "text" : "password"; - }; - - const minError = - typeof props.min !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.min}`) > parseFloat(`${field.value}`) - ? `Value can not be smaller than ${props.min}` - : undefined; - const maxError = - typeof props.max !== "undefined" && - typeof field.value !== "undefined" && - parseFloat(`${props.max}`) < parseFloat(`${field.value}`) - ? `Value can not be greater than ${props.max}` - : undefined; - - const labelSuffixWithThreshold = ( - - {field.labelSuffix} - - ); - - let child = ( - - } - id={field.id} - className={classNames( - "cui-input-base peer", - hasLeading && (props.leadingPadding || "pl-10"), - hasTrailing && (props.trailingPadding || "pr-10"), - field.error && "border-danger-500", - props.inputClassName, - )} - disabled={field.disabled} - type={props.type === "password" ? getPasswordFieldType() : props.type} - name={field.name} - value={field.value} - required={field.required} - onChange={(e) => field.handleChange(e.target.value)} - /> - {props.clearable && field.value && ( - field.handleChange("")} - aria-label="Clear input" - > - - - )} - - ); - - if (props.type === "password") { - child = ( - - {child} - setShowPassword(!showPassword)} - > - - - - ); - } - - if (hasIcon) { - const _leading = - leading === leadingFocused ? ( - - {leading} - - ) : ( - <> - - {leading} - - - {leadingFocused} - - > - ); - const _trailing = - trailing === trailingFocused ? ( - - {trailing} - - ) : ( - <> - - {trailing} - - - {trailingFocused} - - > - ); - - child = ( - - {(leading || leadingFocused) && _leading} - {child} - {(trailing || trailingFocused) && _trailing} - - ); - } - - if ( - props.suggestions?.length && - !props.suggestions.includes(`${field.value}`) - ) { - child = ( - - {child} - - {props.suggestions.map((suggestion) => ( - field.handleChange(suggestion)} - > - {suggestion} - - ))} - - - ); - } - - return ( - - {child} - - ); -}); -TextFormField.displayName = "TextFormField"; - -export default TextFormField; diff --git a/src/components/Form/FormFields/Utils.ts b/src/components/Form/FormFields/Utils.ts deleted file mode 100644 index 1e88bcbd6a0..00000000000 --- a/src/components/Form/FormFields/Utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { FocusEvent } from "react"; - -import { FieldError } from "@/components/Form/FieldValidators"; - -export type FieldChangeEvent = { name: string; value: T }; -export type FieldChangeEventHandler = (event: FieldChangeEvent) => void; - -/** - * The base props for a form field. - * - * If a form context is provided, the field will be registered with the form - * and the onChange, value, and error props will be ignored. - * - * If a form context is not provided, the field will be treated as a standalone - * field. - * - * @template T The type of the field value. - * @template Form The type of the form details. - */ -export type FormFieldBaseProps = { - label?: React.ReactNode; - labelSuffix?: React.ReactNode; - disabled?: boolean; - className?: string; - required?: boolean; - labelClassName?: string; - errorClassName?: string; - name: string; - validate?: undefined; - id?: string; - onChange: FieldChangeEventHandler; - value?: T; - error?: FieldError; - onFocus?: (event: FocusEvent) => void; - onBlur?: (event: FocusEvent) => void; -}; - -/** - * Resolves the props for a form field. - * If a form context is provided, the field will be registered with the form. - * Otherwise, the field will be treated as a standalone field. - * - * @param props The props for the field. - * @returns The resolved props along with a handleChange function. - */ -export const useFormFieldPropsResolver = (props: FormFieldBaseProps) => { - const handleChange = (value: T) => - props.onChange({ name: props.name, value }); - - return { - ...props, - id: props.id ?? props.name, - name: props.name, - onChange: props.onChange, - value: props.value, - error: props.error, - handleChange, - }; -}; diff --git a/src/components/Form/MultiSelectMenuV2.tsx b/src/components/Form/MultiSelectMenuV2.tsx deleted file mode 100644 index b67da3656f4..00000000000 --- a/src/components/Form/MultiSelectMenuV2.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { classNames } from "@/Utils/utils"; - -interface MultiSelectOptionChipProps { - label: ReactNode; - onRemove?: () => void; -} - -export const MultiSelectOptionChip = ({ - label, - onRemove, -}: MultiSelectOptionChipProps) => { - return ( - - {label} - {onRemove && ( - { - e.stopPropagation(); - onRemove(); - }} - > - - - )} - - ); -}; - -interface OptionRenderPropArg { - focus: boolean; - selected: boolean; - disabled: boolean; -} - -export const dropdownOptionClassNames = ({ - focus, - selected, - disabled, -}: OptionRenderPropArg) => { - return classNames( - "group/option relative w-full cursor-default select-none p-4 text-sm transition-colors duration-75 ease-in-out", - !disabled && focus && "bg-primary-500 text-white", - !disabled && !focus && selected && "text-primary-500", - !disabled && !focus && !selected && "text-secondary-900", - disabled && "cursor-not-allowed text-secondary-600", - selected ? "font-semibold" : "font-normal", - ); -}; diff --git a/src/components/Form/SelectMenuV2.tsx b/src/components/Form/SelectMenuV2.tsx deleted file mode 100644 index a13d03607c6..00000000000 --- a/src/components/Form/SelectMenuV2.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { - Label, - Listbox, - ListboxButton, - ListboxOption, - ListboxOptions, -} from "@headlessui/react"; -import { ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; - -import { classNames } from "@/Utils/utils"; - -type OptionCallback = (option: T) => R; - -type SelectMenuProps = { - id?: string; - options: readonly T[]; - disabled?: boolean | undefined; - value: V | undefined; - placeholder?: ReactNode; - position?: "above" | "below"; - optionLabel: OptionCallback; - optionSelectedLabel?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionValue?: OptionCallback; - optionDisabled?: OptionCallback; - showIconWhenSelected?: boolean; - showChevronIcon?: boolean; - className?: string; - inputClassName?: string; - requiredError?: boolean; - onFocus?: () => void; - onBlur?: () => void; -} & ( - | { - required?: false; - onChange: OptionCallback; - } - | { - required: true; - onChange: OptionCallback; - } -); - -/** - * Avoid using this component directly. Use `SelectFormField` instead as its API - * is easier to use and compliant with `FormField` based components. - * - * Use this only when you want to hack into the design and get more - * customizability. - */ -const SelectMenuV2 = (props: SelectMenuProps) => { - const valueOptions = props.options.map((option) => { - const label = props.optionLabel(option); - return { - label, - selectedLabel: props.optionSelectedLabel - ? props.optionSelectedLabel(option) - : label, - description: props.optionDescription?.(option), - icon: props.optionIcon?.(option), - value: props.optionValue ? props.optionValue(option) : option, - disabled: props.optionDisabled?.(option), - }; - }); - - const showChevronIcon = props.showChevronIcon ?? true; - - const placeholder = - valueOptions?.length > 0 ? (props.placeholder ?? "Select") : "No options"; - const defaultOption = { - label: placeholder, - selectedLabel: ( - {placeholder} - ), - description: undefined, - icon: undefined, - value: undefined, - disabled: undefined, - }; - - const options = props.required - ? valueOptions - : [defaultOption, ...valueOptions]; - - const value = options.find((o) => props.value == o.value) ?? defaultOption; - - return ( - - props.onChange(selection.value)} - > - <> - {props.placeholder} - - - - - {props.showIconWhenSelected && value?.icon && ( - - {value.icon} - - )} - - {value.selectedLabel} - - - {showChevronIcon && ( - - )} - - - - - {options.map((option, index) => ( - - {({ focus, selected }) => ( - - - {option.label} - {props.optionIcon - ? option.icon - : selected && ( - - )} - - {option.description && ( - - {option.description} - - )} - - )} - - ))} - - - - > - - - ); -}; - -export default SelectMenuV2; diff --git a/src/components/Location/LocationSearch.tsx b/src/components/Location/LocationSearch.tsx index bd21bb26273..a27d8e8d1ec 100644 --- a/src/components/Location/LocationSearch.tsx +++ b/src/components/Location/LocationSearch.tsx @@ -20,7 +20,7 @@ import locationApi from "@/types/location/locationApi"; interface LocationSearchProps { facilityId: string; - mode?: "kind" | "location"; + mode?: "kind" | "instance"; onSelect: (location: LocationList) => void; disabled?: boolean; value?: LocationList | null; @@ -44,7 +44,6 @@ export function LocationSearch({ }), enabled: facilityId !== "preview", }); - return ( @@ -57,7 +56,7 @@ export function LocationSearch({ - + = ({ const { data: administrations, refetch: refetchAdministrations } = useQuery({ queryKey: ["medication_administrations", patientId, visibleSlots], - queryFn: query(routes.medicationAdministration.list, { + queryFn: query(medicationAdministrationApi.list, { pathParams: { patientId }, queryParams: { encounter: encounterId, diff --git a/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx b/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx index 3668e4f84de..d8e711e5f2d 100644 --- a/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx +++ b/src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx @@ -50,12 +50,9 @@ export const MedicineAdminDialog = ({ }, [initialRequest]); const { mutate: upsertAdministration, isPending } = useMutation({ - mutationFn: mutate( - medicationAdministrationApi.upsertMedicationAdministration, - { - pathParams: { patientId: patientId }, - }, - ), + mutationFn: mutate(medicationAdministrationApi.upsert, { + pathParams: { patientId: patientId }, + }), onSuccess: () => { onOpenChange(false); toast.success(t("medication_administration_saved")); diff --git a/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx b/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx index a2f7ee23111..8b22e738b59 100644 --- a/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx +++ b/src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx @@ -122,12 +122,9 @@ export function MedicineAdminSheet({ const formRef = useRef(null); const { mutate: upsertAdministrations, isPending } = useMutation({ - mutationFn: mutate( - medicationAdministrationApi.upsertMedicationAdministration, - { - pathParams: { patientId }, - }, - ), + mutationFn: mutate(medicationAdministrationApi.upsert, { + pathParams: { patientId }, + }), onSuccess: () => { toast.success(t("medication_administration_saved")); handleClose(); diff --git a/src/components/Patient/FileUploadPage.tsx b/src/components/Patient/FileUploadPage.tsx deleted file mode 100644 index 83bce32954c..00000000000 --- a/src/components/Patient/FileUploadPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useTranslation } from "react-i18next"; - -import Page from "@/components/Common/Page"; -import { FileUpload } from "@/components/Files/FileUpload"; - -export default function FileUploadPage(props: { - facilityId: string; - patientId: string; - encounterId?: string; - type: "encounter" | "patient"; -}) { - const { patientId, encounterId, type } = props; - const { t } = useTranslation(); - - return ( - - - - ); -} diff --git a/src/components/Patient/MedicationStatementList.tsx b/src/components/Patient/MedicationStatementList.tsx index c9ea302c21a..112c42414a4 100644 --- a/src/components/Patient/MedicationStatementList.tsx +++ b/src/components/Patient/MedicationStatementList.tsx @@ -24,13 +24,13 @@ import { import { Avatar } from "@/components/Common/Avatar"; -import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; import { formatDateTime, formatName } from "@/Utils/utils"; import { MEDICATION_STATEMENT_STATUS_STYLES, MedicationStatementRead, } from "@/types/emr/medicationStatement"; +import medicationStatementApi from "@/types/emr/medicationStatement/medicationStatementApi"; interface MedicationStatementListProps { patientId: string; @@ -129,7 +129,7 @@ export function MedicationStatementList({ const { data: medications, isLoading } = useQuery({ queryKey: ["medication_statements", patientId], - queryFn: query(routes.medicationStatement.list, { + queryFn: query(medicationStatementApi.list, { pathParams: { patientId }, }), }); diff --git a/src/components/Patient/PatientHome.tsx b/src/components/Patient/PatientHome.tsx index 0807023978a..69df69c4143 100644 --- a/src/components/Patient/PatientHome.tsx +++ b/src/components/Patient/PatientHome.tsx @@ -247,30 +247,6 @@ export const PatientHome = (props: { - - {/* setOpenAssignVolunteerDialog(false)} - description={ - - setAssignedVolunteer(user.value)} - userType={"Volunteer"} - name={"assign_volunteer"} - error={errors.assignedVolunteer} - /> - - } - action={ - assignedVolunteer || !patientData.assigned_to - ? t("assign") - : t("unassign") - } - onConfirm={handleAssignedVolunteer} - /> */} ); }; diff --git a/src/components/Patient/PatientInfoCard.tsx b/src/components/Patient/PatientInfoCard.tsx index d7bfb9d01d5..955de6a9ef6 100644 --- a/src/components/Patient/PatientInfoCard.tsx +++ b/src/components/Patient/PatientInfoCard.tsx @@ -481,6 +481,11 @@ export default function PatientInfoCard(props: PatientInfoCardProps) { + + {t("cancel")} diff --git a/src/components/Patient/PatientRegistration.tsx b/src/components/Patient/PatientRegistration.tsx index 8557b4fab89..1c1113081ef 100644 --- a/src/components/Patient/PatientRegistration.tsx +++ b/src/components/Patient/PatientRegistration.tsx @@ -42,11 +42,7 @@ import DuplicatePatientDialog from "@/components/Facility/DuplicatePatientDialog import useAppHistory from "@/hooks/useAppHistory"; -import { - BLOOD_GROUP_CHOICES, // DOMESTIC_HEALTHCARE_SUPPORT_CHOICES, - GENDER_TYPES, // OCCUPATION_TYPES, - //RATION_CARD_CATEGORY, // SOCIOECONOMIC_STATUS_CHOICES , -} from "@/common/constants"; +import { BLOOD_GROUP_CHOICES, GENDER_TYPES } from "@/common/constants"; import { GENDERS } from "@/common/constants"; import countryList from "@/common/static/countries.json"; @@ -747,10 +743,13 @@ export default function PatientRegistration( {showDuplicate && ( { - handleDialogClose("close"); + onOpenChange={(open) => { + if (!open) { + handleDialogClose("close"); + } }} /> )} diff --git a/src/components/Patient/models.tsx b/src/components/Patient/models.tsx index 14f048191b0..abc717e6120 100644 --- a/src/components/Patient/models.tsx +++ b/src/components/Patient/models.tsx @@ -1,46 +1,11 @@ -import { PatientCategory } from "@/components/Facility/models"; import { UserBareMinimum } from "@/components/Users/models"; import { - APPETITE_CHOICES, - BLADDER_DRAINAGE_CHOICES, - BLADDER_ISSUE_CHOICES, - BOWEL_ISSUE_CHOICES, - CONSCIOUSNESS_LEVEL, DOMESTIC_HEALTHCARE_SUPPORT_CHOICES, - HEARTBEAT_RHYTHM_CHOICES, - HumanBodyRegion, - INSULIN_INTAKE_FREQUENCY_OPTIONS, - LIMB_RESPONSE_OPTIONS, - NURSING_CARE_PROCEDURES, - NUTRITION_ROUTE_CHOICES, OCCUPATION_TYPES, - ORAL_ISSUE_CHOICES, - OXYGEN_MODALITY_OPTIONS, - PressureSoreExudateAmountOptions, - PressureSoreTissueTypeOptions, - RESPIRATORY_SUPPORT, - SLEEP_CHOICES, SOCIOECONOMIC_STATUS_CHOICES, - URINATION_FREQUENCY_CHOICES, - VENTILATOR_MODE_OPTIONS, } from "@/common/constants"; -export interface FlowModel { - id?: number; - status?: string; - created_date?: string; - modified_date?: string; - deleted?: boolean; - notes?: string; - patient_sample?: number; - created_by?: number; -} - -export interface DischargeSummaryModel { - email?: string; -} - export interface AssignedToObjectModel { first_name: string; last_name: string; @@ -56,138 +21,6 @@ export interface PatientMeta { domestic_healthcare_support?: (typeof DOMESTIC_HEALTHCARE_SUPPORT_CHOICES)[number]; } -export const DailyRoundTypes = [ - "NORMAL", - "COMMUNITY_NURSES_LOG", - "DOCTORS_LOG", - "VENTILATOR", - "AUTOMATED", - "TELEMEDICINE", -] as const; - -export type BloodPressure = { - systolic?: number; - diastolic?: number; -}; - -export interface IPainScale { - description: string; - region: HumanBodyRegion; - scale: number; -} - -export type NameQuantity = { name: string; quantity: number }; - -export type IPressureSore = { - region: HumanBodyRegion; - width: number; - length: number; - description: string; - scale: number; - exudate_amount: (typeof PressureSoreExudateAmountOptions)[number]; - tissue_type: (typeof PressureSoreTissueTypeOptions)[number]; -}; -export interface DailyRoundsModel { - spo2?: number; - rhythm?: (typeof HEARTBEAT_RHYTHM_CHOICES)[number]; - rhythm_detail?: string; - bp?: BloodPressure; - pulse?: number; - resp?: number; - temperature?: number; - physical_examination_info?: string; - other_details?: string; - consultation?: number; - action?: string; - review_interval?: number; - id?: string; - admitted_to?: string; - patient_category?: PatientCategory; - recommend_discharge?: boolean; - created_date?: string; - modified_date?: string; - taken_at?: string; - consciousness_level?: (typeof CONSCIOUSNESS_LEVEL)[number]["value"]; - rounds_type?: (typeof DailyRoundTypes)[number]; - last_updated_by_telemedicine?: boolean; - created_by_telemedicine?: boolean; - created_by?: UserBareMinimum; - last_edited_by?: UserBareMinimum; - bed?: string; - pain_scale_enhanced?: IPainScale[]; - in_prone_position?: boolean; - left_pupil_size?: number; - left_pupil_size_detail?: string; - left_pupil_light_reaction?: string; - left_pupil_light_reaction_detail?: string; - right_pupil_size?: number; - right_pupil_size_detail?: string; - right_pupil_light_reaction?: string; - right_pupil_light_reaction_detail?: string; - glasgow_eye_open?: number; - glasgow_motor_response?: number; - glasgow_verbal_response?: number; - limb_response_upper_extremity_right?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_upper_extremity_left?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_lower_extremity_left?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - limb_response_lower_extremity_right?: (typeof LIMB_RESPONSE_OPTIONS)[number]["value"]; - glasgow_total_calculated?: number; - bilateral_air_entry?: boolean; - etco2?: number; - po2?: number; - pco2?: number; - ph?: number; - hco3?: number; - base_excess?: number; - lactate?: number; - sodium?: number; - potassium?: number; - blood_sugar_level?: number; - insulin_intake_dose?: number; - insulin_intake_frequency?: (typeof INSULIN_INTAKE_FREQUENCY_OPTIONS)[number]; - dialysis_fluid_balance?: number; - dialysis_net_balance?: number; - nursing?: { - procedure: (typeof NURSING_CARE_PROCEDURES)[number]; - description: string; - }[]; - feeds?: NameQuantity[]; - infusions?: NameQuantity[]; - iv_fluids?: NameQuantity[]; - output?: NameQuantity[]; - total_intake_calculated?: number; - total_output_calculated?: number; - ventilator_spo2?: number; - ventilator_interface?: (typeof RESPIRATORY_SUPPORT)[number]["value"]; - ventilator_oxygen_modality?: (typeof OXYGEN_MODALITY_OPTIONS)[number]["value"]; - ventilator_oxygen_modality_flow_rate?: number; - ventilator_oxygen_modality_oxygen_rate?: number; - ventilator_fio2?: number; - ventilator_mode?: (typeof VENTILATOR_MODE_OPTIONS)[number]; - ventilator_peep?: number; - ventilator_pip?: number; - ventilator_mean_airway_pressure?: number; - ventilator_resp_rate?: number; - ventilator_pressure_support?: number; - - ventilator_tidal_volume?: number; - pressure_sore?: IPressureSore[]; - bowel_issue?: (typeof BOWEL_ISSUE_CHOICES)[number]; - bladder_drainage?: (typeof BLADDER_DRAINAGE_CHOICES)[number]; - bladder_issue?: (typeof BLADDER_ISSUE_CHOICES)[number]; - is_experiencing_dysuria?: boolean; - urination_frequency?: (typeof URINATION_FREQUENCY_CHOICES)[number]; - sleep?: (typeof SLEEP_CHOICES)[number]; - nutrition_route?: (typeof NUTRITION_ROUTE_CHOICES)[number]; - oral_issue?: (typeof ORAL_ISSUE_CHOICES)[number]; - appetite?: (typeof APPETITE_CHOICES)[number]; -} - -export interface FacilityNameModel { - id?: string; - name?: string; -} - // File Upload Models export type FileCategory = "unspecified" | "xray" | "audio" | "identity_proof"; diff --git a/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx b/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx index ed60dbe4b7e..5d2e1391d6c 100644 --- a/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx +++ b/src/components/Questionnaire/QuestionTypes/LocationQuestion.tsx @@ -79,7 +79,7 @@ export function LocationQuestion({ Select Location ; - - const { data: patientData } = useQuery({ - queryKey: ["patient", related_patient], - queryFn: query(routes.patient.getPatient, { - pathParams: { id: String(related_patient) }, - }), - enabled: !!related_patient, - }); - - const form = useForm({ - resolver: zodResolver(resourceFormSchema), - defaultValues: { - category: "", - assigned_facility: null, - emergency: "false" as const, - title: "", - reason: "", - referring_facility_contact_name: "", - referring_facility_contact_number: "", - priority: 1, - }, - }); - - const { mutate: createResource, isPending } = useMutation({ - mutationFn: mutate(routes.createResource), - onSuccess: (data: ResourceRequest) => { - toast.success(t("resource_created_successfully")); - navigate(`/facility/${facilityId}/resource/${data.id}`); - }, - }); - - const onSubmit = (data: ResourceFormValues) => { - createResource({ - status: "PENDING", - category: data.category, - origin_facility: String(props.facilityId), - assigned_facility: data.assigned_facility?.id || null, - approving_facility: null, - emergency: data.emergency === "true", - title: data.title, - reason: data.reason, - referring_facility_contact_name: data.referring_facility_contact_name, - referring_facility_contact_number: data.referring_facility_contact_number, - related_patient: related_patient, - priority: data.priority, - }); - }; - - const { data: facilities } = useQuery({ - queryKey: ["facilities", facilitySearch], - queryFn: query.debounced(facilityApi.getAllFacilities, { - queryParams: { - search_text: facilitySearch, - limit: 50, - }, - }), - }); - - const facilityOptions = facilities?.results.map((facility) => ({ - label: facility.name, - value: facility.id, - })); - - const fillMyDetails = () => { - form.setValue( - "referring_facility_contact_name", - `${authUser.first_name} ${authUser.last_name}`.trim(), - ); - if (authUser.phone_number) { - form.setValue("referring_facility_contact_number", authUser.phone_number); - } - }; - - if (isPending) { - return ; - } - - return ( - - - - - - {patientData && ( - - - - - - {t("linked_patient")}:{" "} - {patientData.name} - - - - - )} - - - - - {t("basic_information")} - - - {t("resource_request_basic_info_description")} - - - - - ( - - - {t("facility_for_care_support")} - - - { - const facility = - facilities?.results.find( - (f) => f.id === value, - ) ?? null; - form.setValue("assigned_facility", facility); - }} - /> - - - {t("select_facility_description")} - - - - )} - /> - - ( - - {t("is_this_an_emergency")} - - - - - - - - {t("yes")} - - - - - - - - {t("no")} - - - - - - {t("emergency_description")} - - - - )} - /> - - - ( - - {t("category")} - - - - - - - - {RESOURCE_CATEGORY_CHOICES.map((category) => ( - - {category.text} - - ))} - - - - {t("category_description")} - - - - )} - /> - - - - - - - - {t("request_details")} - - - {t("resource_request_details_description")} - - - - ( - - {t("request_title")} - - field.onChange(value)} - /> - - - {t("request_title_description")} - - - - )} - /> - - ( - - {t("request_reason")} - - field.onChange(value)} - /> - - - {t("request_reason_description")} - - - - )} - /> - - - - - - - - - {t("contact_information")} - - - {t("contact_information_description")} - - - - - {t("fill_my_details")} - - - - - ( - - {t("contact_person")} - - field.onChange(value)} - /> - - - {t("contact_person_description")} - - - - )} - /> - - ( - - {t("contact_phone")} - - field.onChange(value)} - /> - - - {t("contact_phone_description")} - - - - )} - /> - - - - - goBack()} - > - {t("cancel")} - - - {isPending && ( - - )} - {isPending ? t("submitting") : t("submit")} - - - - - - - - ); -} diff --git a/src/components/Resource/ResourceDetailsUpdate.tsx b/src/components/Resource/ResourceDetailsUpdate.tsx deleted file mode 100644 index e589a83c166..00000000000 --- a/src/components/Resource/ResourceDetailsUpdate.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { useMutation } from "@tanstack/react-query"; -import { t } from "i18next"; -import { navigate, useQueryParams } from "raviger"; -import { useEffect, useReducer, useState } from "react"; -import { toast } from "sonner"; - -import Card from "@/CAREUI/display/Card"; - -import Autocomplete from "@/components/ui/autocomplete"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; - -import CircularProgress from "@/components/Common/CircularProgress"; -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; -import UserAutocomplete from "@/components/Common/UserAutocompleteFormField"; -import RadioFormField from "@/components/Form/FormFields/RadioFormField"; -import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; -import { UserModel } from "@/components/Users/models"; - -import useAppHistory from "@/hooks/useAppHistory"; - -import { RESOURCE_STATUS_CHOICES } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import mutate from "@/Utils/request/mutate"; -import query from "@/Utils/request/query"; -import facilityApi from "@/types/facility/facilityApi"; -import { UpdateResourceRequest } from "@/types/resourceRequest/resourceRequest"; - -interface resourceProps { - id: string; - facilityId: string; -} - -const initForm: Partial = { - assigned_facility: null, - emergency: false, - title: "", - reason: "", - assigned_to: null, -}; - -const requiredFields: any = { - assigned_facility_type: { - errorText: "Please Select Facility Type", - }, -}; - -const initError = Object.assign( - {}, - ...Object.keys(initForm).map((k) => ({ [k]: "" })), -); - -const initialState = { - form: { ...initForm }, - errors: { ...initError }, -}; - -export const ResourceDetailsUpdate = (props: resourceProps) => { - const { goBack } = useAppHistory(); - const [qParams, _] = useQueryParams(); - const [facilitySearch, setFacilitySearch] = useState(""); - const [assignedUser, SetAssignedUser] = useState(); - const resourceFormReducer = (state = initialState, action: any) => { - switch (action.type) { - case "set_form": { - return { - ...state, - form: action.form, - }; - } - case "set_error": { - return { - ...state, - errors: action.errors, - }; - } - default: - return state; - } - }; - - const [state, dispatch] = useReducer(resourceFormReducer, initialState); - const { data, isLoading: assignedUserLoading } = useQuery({ - queryKey: ["user", props.facilityId], - queryFn: query(routes.userList), - }); - - useEffect(() => { - if (data) { - SetAssignedUser(data.results[0]); - } - }, [data]); - - const validateForm = () => { - const errors = { ...initError }; - let isInvalidForm = false; - Object.keys(requiredFields).forEach((field) => { - if (!state.form[field] || !state.form[field].length) { - errors[field] = requiredFields[field].errorText; - isInvalidForm = true; - } - }); - - dispatch({ type: "set_error", errors }); - return isInvalidForm; - }; - - const handleChange = (e: FieldChangeEvent) => { - dispatch({ - type: "set_form", - form: { ...state.form, [e.name]: e.value }, - }); - }; - - const handleOnSelect = (user: any) => { - const form = { ...state.form }; - form["assigned_to"] = user?.value?.id; - SetAssignedUser(user.value); - dispatch({ type: "set_form", form }); - }; - - const setFacility = (selected: any, name: string) => { - const form = { ...state.form }; - form[name] = selected; - dispatch({ type: "set_form", form }); - }; - const { data: resourceDetails } = useQuery({ - queryKey: ["resource", props.facilityId, props.id], - queryFn: query(routes.getResourceDetails, { - pathParams: { id: props.id }, - }), - }); - useEffect(() => { - if (resourceDetails) { - dispatch({ - type: "set_form", - form: { - ...resourceDetails, - status: qParams.status || resourceDetails.status.toLowerCase(), - }, - }); - } - }, [resourceDetails]); - - const { mutate: updateResource, isPending: updateResourceLoading } = - useMutation({ - mutationFn: mutate(routes.updateResource, { - pathParams: { id: props.id }, - }), - onSuccess: (data) => { - dispatch({ type: "set_form", form: data }); - toast.success(t("request_updated_successfully")); - navigate(`/facility/${props.facilityId}/resource/${props.id}`); - }, - }); - - const { data: facilities } = useQuery({ - queryKey: ["facilities", facilitySearch], - queryFn: query.debounced(facilityApi.getAllFacilities, { - queryParams: { - search_text: facilitySearch, - limit: 50, - }, - }), - }); - - const facilityOptions = facilities?.results.map((facility) => ({ - label: facility.name, - value: facility.id, - })); - - const handleSubmit = async () => { - const validForm = validateForm(); - - if (validForm) { - const resourceData: UpdateResourceRequest = { - id: props.id, - status: state.form.status, - origin_facility: state.form.origin_facility?.id, - assigned_facility: state.form?.assigned_facility?.id, - emergency: [true, "true"].includes(state.form.emergency), - title: state.form.title, - reason: state.form.reason, - assigned_to: state.form.assigned_to, - category: state.form.category, - priority: state.form.priority, - referring_facility_contact_number: - state.form.referring_facility_contact_number, - referring_facility_contact_name: - state.form.referring_facility_contact_name, - approving_facility: state.form.approving_facility?.id, - related_patient: state.form.related_patient?.id, - }; - updateResource(resourceData); - } - }; - - if (updateResourceLoading || !resourceDetails) { - return ; - } - - return ( - - - - - - option.text} - onChange={handleChange} - optionLabel={(option) => t(`resource_status__${option.text}`)} - /> - - - - {assignedUserLoading ? ( - - ) : ( - - )} - - - - - - {t("facility_assign_request")} - - - setFacility(selected, "assigned_facility") - } - /> - - - - - - - - - {t("request_reason")} - - - handleChange({ name: e.target.name, value: e.target.value }) - } - /> - {state.errors.reason && ( - - {state.errors.emergency} - - )} - - - - (o ? "Yes" : "No")} - optionValue={(o) => String(o)} - value={String(state.form.emergency)} - error={state.errors.emergency} - /> - - - - goBack()}> - {t("cancel")} - - - {t("submit")} - - - - - - - ); -}; diff --git a/src/components/Resource/ResourceForm.tsx b/src/components/Resource/ResourceForm.tsx new file mode 100644 index 00000000000..fb93e88e316 --- /dev/null +++ b/src/components/Resource/ResourceForm.tsx @@ -0,0 +1,571 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { Link, navigate, useQueryParams } from "raviger"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; +import * as z from "zod"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { Alert, AlertDescription } from "@/components/ui/alert"; +import Autocomplete from "@/components/ui/autocomplete"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { PhoneInput } from "@/components/ui/phone-input"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Textarea } from "@/components/ui/textarea"; + +import Loading from "@/components/Common/Loading"; +import Page from "@/components/Common/Page"; +import UserSelector from "@/components/Common/UserSelector"; + +import useAppHistory from "@/hooks/useAppHistory"; +import useAuthUser from "@/hooks/useAuthUser"; + +import { RESOURCE_STATUS_CHOICES } from "@/common/constants"; +import { RESOURCE_CATEGORY_CHOICES } from "@/common/constants"; + +import routes from "@/Utils/request/api"; +import mutate from "@/Utils/request/mutate"; +import query from "@/Utils/request/query"; +import { mergeAutocompleteOptions } from "@/Utils/utils"; +import validators from "@/Utils/validators"; +import facilityApi from "@/types/facility/facilityApi"; +import { ResourceRequest } from "@/types/resourceRequest/resourceRequest"; +import { UserBase } from "@/types/user/user"; + +interface ResourceProps { + facilityId: number; + id?: string; +} + +export default function ResourceForm({ facilityId, id }: ResourceProps) { + const [facilitySearch, setFacilitySearch] = useState(""); + const { goBack } = useAppHistory(); + const { t } = useTranslation(); + const [{ related_patient }] = useQueryParams(); + const [assignedToUser, setAssignedToUser] = useState(); + const authUser = useAuthUser(); + + const resourceFormSchema = z.object({ + status: z.string().min(1, { message: t("field_required") }), + category: z.string().min(1, { message: t("field_required") }), + assigned_facility: z + .object({ + id: z.string(), + name: z.string(), + }) + .nullable(), + assigned_to: z.string().min(1, { message: t("field_required") }), + emergency: z.enum(["true", "false"]), + title: z.string().min(1, { message: t("field_required") }), + reason: z.string().min(1, { message: t("field_required") }), + referring_facility_contact_name: z + .string() + .min(1, { message: t("field_required") }), + referring_facility_contact_number: validators().phoneNumber.required, + priority: z.number().default(1), + }); + + type ResourceFormValues = z.infer; + + const { data: patientData } = useQuery({ + queryKey: ["patient", related_patient], + queryFn: query(routes.patient.getPatient, { + pathParams: { id: String(related_patient) }, + }), + enabled: !!related_patient, + }); + + const { data: resourceData } = useQuery({ + queryKey: ["resource_request", id], + queryFn: query(routes.getResourceDetails, { + pathParams: { id: String(id) }, + }), + enabled: !!id, + }); + + const form = useForm({ + resolver: zodResolver(resourceFormSchema), + defaultValues: { + status: "pending", + category: "", + assigned_facility: null, + assigned_to: "", + emergency: "false" as const, + title: "", + reason: "", + referring_facility_contact_name: "", + referring_facility_contact_number: "", + priority: 1, + }, + }); + + useEffect(() => { + if (resourceData) { + form.reset({ + status: resourceData.status, + category: resourceData.category, + assigned_facility: resourceData.assigned_facility, + assigned_to: resourceData.assigned_to?.id, + emergency: resourceData.emergency ? "true" : "false", + title: resourceData.title, + reason: resourceData.reason, + referring_facility_contact_name: + resourceData.referring_facility_contact_name, + referring_facility_contact_number: + resourceData.referring_facility_contact_number, + priority: resourceData.priority, + }); + if (resourceData.assigned_to) { + setAssignedToUser(resourceData.assigned_to); + } else { + setAssignedToUser(undefined); + } + } + }, [resourceData, form]); + + const { mutate: createResource, isPending } = useMutation({ + mutationFn: mutate(routes.createResource), + onSuccess: (data: ResourceRequest) => { + toast.success(t("resource_created_successfully")); + navigate(`/facility/${facilityId}/resource/${data.id}`); + }, + }); + + const { mutate: updateResource, isPending: isUpdatePending } = useMutation({ + mutationFn: mutate(routes.updateResource, { + pathParams: { id: String(id) }, + }), + onSuccess: (data: ResourceRequest) => { + toast.success(t("resource_updated_successfully")); + navigate(`/facility/${facilityId}/resource/${data.id}`); + }, + }); + + const onSubmit = (data: ResourceFormValues) => { + const resourcePayload = { + status: data.status, + category: data.category, + origin_facility: String(facilityId), + assigned_facility: data.assigned_facility?.id || null, + assigned_to: assignedToUser?.id || null, + approving_facility: null, + emergency: data.emergency === "true", + title: data.title, + reason: data.reason, + referring_facility_contact_name: data.referring_facility_contact_name, + referring_facility_contact_number: data.referring_facility_contact_number, + related_patient: related_patient, + priority: data.priority, + }; + + if (id) { + updateResource({ ...resourcePayload, id }); + } else { + createResource(resourcePayload); + } + }; + const { data: facilities } = useQuery({ + queryKey: ["facilities", facilitySearch], + queryFn: query.debounced(facilityApi.getAllFacilities, { + queryParams: { + search_text: facilitySearch, + limit: 50, + }, + }), + }); + + const facilityOptions = facilities?.results.map((facility) => ({ + label: facility.name, + value: facility.id, + })); + + const handleUserChange = (user: UserBase) => { + form.setValue("assigned_to", user.id); + setAssignedToUser(user); + }; + + const fillMyDetails = () => { + form.setValue( + "referring_facility_contact_name", + `${authUser.first_name} ${authUser.last_name}`.trim(), + ); + if (authUser.phone_number) { + form.setValue("referring_facility_contact_number", authUser.phone_number); + } + }; + + if (isPending || isUpdatePending) { + return ; + } + + return ( + + + + + + + {patientData && ( + + + + + + {t("linked_patient")}:{" "} + + {patientData.name} + + + + + + )} + + + + + {t("basic_information")} + + + {t("resource_request_basic_info_description")} + + + + + ( + + + {t("facility_for_care_support")} + + + { + const facility = + facilities?.results.find( + (f) => f.id === value, + ) ?? null; + form.setValue("assigned_facility", facility); + }} + /> + + + {t("select_facility_description")} + + + + )} + /> + + ( + + {t("is_this_an_emergency")} + + + + + + + + {t("yes")} + + + + + + + + {t("no")} + + + + + + {t("emergency_description")} + + + + )} + /> + + ( + + {t("status")} + + + + + + + + {RESOURCE_STATUS_CHOICES.map((option, index) => ( + + {t(`resource_status__${option.text}`)} + + ))} + + + + + + )} + /> + ( + + {t("category")} + + + + + + + + {RESOURCE_CATEGORY_CHOICES.map((category) => ( + + {category.text} + + ))} + + + + + )} + /> + ( + + {t("assigned_to")} + + + + + + )} + /> + + + + + + + + {t("request_details")} + + + {t("resource_request_details_description")} + + + + ( + + {t("request_title")} + + field.onChange(value)} + /> + + + {t("request_title_description")} + + + + )} + /> + + ( + + {t("request_reason")} + + field.onChange(value)} + /> + + + {t("request_reason_description")} + + + + )} + /> + + + + + + + + + {t("contact_information")} + + + {t("contact_information_description")} + + + + + {t("fill_my_details")} + + + + + ( + + {t("contact_person")} + + field.onChange(value)} + /> + + + {t("contact_person_description")} + + + + )} + /> + + ( + + {t("contact_phone")} + + field.onChange(value)} + /> + + + {t("contact_phone_description")} + + + + )} + /> + + + + + goBack()} + > + {t("cancel")} + + + {isPending && ( + + )} + {isPending ? t("submitting") : t("submit")} + + + + + + + + + ); +} diff --git a/src/components/Users/UserAvatar.tsx b/src/components/Users/UserAvatar.tsx index 1d16f4ba8bd..2d81119e30c 100644 --- a/src/components/Users/UserAvatar.tsx +++ b/src/components/Users/UserAvatar.tsx @@ -97,14 +97,14 @@ export default function UserAvatar({ username }: { username: string }) { imageUrl={userData?.profile_picture_url} handleUpload={handleAvatarUpload} handleDelete={handleAvatarDelete} - onClose={() => setEditAvatar(false)} + onOpenChange={(open) => setEditAvatar(open)} /> diff --git a/src/components/Users/UserDeleteDialog.tsx b/src/components/Users/UserDeleteDialog.tsx index 5a2becefe3d..a088c9f2512 100644 --- a/src/components/Users/UserDeleteDialog.tsx +++ b/src/components/Users/UserDeleteDialog.tsx @@ -1,26 +1,51 @@ -import ConfirmDialog from "@/components/Common/ConfirmDialog"; +import { useTranslation } from "react-i18next"; + +import { cn } from "@/lib/utils"; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { buttonVariants } from "@/components/ui/button"; interface ConfirmDialogProps { name: string; handleCancel: () => void; handleOk: () => void; + show: boolean; } const UserDeleteDialog = (props: ConfirmDialogProps) => { + const { t } = useTranslation(); return ( - - Are you sure you want to delete user {props.name} ? - - } - action="Delete" - variant="destructive" - show - onConfirm={props.handleOk} - onClose={props.handleCancel} - /> + + + + {t("delete_user")} + + {t("are_you_sure_you_want_to_delete_user")} + {props.name}? + + + + + {t("cancel")} + + + {t("delete")} + + + + ); }; diff --git a/src/components/Users/UserForm.tsx b/src/components/Users/UserForm.tsx index 76010214af9..ee78d887d9f 100644 --- a/src/components/Users/UserForm.tsx +++ b/src/components/Users/UserForm.tsx @@ -145,7 +145,7 @@ export default function UserForm({ last_name: userData.last_name, email: userData.email, phone_number: userData.phone_number || "", - gender: userData.gender, + gender: userData.gender || undefined, }; form.reset(formData); } @@ -155,7 +155,7 @@ export default function UserForm({ const [isUsernameFieldFocused, setIsUsernameFieldFocused] = useState(false); //const userType = form.watch("user_type"); - const usernameInput = form.watch("username"); + const usernameInput = form.watch("username") || ""; const phoneNumber = form.watch("phone_number"); useEffect(() => { diff --git a/src/components/Users/UserFormValidations.tsx b/src/components/Users/UserFormValidations.tsx index f07e08a9718..f87017fa479 100644 --- a/src/components/Users/UserFormValidations.tsx +++ b/src/components/Users/UserFormValidations.tsx @@ -1,8 +1,8 @@ import { Trans } from "react-i18next"; -import CareIcon from "@/CAREUI/icons/CareIcon"; +import { cn } from "@/lib/utils"; -import { classNames } from "@/Utils/utils"; +import CareIcon from "@/CAREUI/icons/CareIcon"; export type UserType = "doctor" | "nurse" | "staff" | "volunteer"; @@ -72,7 +72,7 @@ export const validateRule = ( )}{" "} {showDeleteDialog && ( { @@ -96,6 +97,7 @@ export default function UserSummaryTab({ userData }: { userData?: UserBase }) { }} /> )} + ( { const currentValue = - options.find( - (option) => option.label.toLowerCase() === v.toLowerCase(), - )?.value || ""; - onChange(currentValue === value ? "" : currentValue); + options.find((o) => `${o.label} - ${o.value}` === v)?.value || + ""; + onChange(currentValue); setOpen(false); }} > diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index b76d7c5b131..e2514538690 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -8,7 +8,7 @@ const Input = React.forwardRef>( + - - - setArchiveDialogueOpen(null)} + > + + + + + + + + + {t("archive_file")} + + {t("this_action_is_irreversible")} + + + + + + + { + event.preventDefault(); + handleFileArchive(archiveDialogueOpen); + }} + className="mx-2 my-4 flex w-full flex-col" + > + + + }} + /> + + setArchiveReason(e.target.value)} + className={cn( + archiveReasonError && + "border-red-500 focus-visible:ring-red-500", + )} /> + {archiveReasonError && ( + {archiveReasonError} + )} - - Archive File - - {t("this_action_is_irreversible")} - + + setArchiveDialogueOpen(null)} + > + {t("cancel")} + + + {t("proceed")} + - + + + + setArchiveDialogueOpen(null)} + onOpenChange={() => setArchiveDialogueOpen(null)} > - { - event.preventDefault(); - handleFileArchive(archiveDialogueOpen); - }} - className="mx-2 my-4 flex w-full flex-col" - > - - - }} - /> - - setArchiveReason(e.target.value)} - className={cn( - archiveReasonError && - "border-red-500 focus-visible:ring-red-500", - )} - /> - {archiveReasonError && ( - {archiveReasonError} - )} + + + + {archiveDialogueOpen?.name} {t("archived")} + + + + + {t("this_file_has_been_archived")} + + + {[ + { + label: "File Name", + content: archiveDialogueOpen?.name, + icon: "l-file", + }, + { + label: "Uploaded By", + content: archiveDialogueOpen?.uploaded_by?.username, + icon: "l-user", + }, + { + label: "Uploaded On", + content: formatDateTime(archiveDialogueOpen?.created_date), + icon: "l-clock", + }, + { + label: "Archive Reason", + content: archiveDialogueOpen?.archive_reason, + icon: "l-archive", + }, + { + label: "Archived By", + content: archiveDialogueOpen?.archived_by?.username, + icon: "l-user", + }, + { + label: "Archived On", + content: formatDateTime(archiveDialogueOpen?.archived_datetime), + icon: "l-clock", + }, + ].map((item, index) => ( + + + + + + + {item.label} + + + {item.content} + + + + ))} - + {t("cancel")} - - {t("proceed")} - - - - - {archiveDialogueOpen?.name} (Archived) - - } - fixedWidth={false} - className="md:w-[700px]" - onClose={() => setArchiveDialogueOpen(null)} + + + setEditDialogueOpen(null)} > - - - {t("this_file_has_been_archived")} - - - {[ - { - label: "File Name", - content: archiveDialogueOpen?.name, - icon: "l-file", - }, - { - label: "Uploaded By", - content: archiveDialogueOpen?.uploaded_by?.username, - icon: "l-user", - }, - { - label: "Uploaded On", - content: formatDateTime(archiveDialogueOpen?.created_date), - icon: "l-clock", - }, - { - label: "Archive Reason", - content: archiveDialogueOpen?.archive_reason, - icon: "l-archive", - }, - { - label: "Archived By", - content: archiveDialogueOpen?.archived_by?.username, - icon: "l-user", - }, - { - label: "Archived On", - content: formatDateTime(archiveDialogueOpen?.archived_datetime), - icon: "l-clock", - }, - ].map((item, index) => ( - - - - - - - {item.label} + + + + + + - - {item.content} + + {t("rename_file")} - - ))} - - - setArchiveDialogueOpen(null)} + + + { + event.preventDefault(); + setEditing(true); + if (editDialogueOpen) partialupdateFileName(editDialogueOpen); + }} + className="flex w-full flex-col" > - {t("cancel")} - - - - - - + {t("enter_the_file_name")} + { + setEditDialogueOpen({ + ...editDialogueOpen, + name: e.target.value, + }); + }} /> + {editError && {editError}} - - {t("rename_file")} + + setEditDialogueOpen(null)} + > + {t("cancel")} + + + {t("proceed")} + - - } - onClose={() => setEditDialogueOpen(null)} - > - { - event.preventDefault(); - setEditing(true); - if (editDialogueOpen) partialupdateFileName(editDialogueOpen); - }} - className="flex w-full flex-col" - > - - {t("enter_the_file_name")} - { - setEditDialogueOpen({ - ...editDialogueOpen, - name: e.target.value, - }); - }} - /> - {editError && {editError}} - - - setEditDialogueOpen(null)} - > - {t("cancel")} - - - {t("proceed")} - - - - + + + > ); diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index d53b9497e08..d1b556fbb22 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -363,8 +363,8 @@ export default function useFileUpload( const Dialogues = ( <> setCameraModalOpen(false)} + open={cameraModalOpen} + onOpenChange={(open) => setCameraModalOpen(open)} onCapture={(file) => { setFiles((prev) => [...prev, file]); }} diff --git a/src/hooks/useFilters.tsx b/src/hooks/useFilters.tsx index b6e9022a675..a0c10a7a24e 100644 --- a/src/hooks/useFilters.tsx +++ b/src/hooks/useFilters.tsx @@ -2,12 +2,14 @@ import { QueryParam, setQueryParamsOptions, useQueryParams } from "raviger"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { cn } from "@/lib/utils"; + import GenericFilterBadge from "@/CAREUI/display/FilterBadge"; import PaginationComponent from "@/components/Common/Pagination"; import FiltersCache from "@/Utils/FiltersCache"; -import { classNames, humanizeStrings } from "@/Utils/utils"; +import { humanizeStrings } from "@/Utils/utils"; export type FilterState = Record; @@ -222,7 +224,7 @@ export default function useFilters({ } return ( limit ? "visible" : "invisible", !noMargin && "mt-4", diff --git a/src/index.tsx b/src/index.tsx index 9154b832187..4f8ac291d51 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,6 @@ import careConfig from "@careConfig"; import * as Sentry from "@sentry/browser"; +import React from "react"; import { createRoot } from "react-dom/client"; import { registerSW } from "virtual:pwa-register"; @@ -31,4 +32,8 @@ if (import.meta.env.PROD) { } const root = createRoot(document.getElementById("root") as HTMLElement); -root.render(); +root.render( + + + , +); diff --git a/src/pages/Appointments/AppointmentsPage.tsx b/src/pages/Appointments/AppointmentsPage.tsx index 2a9fd13d298..dfb7c5e1401 100644 --- a/src/pages/Appointments/AppointmentsPage.tsx +++ b/src/pages/Appointments/AppointmentsPage.tsx @@ -399,53 +399,52 @@ export default function AppointmentsPage(props: { facilityId?: string }) { : t("no_results")} - + + setQParams({ + ...qParams, + practitioner: null, + slot: null, + }) + } + className="cursor-pointer w-full" + > + + {t("show_all")} + {!qParams.practitioner && ( + + )} + + + {schedulableUsersQuery.data?.users.map((user) => ( setQParams({ ...qParams, - practitioner: null, + practitioner: user.username, slot: null, }) } - className="cursor-pointer" + className="cursor-pointer w-full" > - {t("show_all")} - {!qParams.practitioner && ( + + + {formatName(user)} + + {user.user_type} + + + {qParams.practitioner === user.username && ( )} - - {schedulableUsersQuery.data?.users.map((user) => ( - - - setQParams({ - ...qParams, - practitioner: user.username, - slot: null, - }) - } - className="cursor-pointer" - > - - - {formatName(user)} - - {user.user_type} - - - {qParams.practitioner === user.username && ( - - )} - - ))} diff --git a/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx b/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx index 0319faf83d2..d0bde50e2ce 100644 --- a/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx +++ b/src/pages/Facility/settings/organizations/FacilityOrganizationUsers.tsx @@ -1,19 +1,19 @@ import { useQuery } from "@tanstack/react-query"; -import { useQueryParams } from "raviger"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; import { Avatar } from "@/components/Common/Avatar"; -import { - CardGridSkeleton, - CardListSkeleton, -} from "@/components/Common/SkeletonLoading"; +import { CardGridSkeleton } from "@/components/Common/SkeletonLoading"; import { UserStatusIndicator } from "@/components/Users/UserListAndCard"; +import useFilters from "@/hooks/useFilters"; + import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; import AddUserSheet from "@/pages/Organization/components/AddUserSheet"; @@ -29,19 +29,30 @@ interface Props { } export default function FacilityOrganizationUsers({ id, facilityId }: Props) { - const [qParams, setQueryParams] = useQueryParams<{ + const [sheetState, setSheetState] = useState<{ sheet: string; username: string; - }>(); + }>({ + sheet: "", + username: "", + }); + const { qParams, updateQuery, Pagination, resultsPerPage } = useFilters({ + limit: 12, + }); const { t } = useTranslation(); - const openAddUserSheet = qParams.sheet === "add"; - const openLinkUserSheet = qParams.sheet === "link"; + const openAddUserSheet = sheetState.sheet === "add"; + const openLinkUserSheet = sheetState.sheet === "link"; const { data: users, isLoading: isLoadingUsers } = useQuery({ - queryKey: ["facilityOrganizationUsers", facilityId, id], - queryFn: query(routes.facilityOrganization.listUsers, { + queryKey: ["facilityOrganizationUsers", facilityId, id, qParams], + queryFn: query.debounced(routes.facilityOrganization.listUsers, { pathParams: { facilityId, organizationId: id }, + queryParams: { + search_text: qParams.search || undefined, + limit: resultsPerPage, + offset: ((qParams.page || 1) - 1) * resultsPerPage, + }, }), enabled: !!id, }); @@ -50,32 +61,32 @@ export default function FacilityOrganizationUsers({ id, facilityId }: Props) { return null; } - if (isLoadingUsers) { - return ( - - - - - - - - - ); - } - return ( - - {t("users")} - + + + + { + updateQuery({ search: e.target.value || undefined }); + }} + className="w-full pl-8" + /> + + { - setQueryParams({ sheet: open ? "add" : "", username: "" }); + setSheetState({ sheet: open ? "add" : "", username: "" }); }} onUserCreated={(user) => { - setQueryParams({ sheet: "link", username: user.username }); + setSheetState({ sheet: "link", username: user.username }); }} /> { - setQueryParams({ sheet: open ? "link" : "", username: "" }); + setSheetState({ sheet: open ? "link" : "", username: "" }); }} - preSelectedUsername={qParams.username} + preSelectedUsername={sheetState.username} /> - - {users?.results?.length === 0 ? ( - - - {t("no_users_found")} - - - ) : ( - users?.results?.map((userRole: OrganizationUserRole) => ( - - - - - - - - - - {userRole.user.first_name} {userRole.user.last_name} - - - + {isLoadingUsers ? ( + + + + ) : ( + + + {!users?.results?.length ? ( + + + {t("no_users_found")} + + + ) : ( + users.results.map((userRole: OrganizationUserRole) => ( + + + + + + + + + + {userRole.user.first_name}{" "} + {userRole.user.last_name} + + + + + + {userRole.user.username} - - + + + + {t("role")} + + {userRole.role.name} + + + + + {t("phone_number")} + + + {userRole.user.phone_number} + + + - - - - {t("role")} - - {userRole.role.name} - + + + {t("see_details")} + + } + /> - - {t("phone_number")} - - {userRole.user.phone_number} - - - - - - - - {t("more_details")} - - } - /> - - - - - )) - )} - + + + )) + )} + + + {(users?.results || []).length > 0 && + users?.count && + users.count > resultsPerPage && ( + + + + )} + + )} ); diff --git a/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx b/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx index 0726a6d44fe..741ec7aa046 100644 --- a/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx +++ b/src/pages/Facility/settings/organizations/components/CreateFacilityOrganizationSheet.tsx @@ -1,4 +1,5 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { t } from "i18next"; import { useState } from "react"; import { toast } from "sonner"; @@ -62,7 +63,7 @@ export default function CreateFacilityOrganizationSheet({ queryClient.invalidateQueries({ queryKey: ["getCurrentUser"], }); - toast.success("Organization created successfully"); + toast.success(t("organization_created_successfully")); setOpen(false); setName(""); setDescription(""); @@ -78,7 +79,7 @@ export default function CreateFacilityOrganizationSheet({ const handleSubmit = () => { if (!name.trim()) { - toast.error("Please enter an organization name"); + toast.error(t("please_enter_organization_name")); return; } @@ -93,16 +94,16 @@ export default function CreateFacilityOrganizationSheet({ return ( - + - Create Department/Team + {t("add_department_team")} - Create Department/Team + {t("create_department_team")} - Create a new department/team in this facility. + {t("create_department_team_description")} @@ -111,18 +112,18 @@ export default function CreateFacilityOrganizationSheet({ setName(e.target.value)} - placeholder="Enter department/team name" + placeholder={t("enter_department_team_name")} /> - Type + {t(`type`)} setOrgType(value)} > - + {ORG_TYPES.map((type) => ( @@ -139,7 +140,7 @@ export default function CreateFacilityOrganizationSheet({ setDescription(e.target.value)} - placeholder="Enter department/team description (optional)" + placeholder={t("enter_department_team_description")} /> @@ -148,7 +149,7 @@ export default function CreateFacilityOrganizationSheet({ onClick={handleSubmit} disabled={isPending || !name.trim()} > - {isPending ? "Creating..." : "Create Organization"} + {isPending ? t("creating") : t("create_organization")} diff --git a/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx b/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx index fd34b3449e2..de12da5794e 100644 --- a/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx +++ b/src/pages/Facility/settings/organizations/components/FacilityOrganizationLayout.tsx @@ -105,7 +105,9 @@ export default function FacilityOrganizationLayout({ asChild className="text-sm text-gray-900 hover:underline hover:underline-offset-2" > - {parent.name} + + {parent.name} + diff --git a/src/pages/UserDashboard.tsx b/src/pages/UserDashboard.tsx index ab7e8430033..7ba9c365b25 100644 --- a/src/pages/UserDashboard.tsx +++ b/src/pages/UserDashboard.tsx @@ -8,6 +8,7 @@ import { Avatar } from "@/components/Common/Avatar"; import useAuthUser, { useAuthContext } from "@/hooks/useAuthUser"; +import { formatDisplayName } from "@/Utils/utils"; import { getOrgLabel } from "@/types/organization/organization"; export default function UserDashboard() { @@ -22,7 +23,7 @@ export default function UserDashboard() { diff --git a/src/pluginTypes.ts b/src/pluginTypes.ts index f25c3aa7eb3..180df1502a2 100644 --- a/src/pluginTypes.ts +++ b/src/pluginTypes.ts @@ -31,6 +31,10 @@ export type PatientInfoCardActionsComponentType = React.FC<{ className?: string; }>; +export type PatientInfoCardMarkAsCompleteComponentType = React.FC<{ + encounter: Encounter; +}>; + export type FacilityHomeActionsComponentType = React.FC<{ facility: FacilityData; className?: string; @@ -53,6 +57,7 @@ export type SupportedPluginComponents = { Scribe: ScribeComponentType; PatientHomeActions: PatientHomeActionsComponentType; PatientInfoCardActions: PatientInfoCardActionsComponentType; + PatientInfoCardMarkAsComplete: PatientInfoCardMarkAsCompleteComponentType; FacilityHomeActions: FacilityHomeActionsComponentType; PatientRegistrationForm: PatientRegistrationFormComponentType; PatientDetailsTabDemographyGeneralInfo: PatientDetailsTabDemographyGeneralInfoComponentType; diff --git a/src/service-worker.ts b/src/service-worker.ts index 078e4237c28..973ff051736 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -1,6 +1,4 @@ /// - -/* eslint-disable no-restricted-globals */ // This service worker can be customized! // See https://developers.google.com/web/tools/workbox/modules // for the list of available Workbox modules, or add any other @@ -13,7 +11,6 @@ import { precacheAndRoute } from "workbox-precaching"; declare const self: ServiceWorkerGlobalScope; -// eslint-disable-next-line no-restricted-globals const _ignored = self.__WB_MANIFEST.map((_) => { return _; }); diff --git a/src/style/CAREUI.css b/src/style/CAREUI.css index 893d16c2e4e..0e01efa2853 100644 --- a/src/style/CAREUI.css +++ b/src/style/CAREUI.css @@ -1,34 +1,3 @@ -.cui-input-legend::before { - content: " "; - position: absolute; - left: -4px; - right: -4px; - top: calc(50% - 3px); - height: 6px; - background: #fff; - z-index: -1; -} - -.cui-label-required::after { - content: "*"; - color: rgb(255, 81, 0); - font-size: 1.2em; - font-weight: bold; - margin-left: 4px; -} - -.cui-input-base { - @apply text-sm block w-full py-3 px-4 text-black placeholder:text-secondary-600 bg-white disabled:bg-secondary-200 border border-secondary-400 focus:border-primary-400 ring-0 focus:ring-1 ring-primary-400 outline-none focus:outline-none shadow-none rounded transition-colors duration-300 !important -} - -.cui-dropdown-base { - @apply z-40 w-full rounded-b-md xl:rounded-b-lg shadow-lg overflow-auto max-h-96 bg-secondary-100 divide-y divide-secondary-300 ring-1 ring-secondary-400 focus:outline-none -} - -.cui-card { - @apply bg-white p-5 rounded-lg shadow -} - .tooltip { @apply relative } @@ -83,41 +52,6 @@ transform: translateY(0px); } -.cui-input:-webkit-autofill, -.cui-input:-webkit-autofill:hover, -.cui-input:-webkit-autofill:focus, -.cui-input:-webkit-autofill:active { - box-shadow: 0 0 0 40px #f9fafb inset !important; - -webkit-box-shadow: 0 0 0 40px #f9fafb inset !important; -} - -.cui-slideover-x { - @apply w-full md:w-[300px] h-full -} - -.cui-slideover-y { - @apply h-full md:h-[300px] w-full -} - -.dropdown-item-primary { @apply accent-primary-500 hover:bg-primary-100 text-black hover:text-primary-500 } -.dropdown-item-secondary { @apply accent-secondary-200 hover:bg-secondary-200 text-secondary-700 } -.dropdown-item-danger { @apply accent-danger-500 hover:bg-danger-100 text-danger-500 } -.dropdown-item-warning { @apply accent-warning-500 hover:bg-warning-100 text-warning-500 } -.dropdown-item-alert { @apply accent-alert-600 hover:bg-alert-100 text-alert-600 } - -.cui-form-button-group { - @apply flex flex-col-reverse md:flex-row md:justify-end gap-2 w-full md:w-auto -} - -.cui-range-slider { - @apply outline-none bg-black/10 h-2 w-full appearance-none transition-all -} -.cui-range-slider::-webkit-slider-thumb { - @apply appearance-none w-5 aspect-square bg-black border border-black rounded-full cursor-pointer hover:scale-125 transition-all -} -.cui-range-slider::-moz-range-thumb { - @apply appearance-none w-5 aspect-square bg-black border border-black rounded-full cursor-pointer -} hr { @apply border border-secondary-300 } diff --git a/src/style/index.css b/src/style/index.css index 2f14f673a18..4c7410e732b 100644 --- a/src/style/index.css +++ b/src/style/index.css @@ -10,23 +10,7 @@ @tailwind base; @tailwind components; - -@keyframes indeterminateAnimation { - 0% { - transform: translateX(0) scaleX(0); - } - 40% { - transform: translateX(0) scaleX(0.4); - } - 100% { - transform: translateX(100%) scaleX(0.5); - } -} - -.indeterminate { - animation: indeterminateAnimation 1s infinite linear; - transform-origin: 0% 50%; -} +@tailwind utilities; html { @@ -72,363 +56,12 @@ h6 { font-size: 0.875rem; } -.btn { - @apply inline-flex items-center justify-center whitespace-nowrap text-sm font-semibold py-2 px-4 rounded cursor-pointer; -} - -.btn:focus { - @apply outline-none shadow-inner; -} - -[type="text"]:focus { - @apply border-black ring-white; -} - - -/* Button Styles */ - -.btn-large { - @apply py-2 px-5 text-base h-10; -} - -a { - @apply text-blue-800; -} - -a:hover { - @apply text-blue-600; -} - -.bg-primary { - background-image: linear-gradient( 135deg, rgba(56, 161, 105, 1) 0%, rgba(47, 133, 90, 1) 100%); -} - - -/* Button */ - -button:focus { - outline: none; -} - -.btn { - @apply inline-flex items-center justify-center whitespace-nowrap text-sm font-semibold py-2 px-4 rounded cursor-pointer; -} - -.btn:focus { - @apply outline-none shadow-inner; -} - - -/* Button Styles */ - -.btn-default { - @apply border text-secondary-800; -} - -.btn-default:hover { - @apply bg-secondary-100 text-secondary-600; -} - -.btn-default:focus { - @apply bg-secondary-400 text-secondary-700; -} - -.btn-subtle { - @apply bg-secondary-200 text-secondary-800; -} - -.btn-subtle:hover { - @apply bg-secondary-300 text-secondary-900; -} - -.btn-subtle:focus { - @apply bg-secondary-400 text-secondary-900; -} - -.btn-primary-ghost { - @apply bg-white text-primary-500 border border-primary-500; -} - -.btn-primary-ghost:hover { - @apply bg-primary-100 text-primary-600 border-primary-400; -} - -.btn-primary-ghost:focus { - @apply bg-primary-800 text-white border-primary-400; -} - -.btn-primary { - @apply bg-primary-500 text-white; -} - -.btn-primary:hover { - @apply bg-primary-600; -} - -.btn-primary:focus { - @apply bg-primary-800; - background-image: none; -} - -.btn-warning { - @apply bg-yellow-500 text-white; - background-image: linear-gradient( 135deg, rgba(237, 137, 54, 1) 0%, rgba(221, 107, 32, 1) 100%); -} - -.btn-warning:hover { - @apply bg-yellow-600; - background-image: linear-gradient( 135deg, rgba(221, 107, 32, 1) 0%, rgba(192, 86, 33, 1) 100%); -} - -.btn-warning:focus { - @apply bg-yellow-800; - background-image: none; -} - -.btn-danger { - @apply bg-red-500 text-white; - background-image: linear-gradient( 135deg, rgba(245, 101, 101, 1) 0%, rgba(229, 62, 62, 1) 100%); -} - -.btn-danger:hover { - @apply bg-red-600; - background-image: linear-gradient( 135deg, rgba(229, 62, 62, 1) 0%, rgba(197, 48, 48, 1) 100%); -} - -.btn-danger:focus { - @apply bg-red-800; - background-image: none; -} - - -.primary-button { - @apply focus:outline-none text-white bg-primary-500 hover:bg-primary-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-6 py-2.5 mr-2 mb-2 transition-all ease-in-out duration-200 !important; -} - -.secondary-button { - @apply text-secondary-900 bg-white border border-secondary-300 focus:outline-none hover:bg-secondary-100 focus:ring-4 focus:ring-secondary-200 font-medium rounded-lg text-sm px-6 py-2.5 mr-2 mb-2 transition-all ease-in-out duration-200 !important; -} - -button:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-300 text-secondary-500 shadow-none border-transparent; - background-image: none; -} - -button:disabled:hover, -.disabled:hover, -button:disabled:focus, -.disabled:focus { - @apply bg-secondary-300 text-secondary-500 border-transparent shadow-none; - background-image: none; -} - -.btn-small { - @apply py-1 px-3 text-xs h-7 leading-normal; -} - -.btn-normal { - @apply py-1 px-4 text-sm h-8; -} - -.btn-large { - @apply py-2 px-5 text-base h-10; -} - -.button-xl { - @apply py-2 px-6 text-base h-12; -} - -@tailwind utilities; -.multiselect-dropdown__search-dropdown { - max-height: 20rem; - overflow: auto; -} - -.max-height-dropdown { - max-height: 20rem; - overflow: auto; -} - - -/* Radio */ - -.radio-label { - @apply select-none cursor-pointer; -} - -.radio-label span { - @apply inline-block align-middle; - transform: translate3d(0, 0, 0); -} - -.radio-label span:first-child { - @apply relative rounded-full align-middle border border-secondary-500 bg-white mr-3; - width: 1.125rem; - height: 1.125rem; - transform: scale(1); - transition: all 0.2s ease; -} - -.radio-label span:first-child svg { - position: absolute; - top: 0; - left: 0; - fill: none; - stroke: #ffffff; - stroke-width: 2; - stroke-linecap: round; - stroke-linejoin: round; - transition: all 0.3s ease; - transition-delay: 0.1s; -} - -.radio-label span:first-child:before { - content: ""; - @apply bg-primary-500 w-full h-full block opacity-100 rounded-full; - transform: scale(0); -} - -.radio-label:hover span:first-child { - @apply bg-primary-100 border-primary-500; -} - -.radio-input:checked+.radio-label span:first-child { - @apply bg-primary-500 border-primary-500; - animation: wave 0.4s ease; -} - -.radio-input:checked+.radio-label span:first-child:before { - transform: scale(3.5); - opacity: 0; - transition: all 0.6s ease; -} - - -/* Checkbox */ - -.checkbox__label { - @apply select-none cursor-pointer; -} - -.checkbox__label span { - @apply inline-block align-middle; - transform: translate3d(0, 0, 0); -} - -.checkbox__label span:first-child { - @apply relative rounded align-middle border border-secondary-500 bg-white w-4 h-4 mr-3; - transform: scale(1); - transition: all 0.2s ease; -} - -.checkbox__label span:first-child svg { - position: absolute; - top: 3px; - left: 2px; - fill: none; - stroke: #ffffff; - stroke-width: 2; - stroke-linecap: round; - stroke-linejoin: round; - stroke-dasharray: 16px; - stroke-dashoffset: 16px; - transition: all 0.3s ease; - transition-delay: 0.1s; - transform: translate3d(0, 0, 0); -} - -.checkbox__label span:first-child:before { - content: ""; - @apply bg-primary-500 w-full h-full block opacity-100 rounded-full; - transform: scale(0); -} - -.checkbox__label:hover span:first-child { - @apply bg-primary-100 border-primary-500; -} - -.checkbox__input:checked+.checkbox__label span:first-child { - @apply bg-primary-500 border-primary-500; - animation: wave 0.4s ease; -} - -.checkbox__input:checked+.checkbox__label span:first-child svg { - stroke-dashoffset: 0; -} - -.checkbox__input:checked+.checkbox__label span:first-child:before { - transform: scale(3.5); - opacity: 0; - transition: all 0.6s ease; -} - @keyframes wave { 50% { transform: scale(0.9); } } -label { - @apply block text-secondary-700 text-sm font-medium; -} - -input { - @apply border-secondary-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 transition-colors duration-300 !important; -} - -input:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-200 border-secondary-400 text-secondary-700; -} - -textarea { - @apply border-secondary-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 transition-colors duration-300; -} - -button:disabled, -.disabled { - @apply cursor-not-allowed bg-secondary-300 text-secondary-500 shadow-none border-transparent; - background-image: none; -} - - -/* Styling skeleton loading */ - -.skeleton-placeholder__line-sm { - @apply rounded-full bg-secondary-100; - height: 0.625rem; -} - -.skeleton-placeholder__line-md { - @apply rounded-full bg-secondary-100; - height: 1rem; -} - -.skeleton-placeholder__image { - @apply mt-5 h-48 rounded-lg bg-secondary-100; -} - -.skeleton-animate { - animation: skeletonShimmer 3s infinite linear; - background: linear-gradient(to right, #f5f3f7 10%, #fbfafc 40%, #f5f3f7 70%); - background-size: 1000px 100%; -} - -@keyframes skeletonShimmer { - 0% { - background-position: -1000px 0; - } - 100% { - background-position: 1000px 0; - } -} - -.skeleton-animate-alpha { - animation: skeletonShimmer 3s infinite linear; - background: linear-gradient(to right, rgba(0, 0, 0, 0.1) 10%, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.1) 70%); - background-size: 1000px 100%; -} @media print { @page { @@ -449,20 +82,6 @@ button:disabled, } } -.header-section .appBar { - z-index: 1201; -} - -.header-section .toolbar { - padding-top: 72px; -} - -@media only screen and (min-width: 601px) { - .header-section .menuButton { - display: none; - } -} - .App { text-align: center; } @@ -497,87 +116,10 @@ button:disabled, 100% {opacity: 0;} } -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 12px; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; - transition: color 0.15s; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #007bff; -} - -.badge-secondary { - color: #fff; - background-color: #6c757d; -} - -.badge-success { - color: #fff; - background-color: #28a745; -} - -.badge-warning { - color: #212529; - background-color: #ffc107; -} - -.badge-danger { - color: #fff; - background-color: #dc3545; -} - -.badge-dark { - color: #fff; - background-color: #343a40; -} - .dropdown:hover .dropdown-menu { display: block; } -.border-primary { - border-color: #28a745; -} - -.form-input { - padding-top: 6px; - padding-bottom: 6px; - width: 100%; - border-radius: 4px; - height: 38px; -} - -.form-input:focus { - border-color: #28a745 !important; - box-shadow: 0 0 0 1px #28a745 !important; -} - -.copied-to-cb { - color: #28a745; - font-size: 13px; - padding-left: 5px; - font-style: italic; -} - -.copy-to-cb { - padding-left: 5px; -} - ::-webkit-scrollbar { width: 10px; height: 8px; @@ -596,166 +138,6 @@ button:disabled, background: rgba(0, 0, 0, 0.4); } -@media (min-width:1000px) { - .manualGrid { - display: grid !important - } -} - -.scrollbar-hide::-webkit-scrollbar { - width: 0px; - height: 0px; -} - -/* - Range Sliders for Camera Feed -*/ - -#feed-range { - -webkit-appearance: none; - appearance: none; - margin: 18px 0; - width: 100%; -} - -#feed-range:focus { - outline: none; -} - -#feed-range::-webkit-slider-runnable-track { - width: 100%; - height: 1px; - cursor: pointer; - background: transparent; - border-radius: 5px; -} - -#feed-range::-webkit-slider-thumb { - height: 16px; - width: 16px; - border-radius: 3px; - border: 1px solid white; - background: black; - cursor: pointer; - -webkit-appearance: none; - margin-top: -8px; - border-radius: 50%; -} - -@media (min-width:471px) { - .csv-input { - display: block; - font-size: 0.875rem; - line-height: 1.25rem; - margin: auto; - width: 50%; - } -} - -@media (max-width:470px) { - .csv-input { - display: block; - font-size: 0.875rem; - line-height: 1.25rem; - margin: auto; - width: 100%; - } -} - -[type="checkbox"] { - @apply text-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none; -} - - - -.csv-input::file-selector-button:hover { - background-color: #84e1bc; -} - - - -/* for gmaps search dropdown */ -.pac-container { - z-index: 100000 !important; -} - -.csv-input::file-selector-button { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - font-size: 0.875rem; - line-height: 1.25rem; - font-weight: 600; - border-width: 0; - background-color: #def7ec; - color: #046c4e; -} - -.csv-input::file-selector-button:hover { - background-color: #84e1bc; -} - -/* Patient Category Styles */ - -.patient-comfort { - @apply bg-patient-comfort text-patient-comfort-fore -} -.patient-stable { - @apply bg-patient-stable text-patient-stable-fore -} -.patient-abnormal { - @apply bg-patient-abnormal text-patient-abnormal-fore -} -.patient-critical { - @apply bg-patient-critical text-patient-critical-fore -} -.patient-unknown { - @apply bg-patient-unknown text-patient-unknown-fore -} - -.patient-activelydying { - @apply bg-patient-activelydying text-patient-activelydying-fore -} - -.patient-comfort-ring { - @apply ring-patient-comfort -} -.patient-stable-ring { - @apply ring-patient-stable -} -.patient-abnormal-ring { - @apply ring-patient-abnormal -} -.patient-critical-ring { - @apply ring-patient-critical -} -.patient-unknown-ring { - @apply ring-patient-unknown -} -.patient-activelydying-ring { - @apply ring-patient-activelydying -} - -.patient-comfort-profile { - @apply border-2 border-patient-comfort rounded-t -} -.patient-stable-profile { - @apply border-2 border-patient-stable rounded-t -} -.patient-abnormal-profile { - @apply border-2 border-patient-abnormal rounded-t -} -.patient-critical-profile { - @apply border-2 border-patient-critical rounded-t -} -.patient-unknown-profile { - @apply border border-patient-unknown rounded -} -.patient-activelydying-profile { - @apply border-2 border-patient-activelydying rounded-t -} - /* for gmaps search dropdown */ .pac-container { z-index: 100000 !important; @@ -855,468 +237,6 @@ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-results-button, input[type="search"]::-webkit-search-results-decoration { display: none; } -.service-panel{ - @apply flex-row -} - -@media screen and (max-width: 920px) { - .service-panel{ - @apply flex-col - } -} -/* styles for files in src/Components/CriticalCareRecording/PressureSore */ - -input[type="checkbox"]:checked~.dot { - transform: translateX(100%); -} - - -/* styles for files in src/Components/CriticalCareRecording/components */ - -.range { - position: relative; - width: 644px; - height: 5px; -} - -.range input { - width: 100%; - position: absolute; - top: 2px; - height: 0; - -webkit-appearance: none; -} - -.range input::-webkit-slider-thumb { - -webkit-appearance: none; - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-moz-range-thumb { - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-ms-thumb { - width: 18px; - height: 18px; - margin: -8px 0 0; - border-radius: 50%; - background: rgb(7, 109, 243); - cursor: pointer; - border: 0 !important; - z-index: 10; -} - -.range input::-webkit-slider-runnable-track { - width: 100%; - height: 2px; - cursor: pointer; - background: #b2b2b2; - z-index: 1; -} - -.range input::-moz-range-track { - width: 100%; - height: 2px; - cursor: pointer; - background: black; - z-index: 1; -} - -.range input::-ms-track { - width: 100%; - height: 2px; - cursor: pointer; - background: #b2b2b2; - z-index: 1; -} - -.range input:focus { - background: none; - outline: none; -} - -.range input::-ms-track { - width: 100%; - cursor: pointer; - background: transparent; - border-color: transparent; - color: transparent; -} - -.range-labels { - margin: 18px -41px 0; - padding: 0; - list-style: none; -} - -.range-labels li { - position: relative; - float: left; - width: 90.25px; - text-align: center; - color: black; - font-size: 14px; - cursor: pointer; -} - -.range-labels .label::before { - position: absolute; - top: -25px; - right: 0; - left: 0; - content: ""; - margin: 0 auto; - width: 9px; - height: 9px; - background: #b2b2b2; - border-radius: 50%; -} - -.double-range-labels { - margin: 18px -41px 0; - padding: 0; - list-style: none; -} - -.double-range-labels li { - position: relative; - float: left; - width: 45.25px; - text-align: center; - color: black; - font-size: 14px; - cursor: pointer; -} - -.double-range-labels .label::before { - position: absolute; - top: -25px; - right: 0; - left: 0; - content: ""; - margin: 0 auto; - width: 9px; - height: 9px; - background: #b2b2b2; - border-radius: 50%; -} - -.upper-label { - height: 10px; - margin-bottom: 10px; -} - -.pupil1 { - bottom: 0; - margin: 0 auto; - width: 9px; - height: 9px; - border-radius: 50%; - background-color: black; -} - -.pupil2 { - bottom: 0; - margin: 0 auto; - width: 15px; - height: 15px; - border-radius: 50%; - background-color: black; -} - -.pupil3 { - bottom: 0; - margin: 0 auto; - width: 21px; - height: 21px; - border-radius: 50%; - background-color: black; -} - -.pupil4 { - bottom: 0; - margin: 0 auto; - width: 25px; - height: 25px; - border-radius: 50%; - background-color: black; -} - -.pupil5 { - bottom: 0; - margin: 0 auto; - width: 30px; - height: 30px; - border-radius: 50%; - background-color: black; -} - -.pupil6 { - bottom: 0; - margin: 0 auto; - width: 35px; - height: 35px; - border-radius: 50%; - background-color: black; -} - -.pupil7 { - bottom: 0; - margin: 0 auto; - width: 38px; - height: 38px; - border-radius: 50%; - background-color: black; -} - -.pupil8 { - width: 41px; - height: 41px; - border-radius: 50%; - background-color: black; -} - -.range-labels .active { - color: rgb(7, 109, 243); -} - -.range-labels .selected::before { - background: rgb(7, 109, 243); -} - -.range-labels .active.selected::before { - display: none; -} - -.align-circles { - position: relative; - display: flex; - justify-content: center; - height: 41px; - vertical-align: baseline; -} - -input[type="number"] { - background-color: #fbfafc; -} - - -/* Copied CSS */ - -.slider { - position: relative; - width: 200px; -} - -.slider__track, -.slider__range, -.slider__left-value, -.slider__right-value { - position: absolute; -} - -.slider__track, -.slider__range { - border-radius: 3px; - height: 5px; -} - -.slider__track { - background-color: #1476d8; - width: 100%; - z-index: 1; -} - -.slider__range { - background-color: #19ebe0; - z-index: 2; -} - -.slider__left-value, -.slider__right-value { - color: #1c85ee; - font-size: 12px; - margin-top: 20px; -} - -.slider__left-value { - left: 6px; -} - -.slider__right-value { - right: -4px; -} - - -/* Removing the default appearance */ - -.thumb input { - pointer-events: none; - position: absolute; - height: 0; - width: 200px; - outline: none; -} - -.thumb--left { - z-index: 3; -} - -.thumb--right { - z-index: 4; -} - - -/* For Chrome browsers */ - -.thumb input::-webkit-slider-thumb { - background-color: #242525; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #0b2846; - cursor: pointer; - height: 18px; - width: 18px; - margin-top: 4px; - pointer-events: all; - position: relative; -} - - -/* For Firefox browsers */ - -.thumb input::-moz-range-thumb { - background-color: #272b2c; - border: none; - border-radius: 50%; - box-shadow: 0 0 1px 1px #0d2d4d; - cursor: pointer; - height: 18px; - width: 18px; - margin-top: 4px; - pointer-events: all; - position: relative; -} - -.slider { - appearance: none; - background-color: #dee0e4; - width: 100%; - height: 4px; - margin: 0; - padding: 0; - z-index: 2; -} - -.slider::-webkit-slider-thumb { - cursor: pointer; -} - -.slider-container { - position: relative; - width: 100%; - /* max-width: 700px; */ - display: flex; - justify-content: center; -} - -.indicators { - position: absolute; - top: -200%; - width: 100%; - height: 20px; - border-left: 2px solid #dee0e4; -} - -.tick { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: linear-gradient(to left, #dee0e4 2px, transparent 2px); -} - -.slider-box { - width: 100%; - /* max-width: 700px; */ - padding: 20px 10px 60px 10px; - border-radius: 12px; -} - -.slider-head { - font-family: sans-serif; - margin-bottom: 2rem; -} - -.slider-head>div>h1 { - font-size: 1rem; - font-weight: bold; -} - -.slider-head input { - padding: 0.5rem 0.7rem; - border-radius: 8px; - border: 1px solid #979aa0; - margin-left: 12px; - font-size: 1.1rem; - color: #383a3e; - letter-spacing: 1px; -} - -.slider-head label { - font-weight: 700; - color: #2856ff; - font-size: 1rem; -} - - -.grid-2-col, .grid-3-col, .grid-2-1-col, .grid-3-1-col, .grid-1-2-col { - display: grid; - grid-template-columns: 1fr; - gap: 1.5rem; -} - -@media (min-width: 768px) { - .grid-2-col { - grid-template-columns: repeat(2, 1fr); - } - .grid-3-col { - grid-template-columns: repeat(3, 1fr); - } - .grid-2-1-col { - grid-template-columns: 2fr 1fr; - } - .grid-1-2-col { - grid-template-columns: 1fr 2fr; - } -} - -.input-field-group { - display: flex; - flex-direction: column; - gap: 0.5rem; -} -.col-span-all { - grid-column: 1 / -1; -} - @layer base { :root { --radius: 0.5rem; diff --git a/src/types/emr/medicationAdministration/medicationAdministration.ts b/src/types/emr/medicationAdministration/medicationAdministration.ts index db45449679f..caf6f254711 100644 --- a/src/types/emr/medicationAdministration/medicationAdministration.ts +++ b/src/types/emr/medicationAdministration/medicationAdministration.ts @@ -92,4 +92,6 @@ export interface MedicationAdministrationRead { dose?: DosageQuantity; rate?: Quantity; }; + created_by: UserBareMinimum; + updated_by: UserBareMinimum; } diff --git a/src/types/emr/medicationAdministration/medicationAdministrationApi.ts b/src/types/emr/medicationAdministration/medicationAdministrationApi.ts index 77e37c391e2..35b980b0349 100644 --- a/src/types/emr/medicationAdministration/medicationAdministrationApi.ts +++ b/src/types/emr/medicationAdministration/medicationAdministrationApi.ts @@ -7,12 +7,12 @@ import { } from "./medicationAdministration"; export default { - listMedicationAdministrations: { + list: { path: "/api/v1/patient/{patientId}/medication/administration/", method: HttpMethod.GET, TRes: Type>(), }, - upsertMedicationAdministration: { + upsert: { path: "/api/v1/patient/{patientId}/medication/administration/upsert/", method: HttpMethod.POST, TRes: Type, diff --git a/src/types/emr/medicationStatement/medicationStatementApi.ts b/src/types/emr/medicationStatement/medicationStatementApi.ts index 68736e4ab13..7f65dc5f3c8 100644 --- a/src/types/emr/medicationStatement/medicationStatementApi.ts +++ b/src/types/emr/medicationStatement/medicationStatementApi.ts @@ -1,12 +1,12 @@ import { Type } from "@/Utils/request/api"; import { PaginatedResponse } from "@/Utils/request/types"; -import { MedicationStatement } from "@/types/emr/medicationStatement"; +import { MedicationStatementRead } from "@/types/emr/medicationStatement"; const medicationStatementApi = { list: { path: "/api/v1/patient/{patientId}/medication/statement/", method: "GET", - TRes: Type>(), + TRes: Type>(), }, } as const; diff --git a/src/types/resourceRequest/resourceRequest.ts b/src/types/resourceRequest/resourceRequest.ts index 7594766017c..07bfdbd2479 100644 --- a/src/types/resourceRequest/resourceRequest.ts +++ b/src/types/resourceRequest/resourceRequest.ts @@ -2,6 +2,7 @@ import { FacilityModel } from "@/components/Facility/models"; import { UserBareMinimum } from "@/components/Users/models"; import { PatientModel } from "@/types/emr/patient"; +import { UserBase } from "@/types/user/user"; export interface ResourceRequest { approving_facility: FacilityModel | null; @@ -17,9 +18,9 @@ export interface ResourceRequest { requested_quantity: number; status: string; title: string; - assigned_to: UserBareMinimum | null; - created_by: UserBareMinimum; - updated_by: UserBareMinimum; + assigned_to: UserBase | null; + created_by: UserBase; + updated_by: UserBase; created_date: string; modified_date: string; related_patient: PatientModel | null; @@ -32,6 +33,7 @@ export interface CreateResourceRequest { referring_facility_contact_name: string; referring_facility_contact_number: string; approving_facility: string | null; + assigned_to: string | null; assigned_facility: string | null; origin_facility: string; related_patient: string; diff --git a/src/types/user/userApi.ts b/src/types/user/userApi.ts index f1f776cece0..dd11f7e2ae2 100644 --- a/src/types/user/userApi.ts +++ b/src/types/user/userApi.ts @@ -30,4 +30,4 @@ export default { TRes: Type(), TBody: Type>(), }, -}; +} as const; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index defba258b80..5e2066155a8 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -19,11 +19,7 @@ interface ImportMetaEnv { readonly REACT_CUSTOM_LOGO_ALT?: string; readonly REACT_CUSTOM_DESCRIPTION?: string; readonly REACT_GMAPS_API_KEY?: string; - readonly REACT_GOV_DATA_API_KEY?: string; readonly REACT_RECAPTCHA_SITE_KEY?: string; - readonly REACT_WARTIME_SHIFTING?: string; - readonly REACT_STILL_WATCHING_IDLE_TIMEOUT?: string; - readonly REACT_STILL_WATCHING_PROMPT_DURATION?: string; readonly REACT_JWT_TOKEN_REFRESH_INTERVAL?: string; readonly REACT_MIN_ENCOUNTER_DATE?: string; readonly REACT_DEFAULT_ENCOUNTER_TYPE?: string; diff --git a/vite.config.mts b/vite.config.mts index 749f188002c..308c942b7ad 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -288,6 +288,7 @@ export default defineConfig(({ mode }) => { server: { port: 4000, host: "0.0.0.0", + allowedHosts: true, }, preview: { headers: {
+ {fileNameTooltip} +
+ {fileName} +
+ {t("created_on")}{" "} + {new Date( + uploadedFiles[index].created_date!, + ).toLocaleString("en-US", { + dateStyle: "long", + timeStyle: "short", + })}
- {t("created_on")}{" "} - {new Date( - uploadedFiles[index].created_date!, - ).toLocaleString("en-US", { - dateStyle: "long", - timeStyle: "short", - })} -
Software Update
- {t("patient_records_found_description")}( - {patientList[0].phone_number}) -
+ {t("patient_records_found_description")}( + {patientList[0].phone_number}) +
{t("duplicate_patient_record_confirmation")}
{t("duplicate_patient_record_rejection")}
{t("duplicate_patient_record_birth_unknown")}
{`${Math.round(props.value)}%`}
{fileUpload.error}
{label}
{ - e.stopPropagation(); - onRemove(); - }} - > - -
- {value.selectedLabel} -
- {t("resource_request_basic_info_description")} -
- {t("resource_request_details_description")} -
- {t("contact_information_description")} -
- {state.errors.emergency} -
+ {t("resource_request_basic_info_description")} +
+ {t("resource_request_details_description")} +
+ {t("contact_information_description")} +
{archiveReasonError}
{editError}