);`;
+ assert.equal(transform(src), null);
+});
+
+test("non-component lowercase function returns null", () => {
+ // returns JSX so only the PascalCase guard (not the JSX guard) excludes it
+ const src = `export function notComp() { return
; }`;
+ assert.equal(transform(src), null);
+});
+
+// ---------------------------------------------------------------------------
+// Allowlist filtering
+// ---------------------------------------------------------------------------
+
+test("include=null transforms all matching components", () => {
+ const src = `export function Comp() { return
; }`;
+ const result = transform(src, null);
+ assert.ok(result !== null);
+ assert.ok(result.code.includes('__careRegisterComponent("Comp", Comp)'));
+});
+
+test("include Set transforms Keep but leaves Drop untouched", () => {
+ const src = [
+ `export function Keep() { return
; }`,
+ `export function Drop() { return
; }`,
+ ].join("\n");
+ const result = transform(src, new Set(["Keep"]));
+ assert.ok(result !== null);
+ assert.ok(result.code.includes('__careRegisterComponent("Keep", Keep)'));
+ assert.ok(!result.code.includes('__careRegisterComponent("Drop", Drop)'));
+ // Drop retains its export keyword
+ assert.ok(result.code.includes("export function Drop"));
+});
+
+// ---------------------------------------------------------------------------
+// collectUnsupportedComponentTargets
+// ---------------------------------------------------------------------------
+
+test("collectUnsupportedComponentTargets: export { FooComp } yields FooComp", () => {
+ const src = `function FooComp() { return
; }\nexport { FooComp };`;
+ const targets = collectUnsupportedComponentTargets(src, ID);
+ assert.equal(targets.length, 1);
+ assert.equal(targets[0].name, "FooComp");
+});
+
+test("collectUnsupportedComponentTargets: aliased export { X as YComp } yields YComp", () => {
+ const src = `function X() { return
; }\nexport { X as YComp };`;
+ const targets = collectUnsupportedComponentTargets(src, ID);
+ assert.equal(targets.length, 1);
+ assert.equal(targets[0].name, "YComp");
+});
+
+// ---------------------------------------------------------------------------
+// assertUniqueComponentNames
+// ---------------------------------------------------------------------------
+
+test("assertUniqueComponentNames: duplicate name across files throws", () => {
+ const targets = [
+ { name: "Foo", file: "/src/a.tsx" },
+ { name: "Foo", file: "/src/b.tsx" },
+ ];
+ assert.throws(() => assertUniqueComponentNames(targets), /Duplicate/);
+});
+
+test("assertUniqueComponentNames: unique names do not throw", () => {
+ const targets = [
+ { name: "Foo", file: "/src/a.tsx" },
+ { name: "Bar", file: "/src/b.tsx" },
+ ];
+ assert.doesNotThrow(() => assertUniqueComponentNames(targets));
+});
+
+// ---------------------------------------------------------------------------
+// assertAllowlistFormsSupported
+// ---------------------------------------------------------------------------
+
+test("assertAllowlistFormsSupported: allowlisted name in unsupported throws", () => {
+ const unsupported = [{ name: "Foo", file: "/src/a.tsx" }];
+ assert.throws(
+ () => assertAllowlistFormsSupported(unsupported, new Set(["Foo"])),
+ /unsupported export form/,
+ );
+});
+
+test("assertAllowlistFormsSupported: include=null never throws", () => {
+ const unsupported = [{ name: "Foo", file: "/src/a.tsx" }];
+ assert.doesNotThrow(() => assertAllowlistFormsSupported(unsupported, null));
+});
+
+// ---------------------------------------------------------------------------
+// assertKnownComponentNames
+// ---------------------------------------------------------------------------
+
+test("assertKnownComponentNames: allowlisted typo (unknown name) throws", () => {
+ const targets = [{ name: "Foo", file: "/src/a.tsx" }];
+ const unsupported: { name: string; file: string }[] = [];
+ assert.throws(
+ () => assertKnownComponentNames(targets, new Set(["Typo"]), unsupported),
+ /Unknown component/,
+ );
+});
+
+test("assertKnownComponentNames: allowlisted name in unsupported does not throw here", () => {
+ const targets: { name: string; file: string }[] = [];
+ const unsupported = [{ name: "Foo", file: "/src/a.tsx" }];
+ assert.doesNotThrow(() =>
+ assertKnownComponentNames(targets, new Set(["Foo"]), unsupported),
+ );
+});
+
+test("assertKnownComponentNames: include=null never throws", () => {
+ const targets = [{ name: "Foo", file: "/src/a.tsx" }];
+ const unsupported: { name: string; file: string }[] = [];
+ assert.doesNotThrow(() =>
+ assertKnownComponentNames(targets, null, unsupported),
+ );
+});
diff --git a/plugins/autoRegisterComponents.ts b/plugins/autoRegisterComponents.ts
new file mode 100644
index 00000000000..ca6e630d90f
--- /dev/null
+++ b/plugins/autoRegisterComponents.ts
@@ -0,0 +1,683 @@
+import fs from "fs";
+import MagicString from "magic-string";
+import path from "path";
+import ts from "typescript";
+import type { Plugin } from "vite";
+import { normalizePath } from "vite";
+
+const REGISTER_IMPORT = "@/lib/override";
+const REGISTER_ALIAS = "__careRegisterComponent";
+
+interface Edit {
+ start: number;
+ end: number;
+ text: string;
+}
+
+interface TransformTarget {
+ exportName: string;
+ registeredName: string;
+ isDefault: boolean;
+}
+
+interface ComponentTarget {
+ name: string;
+ file: string;
+}
+
+interface AutoRegisterComponentsOptions {
+ include?: ReadonlySet
| null;
+}
+
+function shouldRegisterComponent(
+ name: string,
+ include: ReadonlySet | null | undefined,
+) {
+ return !include || include.has(name);
+}
+
+function isPascalCase(name: string) {
+ return /^[A-Z][A-Za-z0-9_]*$/.test(name);
+}
+
+function hasModifier(
+ node: ts.Node,
+ kind: ts.SyntaxKind.ExportKeyword | ts.SyntaxKind.DefaultKeyword,
+) {
+ return (
+ (ts.canHaveModifiers(node) &&
+ ts.getModifiers(node)?.some((modifier) => modifier.kind === kind)) ??
+ false
+ );
+}
+
+function containsJsx(node: ts.Node): boolean {
+ let found = false;
+
+ function visit(current: ts.Node) {
+ if (found) {
+ return;
+ }
+
+ switch (current.kind) {
+ case ts.SyntaxKind.JsxElement:
+ case ts.SyntaxKind.JsxSelfClosingElement:
+ case ts.SyntaxKind.JsxFragment:
+ found = true;
+ return;
+ default:
+ ts.forEachChild(current, visit);
+ }
+ }
+
+ visit(node);
+ return found;
+}
+
+function isComponentFunction(node: ts.FunctionDeclaration) {
+ return !!node.body && containsJsx(node.body);
+}
+
+function isRefOrMemoCall(node: ts.CallExpression): boolean {
+ const callee = node.expression;
+ if (ts.isIdentifier(callee)) {
+ return callee.text === "forwardRef" || callee.text === "memo";
+ }
+ if (ts.isPropertyAccessExpression(callee)) {
+ return callee.name.text === "forwardRef" || callee.name.text === "memo";
+ }
+ return false;
+}
+
+function isComponentInitializer(node: ts.Expression | undefined): boolean {
+ if (!node) {
+ return false;
+ }
+
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
+ return containsJsx(node.body);
+ }
+
+ if (ts.isCallExpression(node)) {
+ if (isRefOrMemoCall(node)) {
+ return false;
+ }
+ return containsJsx(node);
+ }
+
+ return false;
+}
+
+function uniqueName(source: string, preferred: string) {
+ let name = preferred;
+ let index = 2;
+
+ while (new RegExp(`\\b${name}\\b`).test(source)) {
+ name = `${preferred}${index}`;
+ index += 1;
+ }
+
+ return name;
+}
+
+function removeExportAndDefaultModifiers(
+ sourceFile: ts.SourceFile,
+ node: ts.Node,
+ edits: Edit[],
+) {
+ if (!ts.canHaveModifiers(node)) {
+ return;
+ }
+
+ for (const modifier of ts.getModifiers(node) ?? []) {
+ if (
+ modifier.kind !== ts.SyntaxKind.ExportKeyword &&
+ modifier.kind !== ts.SyntaxKind.DefaultKeyword
+ ) {
+ continue;
+ }
+
+ let end = modifier.end;
+ while (/\s/.test(sourceFile.text[end] ?? "")) {
+ end += 1;
+ }
+
+ edits.push({ start: modifier.getStart(sourceFile), end, text: "" });
+ }
+}
+
+function appendRegistration(target: TransformTarget) {
+ const registration = `${REGISTER_ALIAS}(${JSON.stringify(
+ target.exportName,
+ )}, ${target.exportName})`;
+ const declaration = `\nconst ${target.registeredName} = ${registration};`;
+
+ if (target.isDefault) {
+ return `${declaration}\nexport default ${target.registeredName};`;
+ }
+
+ return `${declaration}\nexport { ${target.registeredName} as ${target.exportName} };`;
+}
+
+function applyEdits(source: string, id: string, edits: Edit[]) {
+ const code = new MagicString(source, { filename: id });
+
+ for (const edit of edits.sort((left, right) => right.start - left.start)) {
+ if (edit.start === edit.end) {
+ code.appendLeft(edit.start, edit.text);
+ continue;
+ }
+
+ if (edit.text === "") {
+ code.remove(edit.start, edit.end);
+ continue;
+ }
+
+ code.update(edit.start, edit.end, edit.text);
+ }
+
+ return {
+ code: code.toString(),
+ map: code.generateMap({
+ hires: true,
+ includeContent: true,
+ source: id,
+ }),
+ };
+}
+
+function getImportInsertPosition(sourceFile: ts.SourceFile) {
+ let position = 0;
+
+ for (const statement of sourceFile.statements) {
+ if (ts.isImportDeclaration(statement)) {
+ position = statement.end;
+ continue;
+ }
+
+ if (
+ ts.isExpressionStatement(statement) &&
+ ts.isStringLiteral(statement.expression)
+ ) {
+ position = statement.end;
+ continue;
+ }
+
+ break;
+ }
+
+ return position;
+}
+
+function createRegisterImport(sourceFile: ts.SourceFile) {
+ const insertPosition = getImportInsertPosition(sourceFile);
+ const prefix = insertPosition > 0 ? "\n" : "";
+
+ return {
+ start: insertPosition,
+ end: insertPosition,
+ text: `${prefix}import { register as ${REGISTER_ALIAS} } from "${REGISTER_IMPORT}";\n`,
+ };
+}
+
+function findLocalComponentDeclaration(
+ sourceFile: ts.SourceFile,
+ name: string,
+): ts.FunctionDeclaration | ts.VariableDeclaration | null {
+ for (const statement of sourceFile.statements) {
+ if (ts.isFunctionDeclaration(statement) && statement.name?.text === name) {
+ return isComponentFunction(statement) ? statement : null;
+ }
+
+ if (ts.isVariableStatement(statement)) {
+ for (const declaration of statement.declarationList.declarations) {
+ if (
+ !ts.isIdentifier(declaration.name) ||
+ declaration.name.text !== name
+ ) {
+ continue;
+ }
+
+ return isComponentInitializer(declaration.initializer)
+ ? declaration
+ : null;
+ }
+ }
+ }
+
+ return null;
+}
+
+function collectComponentTargets(
+ source: string,
+ id: string,
+): ComponentTarget[] {
+ const sourceFile = ts.createSourceFile(
+ id,
+ source,
+ ts.ScriptTarget.Latest,
+ true,
+ ts.ScriptKind.TSX,
+ );
+ const targets: ComponentTarget[] = [];
+ const localComponents = new Set();
+
+ for (const statement of sourceFile.statements) {
+ if (
+ ts.isFunctionDeclaration(statement) &&
+ statement.name &&
+ isPascalCase(statement.name.text) &&
+ isComponentFunction(statement)
+ ) {
+ localComponents.add(statement.name.text);
+ }
+
+ if (ts.isVariableStatement(statement)) {
+ for (const declaration of statement.declarationList.declarations) {
+ if (
+ ts.isIdentifier(declaration.name) &&
+ isPascalCase(declaration.name.text) &&
+ isComponentInitializer(declaration.initializer)
+ ) {
+ localComponents.add(declaration.name.text);
+ }
+ }
+ }
+ }
+
+ for (const statement of sourceFile.statements) {
+ const isExported = hasModifier(statement, ts.SyntaxKind.ExportKeyword);
+
+ if (
+ isExported &&
+ ts.isFunctionDeclaration(statement) &&
+ statement.name &&
+ localComponents.has(statement.name.text)
+ ) {
+ targets.push({ name: statement.name.text, file: id });
+ continue;
+ }
+
+ if (
+ isExported &&
+ ts.isVariableStatement(statement) &&
+ statement.declarationList.declarations.length === 1
+ ) {
+ const declaration = statement.declarationList.declarations[0];
+
+ if (
+ ts.isIdentifier(declaration.name) &&
+ localComponents.has(declaration.name.text)
+ ) {
+ targets.push({ name: declaration.name.text, file: id });
+ }
+
+ continue;
+ }
+
+ if (
+ ts.isExportAssignment(statement) &&
+ ts.isIdentifier(statement.expression) &&
+ localComponents.has(statement.expression.text)
+ ) {
+ targets.push({ name: statement.expression.text, file: id });
+ }
+ }
+
+ return targets;
+}
+
+export function collectUnsupportedComponentTargets(
+ source: string,
+ id: string,
+): ComponentTarget[] {
+ const sourceFile = ts.createSourceFile(
+ id,
+ source,
+ ts.ScriptTarget.Latest,
+ true,
+ ts.ScriptKind.TSX,
+ );
+ const unsupported: ComponentTarget[] = [];
+
+ for (const statement of sourceFile.statements) {
+ // export { Foo } / export { X as Y } / export { Foo } from "./x" (re-exports)
+ if (
+ ts.isExportDeclaration(statement) &&
+ statement.exportClause &&
+ ts.isNamedExports(statement.exportClause)
+ ) {
+ for (const specifier of statement.exportClause.elements) {
+ const exportedName = specifier.name.text; // Y in "X as Y", Foo in "{ Foo }"
+ if (isPascalCase(exportedName)) {
+ unsupported.push({ name: exportedName, file: id });
+ }
+ }
+ continue;
+ }
+
+ // multi-declarator: export const A = ..., B = ...
+ if (
+ hasModifier(statement, ts.SyntaxKind.ExportKeyword) &&
+ ts.isVariableStatement(statement) &&
+ statement.declarationList.declarations.length > 1
+ ) {
+ for (const declaration of statement.declarationList.declarations) {
+ if (
+ ts.isIdentifier(declaration.name) &&
+ isPascalCase(declaration.name.text)
+ ) {
+ unsupported.push({ name: declaration.name.text, file: id });
+ }
+ }
+ }
+ }
+
+ return unsupported;
+}
+
+function findTsxFiles(root: string): string[] {
+ if (!fs.existsSync(root)) {
+ return [];
+ }
+
+ return fs.readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
+ const filePath = path.join(root, entry.name);
+
+ if (entry.isDirectory()) {
+ return findTsxFiles(filePath);
+ }
+
+ return entry.isFile() && filePath.endsWith(".tsx") ? [filePath] : [];
+ });
+}
+
+function collectRegisteredComponentTargets(
+ srcRoot: string,
+ overrideRoot: string,
+): { targets: ComponentTarget[]; unsupported: ComponentTarget[] } {
+ const targets: ComponentTarget[] = [];
+ const unsupported: ComponentTarget[] = [];
+
+ for (const filePath of findTsxFiles(srcRoot)) {
+ const normalizedPath = normalizePath(filePath);
+
+ if (normalizedPath.startsWith(overrideRoot)) {
+ continue;
+ }
+
+ const source = fs.readFileSync(filePath, "utf8");
+ targets.push(...collectComponentTargets(source, normalizedPath));
+ unsupported.push(
+ ...collectUnsupportedComponentTargets(source, normalizedPath),
+ );
+ }
+
+ return { targets, unsupported };
+}
+
+export function assertUniqueComponentNames(targets: ComponentTarget[]) {
+ const componentFiles = new Map>();
+
+ for (const target of targets) {
+ const files = componentFiles.get(target.name) ?? new Set();
+ files.add(target.file);
+ componentFiles.set(target.name, files);
+ }
+
+ const duplicates = Array.from(componentFiles.entries())
+ .filter(([, files]) => files.size > 1)
+ .sort(([left], [right]) => left.localeCompare(right));
+
+ if (duplicates.length === 0) {
+ return;
+ }
+
+ const message = duplicates
+ .map(([name, files]) =>
+ [
+ `- ${name}`,
+ ...Array.from(files)
+ .sort()
+ .map((file) => ` ${file}`),
+ ].join("\n"),
+ )
+ .join("\n");
+
+ throw new Error(
+ `Duplicate exported component names are not allowed for auto-registration:\n${message}`,
+ );
+}
+
+export function assertAllowlistFormsSupported(
+ unsupported: ComponentTarget[],
+ include: ReadonlySet | null | undefined,
+) {
+ if (!include) {
+ return;
+ }
+
+ const hits = unsupported
+ .filter((target) => include.has(target.name))
+ .sort(
+ (left, right) =>
+ left.name.localeCompare(right.name) ||
+ left.file.localeCompare(right.file),
+ );
+
+ if (hits.length === 0) {
+ return;
+ }
+
+ const list = hits
+ .map((target) => `- ${target.name}\n ${target.file}`)
+ .join("\n");
+
+ throw new Error(
+ [
+ "Components requested in REACT_MFE_REGISTERED_COMPONENTS use an unsupported export form and cannot be auto-registered:",
+ list,
+ "Use 'export function', 'export const X = …' (single declarator), or 'export default'.",
+ ].join("\n"),
+ );
+}
+
+export function assertKnownComponentNames(
+ targets: ComponentTarget[],
+ include: ReadonlySet | null | undefined,
+ unsupported: ComponentTarget[],
+) {
+ if (!include) {
+ return;
+ }
+
+ const knownNames = new Set(targets.map((target) => target.name));
+ const unsupportedNames = new Set(unsupported.map((t) => t.name));
+ const unknownNames = Array.from(include)
+ .filter((name) => !knownNames.has(name) && !unsupportedNames.has(name))
+ .sort((left, right) => left.localeCompare(right));
+
+ if (unknownNames.length === 0) {
+ return;
+ }
+
+ throw new Error(
+ [
+ "Unknown component names in REACT_MFE_REGISTERED_COMPONENTS:",
+ ...unknownNames.map((name) => `- ${name}`),
+ ].join("\n"),
+ );
+}
+
+export function transformSource(
+ source: string,
+ id: string,
+ include: ReadonlySet | null | undefined,
+) {
+ if (!source.includes("export")) {
+ return null;
+ }
+
+ const sourceFile = ts.createSourceFile(
+ id,
+ source,
+ ts.ScriptTarget.Latest,
+ true,
+ ts.ScriptKind.TSX,
+ );
+
+ const edits: Edit[] = [];
+ const transformedNames = new Set();
+
+ for (const statement of sourceFile.statements) {
+ const isExported = hasModifier(statement, ts.SyntaxKind.ExportKeyword);
+ const isDefault = hasModifier(statement, ts.SyntaxKind.DefaultKeyword);
+
+ if (
+ isExported &&
+ ts.isFunctionDeclaration(statement) &&
+ statement.name &&
+ isPascalCase(statement.name.text) &&
+ isComponentFunction(statement)
+ ) {
+ const exportName = statement.name.text;
+ if (!shouldRegisterComponent(exportName, include)) {
+ continue;
+ }
+
+ const registeredName = uniqueName(source, `${exportName}Registered`);
+
+ removeExportAndDefaultModifiers(sourceFile, statement, edits);
+ edits.push({
+ start: statement.end,
+ end: statement.end,
+ text: appendRegistration({
+ exportName,
+ registeredName,
+ isDefault,
+ }),
+ });
+ transformedNames.add(exportName);
+ continue;
+ }
+
+ if (
+ isExported &&
+ ts.isVariableStatement(statement) &&
+ statement.declarationList.declarations.length === 1
+ ) {
+ const declaration = statement.declarationList.declarations[0];
+
+ if (
+ !ts.isIdentifier(declaration.name) ||
+ !isPascalCase(declaration.name.text) ||
+ !isComponentInitializer(declaration.initializer)
+ ) {
+ continue;
+ }
+
+ const exportName = declaration.name.text;
+ if (!shouldRegisterComponent(exportName, include)) {
+ continue;
+ }
+
+ const registeredName = uniqueName(source, `${exportName}Registered`);
+
+ removeExportAndDefaultModifiers(sourceFile, statement, edits);
+ edits.push({
+ start: statement.end,
+ end: statement.end,
+ text: appendRegistration({
+ exportName,
+ registeredName,
+ isDefault: false,
+ }),
+ });
+ transformedNames.add(exportName);
+ continue;
+ }
+
+ if (
+ ts.isExportAssignment(statement) &&
+ ts.isIdentifier(statement.expression) &&
+ isPascalCase(statement.expression.text) &&
+ !transformedNames.has(statement.expression.text)
+ ) {
+ const exportName = statement.expression.text;
+ if (!shouldRegisterComponent(exportName, include)) {
+ continue;
+ }
+
+ const declaration = findLocalComponentDeclaration(sourceFile, exportName);
+
+ if (!declaration) {
+ continue;
+ }
+
+ const registeredName = uniqueName(source, `${exportName}Registered`);
+ edits.push({
+ start: statement.getStart(sourceFile),
+ end: statement.end,
+ text: `const ${registeredName} = ${REGISTER_ALIAS}(${JSON.stringify(
+ exportName,
+ )}, ${exportName});\nexport default ${registeredName};`,
+ });
+ transformedNames.add(exportName);
+ }
+ }
+
+ if (edits.length === 0) {
+ return null;
+ }
+
+ edits.push(createRegisterImport(sourceFile));
+ return applyEdits(source, id, edits);
+}
+
+export function autoRegisterComponents({
+ include = null,
+}: AutoRegisterComponentsOptions = {}): Plugin {
+ let srcRoot = "";
+ let overrideRoot = "";
+
+ return {
+ name: "auto-register-components",
+ enforce: "pre",
+ configResolved(config) {
+ srcRoot = `${normalizePath(path.resolve(config.root, "src"))}/`;
+ overrideRoot = `${normalizePath(
+ path.resolve(config.root, "src/lib/override"),
+ )}/`;
+ },
+ buildStart() {
+ const { targets, unsupported } = collectRegisteredComponentTargets(
+ srcRoot,
+ overrideRoot,
+ );
+ assertUniqueComponentNames(targets);
+ assertAllowlistFormsSupported(unsupported, include);
+ assertKnownComponentNames(targets, include, unsupported);
+ },
+ transform(source, id) {
+ const normalizedId = normalizePath(id.split("?")[0]);
+
+ if (
+ !normalizedId.endsWith(".tsx") ||
+ !normalizedId.startsWith(srcRoot) ||
+ normalizedId.startsWith(overrideRoot)
+ ) {
+ return null;
+ }
+
+ const transformed = transformSource(source, normalizedId, include);
+ if (!transformed) {
+ return null;
+ }
+
+ return {
+ code: transformed.code,
+ map: transformed.map,
+ };
+ },
+ };
+}
diff --git a/src/Routers/PatientRouter.tsx b/src/Routers/PatientRouter.tsx
index f7fe0b89413..b23c34fb025 100644
--- a/src/Routers/PatientRouter.tsx
+++ b/src/Routers/PatientRouter.tsx
@@ -15,7 +15,7 @@ import useSidebarState from "@/hooks/useSidebarState";
import PatientUserProvider from "@/Providers/PatientUserProvider";
import { FacilitiesPage } from "@/pages/Facility/FacilitiesPage";
import PatientIndex from "@/pages/Patient/index";
-import { PatientRegistration } from "@/pages/PublicAppointments/PatientRegistration";
+import PublicPatientRegistration from "@/pages/PublicAppointments/PatientRegistration";
import PatientSelect from "@/pages/PublicAppointments/PatientSelect";
import { ScheduleAppointment } from "@/pages/PublicAppointments/Schedule";
import { AppointmentSuccess } from "@/pages/PublicAppointments/Success";
@@ -78,7 +78,7 @@ const AppointmentRoutes = {
}: {
facilityId: string;
staffId: string;
- }) => ,
+ }) => ,
};
export default function PatientRouter() {
diff --git a/src/components/Auth/AuthHero.tsx b/src/components/Auth/AuthHero.tsx
index 081e3d2fe2d..ae8601b7635 100644
--- a/src/components/Auth/AuthHero.tsx
+++ b/src/components/Auth/AuthHero.tsx
@@ -2,9 +2,7 @@ import careConfig from "@careConfig";
import { Link } from "raviger";
import { useTranslation } from "react-i18next";
-import { register } from "@/lib/override";
-
-const AuthHeroBase = () => {
+export const AuthHero = () => {
const { urls, stateLogo, customLogo, customLogoAlt } = careConfig;
const customDescriptionHtml = __CUSTOM_DESCRIPTION_HTML__;
const { t } = useTranslation();
@@ -119,5 +117,3 @@ const AuthHeroBase = () => {
);
};
-
-export const AuthHero = register("AuthHero", AuthHeroBase);
diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx
index 2c4ca7eed20..f0c2d1d63c5 100644
--- a/src/components/Auth/Login.tsx
+++ b/src/components/Auth/Login.tsx
@@ -49,7 +49,6 @@ import otpApi from "@/types/otp/otpApi";
import { clearQueryPersistenceCache } from "@/Utils/request/queryClient";
import { invalidateAllPaymentReconcilationLocationCaches } from "@/atoms/paymentReconcilationLocationAtom";
import { clearQueuePractitionerCache } from "@/atoms/queuePractitionerAtom";
-import { register } from "@/lib/override";
import { AuthHero } from "./AuthHero";
interface OtpLoginData {
@@ -807,4 +806,4 @@ const Login = (props: LoginProps) => {
);
};
-export default register("Login", Login);
+export default Login;
diff --git a/src/components/CareTeam/CareTeamSheet.tsx b/src/components/CareTeam/CareTeamSheet.tsx
index 9bbee3c3b74..ea68daad9b4 100644
--- a/src/components/CareTeam/CareTeamSheet.tsx
+++ b/src/components/CareTeam/CareTeamSheet.tsx
@@ -40,7 +40,7 @@ type CareTeamSheetProps = {
setOpen?: (open: boolean) => void;
};
-export function EmptyState() {
+export function CareTeamEmptyState() {
const { t } = useTranslation();
return (
@@ -252,7 +252,7 @@ export function CareTeamSheet({
{encounter.care_team.length === 0 ? (
-
+
) : (
encounter.care_team.map((member, index) => (
{
const prevTitle = document.title;
if (title) {
diff --git a/src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx b/src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx
index 48caa8df2ef..57c121ff4f2 100644
--- a/src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx
+++ b/src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx
@@ -96,7 +96,7 @@ export function PrintAllQuestionnaireResponses({
-
@@ -113,7 +113,7 @@ export function PrintAllQuestionnaireResponses({
{questionnaireResponses?.results?.map(
(item: QuestionnaireResponse) => (
),
)}
@@ -150,7 +150,7 @@ interface EncounterDetailsProps {
patient?: PatientRead;
}
-export function EncounterDetails({
+export function PrintableEncounterDetails({
encounter,
patient,
}: EncounterDetailsProps) {
@@ -331,7 +331,7 @@ interface ResponseCardProps {
item?: QuestionnaireResponse;
}
-export function ResponseCard({ item }: ResponseCardProps) {
+export function PrintableResponseCard({ item }: ResponseCardProps) {
const { t } = useTranslation();
if (!item) return null;
diff --git a/src/components/Facility/ConsultationDetails/PrintQuestionnaireResponse.tsx b/src/components/Facility/ConsultationDetails/PrintQuestionnaireResponse.tsx
index 9b540edada8..7bb538a3aec 100644
--- a/src/components/Facility/ConsultationDetails/PrintQuestionnaireResponse.tsx
+++ b/src/components/Facility/ConsultationDetails/PrintQuestionnaireResponse.tsx
@@ -4,8 +4,8 @@ import { useTranslation } from "react-i18next";
import PrintPreview from "@/CAREUI/misc/PrintPreview";
import {
- EncounterDetails,
- ResponseCard,
+ PrintableEncounterDetails as EncounterDetails,
+ PrintableResponseCard as ResponseCard,
} from "@/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses";
import query from "@/Utils/request/query";
diff --git a/src/components/Medicine/MedicationAdministration/AdministrationTab.tsx b/src/components/Medicine/MedicationAdministration/AdministrationTab.tsx
index 123a108ba04..74464ea1e6b 100644
--- a/src/components/Medicine/MedicationAdministration/AdministrationTab.tsx
+++ b/src/components/Medicine/MedicationAdministration/AdministrationTab.tsx
@@ -17,7 +17,7 @@ import { Label } from "@/components/ui/label";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { TableSkeleton } from "@/components/Common/SkeletonLoading";
-import { EmptyState } from "@/components/Medicine/MedicationRequestTable";
+import { MedicationRequestEmptyState as EmptyState } from "@/components/Medicine/MedicationRequestTable";
import mutate from "@/Utils/request/mutate";
import query from "@/Utils/request/query";
diff --git a/src/components/Medicine/MedicationRequestTable/index.tsx b/src/components/Medicine/MedicationRequestTable/index.tsx
index 2e0f33be21b..ca5d58f4e22 100644
--- a/src/components/Medicine/MedicationRequestTable/index.tsx
+++ b/src/components/Medicine/MedicationRequestTable/index.tsx
@@ -14,19 +14,19 @@ import { MedicationStatementList } from "@/components/Patient/MedicationStatemen
import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
-interface EmptyStateProps {
+interface MedicationRequestEmptyStateProps {
searching?: boolean;
searchQuery?: string;
message?: string;
description?: string;
}
-export const EmptyState = ({
+export const MedicationRequestEmptyState = ({
searching,
searchQuery,
message,
description,
-}: EmptyStateProps) => {
+}: MedicationRequestEmptyStateProps) => {
const { t } = useTranslation();
return (
diff --git a/src/components/Patient/Common/EmptyState.tsx b/src/components/Patient/Common/EmptyState.tsx
index c3578bf7335..5a1fab0c52e 100644
--- a/src/components/Patient/Common/EmptyState.tsx
+++ b/src/components/Patient/Common/EmptyState.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next";
import { Skeleton } from "@/components/ui/skeleton";
-export default function EmptyState({
+export default function PatientClinicalEmptyState({
title,
description,
}: {
diff --git a/src/components/Patient/MedicationStatementList.tsx b/src/components/Patient/MedicationStatementList.tsx
index fdea805816c..9450dcab0a6 100644
--- a/src/components/Patient/MedicationStatementList.tsx
+++ b/src/components/Patient/MedicationStatementList.tsx
@@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { TableSkeleton } from "@/components/Common/SkeletonLoading";
-import { EmptyState } from "@/components/Medicine/MedicationRequestTable";
+import { MedicationRequestEmptyState as EmptyState } from "@/components/Medicine/MedicationRequestTable";
import query from "@/Utils/request/query";
import { PaginatedResponse } from "@/Utils/request/types";
diff --git a/src/lib/override/register.ts b/src/lib/override/register.ts
index 568ecff35c6..99fd9126a60 100644
--- a/src/lib/override/register.ts
+++ b/src/lib/override/register.ts
@@ -51,22 +51,25 @@ class OverrideErrorBoundary extends Component<
/**
* register() - Makes a component overrideable
*
- * This is the main API for making components overrideable. Simply wrap your
- * component export with register() and it becomes context-aware and overrideable.
+ * This is the runtime primitive that wraps a component so it becomes
+ * context-aware and overrideable. You normally do NOT call it by hand:
+ * every exported PascalCase component under `src/` is registered
+ * automatically at build time by `plugins/autoRegisterComponents.ts`, which
+ * rewrites the export to call register() for you. Just export the component
+ * normally and its call sites stay unchanged.
*
- * @example
* ```tsx
- * function PatientCard(props: PatientCardProps) {
+ * // You write this:
+ * export function PatientCard(props: PatientCardProps) {
* return
Base Card
;
* }
*
- * export default register("PatientCard", PatientCard);
- * ```
- *
- * Usage remains unchanged:
- * ```tsx
+ * // The build transform emits the registration; usage is unchanged:
*
* ```
+ *
+ * See `docs/care-apps-plugin-overrides.md` ("Supported Export Forms") for the
+ * export shapes the transform handles and the ones it deliberately skips.
*/
export function register
(
key: string,
diff --git a/src/pages/Appointments/BookAppointment/BookAppointmentDetails.tsx b/src/pages/Appointments/BookAppointment/BookAppointmentDetails.tsx
index e6f003e62d4..32316226d9e 100644
--- a/src/pages/Appointments/BookAppointment/BookAppointmentDetails.tsx
+++ b/src/pages/Appointments/BookAppointment/BookAppointmentDetails.tsx
@@ -10,7 +10,6 @@ import { scheduleServiceTypeAtom } from "@/atoms/scheduleServiceTypeAtom";
import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
-import { register } from "@/lib/override";
import { AppointmentSlotPicker } from "@/pages/Appointments/BookAppointment/AppointmentSlotPicker";
import useCurrentFacility from "@/pages/Facility/utils/useCurrentFacility";
import { TagConfig } from "@/types/emr/tagConfig/tagConfig";
@@ -27,7 +26,7 @@ export interface BookAppointmentDetailsProps {
onSuccess?: () => void;
}
-const BookAppointmentDetailsBase = ({
+export const BookAppointmentDetails = ({
patientId,
onSuccess,
}: BookAppointmentDetailsProps) => {
@@ -242,8 +241,3 @@ const BookAppointmentDetailsBase = ({
);
};
-
-export const BookAppointmentDetails = register(
- "BookAppointmentDetails",
- BookAppointmentDetailsBase,
-);
diff --git a/src/pages/Encounters/tabs/overview/quick-actions.tsx b/src/pages/Encounters/tabs/overview/quick-actions.tsx
index 78cb2abb72f..75d770ebea4 100644
--- a/src/pages/Encounters/tabs/overview/quick-actions.tsx
+++ b/src/pages/Encounters/tabs/overview/quick-actions.tsx
@@ -14,7 +14,6 @@ import {
import { ShortcutBadge } from "@/Utils/keyboardShortcutComponents";
-import { register } from "@/lib/override";
import { FormDialog } from "./FormsDialog";
export const QuickActions = (props: React.ComponentProps<"div">) => {
@@ -71,9 +70,7 @@ export const QuickActions = (props: React.ComponentProps<"div">) => {
);
};
-export const QuickAction = register("QuickAction", QuickActionBase);
-
-function QuickActionBase({
+export function QuickAction({
icon,
title,
actionId,
@@ -87,11 +84,10 @@ function QuickActionBase({
title: string;
actionId?: string;
href?: string;
- props?: React.ComponentProps<"div">;
basePath?: string;
onClick?: () => void;
hidden?: boolean;
-}) {
+} & React.ComponentProps<"button">) {
const className = cn(
"flex-1 flex flex-row md:flex-col gap-1.25 p-1 pb-2 rounded-lg shadow bg-white",
hidden && "hidden",
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/account.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/account.tsx
index ccf0dbb6eeb..9fc1da09570 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/account.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/account.tsx
@@ -17,7 +17,7 @@ import accountApi from "@/types/billing/account/accountApi";
import { cn } from "@/lib/utils";
import { toast } from "sonner";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const Account = () => {
const { t } = useTranslation();
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/department-and-team.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/department-and-team.tsx
index f2e53d0afe3..177cdf8219b 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/department-and-team.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/department-and-team.tsx
@@ -8,7 +8,7 @@ import { CardListSkeleton } from "@/components/Common/SkeletonLoading";
import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const DepartmentsAndTeams = () => {
const { t } = useTranslation();
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/discharge-summary.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/discharge-summary.tsx
index e6f8343d65a..aef8fe9594a 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/discharge-summary.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/discharge-summary.tsx
@@ -17,7 +17,7 @@ import { CardListSkeleton } from "@/components/Common/SkeletonLoading";
import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const DischargeDetails = () => {
const { t } = useTranslation();
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/empty-state.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/empty-state.tsx
index 3058735b1f1..a657caf9d9e 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/empty-state.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/empty-state.tsx
@@ -1,6 +1,6 @@
import CareIcon from "@/CAREUI/icons/CareIcon";
-export const EmptyState = ({ message }: { message: string }) => (
+export const SummaryPanelEmptyState = ({ message }: { message: string }) => (
{message}
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/encounter-tags.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/encounter-tags.tsx
index db2422ba837..1f3dd83eeb3 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/encounter-tags.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/encounter-tags.tsx
@@ -6,7 +6,7 @@ import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
import { useQueryClient } from "@tanstack/react-query";
import { SquarePen } from "lucide-react";
import { useTranslation } from "react-i18next";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const EncounterTags = () => {
const { canWriteSelectedEncounter: canEdit, selectedEncounter: encounter } =
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/locations.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/locations.tsx
index 34590a9283e..16ddfc2a3d5 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/locations.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/locations.tsx
@@ -8,7 +8,7 @@ import { LocationTree } from "@/components/Location/LocationTree";
import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const Locations = () => {
const { t } = useTranslation();
diff --git a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/manage-care-team.tsx b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/manage-care-team.tsx
index e080c18768f..e31025611fe 100644
--- a/src/pages/Encounters/tabs/overview/summary-panel-details-tab/manage-care-team.tsx
+++ b/src/pages/Encounters/tabs/overview/summary-panel-details-tab/manage-care-team.tsx
@@ -12,7 +12,7 @@ import { useEncounter } from "@/pages/Encounters/utils/EncounterProvider";
import { formatName } from "@/Utils/utils";
-import { EmptyState } from "./empty-state";
+import { SummaryPanelEmptyState as EmptyState } from "./empty-state";
export const ManageCareTeam = () => {
const { t } = useTranslation();
diff --git a/src/pages/Facility/FacilityDetailsPage.tsx b/src/pages/Facility/FacilityDetailsPage.tsx
index 08decff311b..a5ea5401760 100644
--- a/src/pages/Facility/FacilityDetailsPage.tsx
+++ b/src/pages/Facility/FacilityDetailsPage.tsx
@@ -21,7 +21,7 @@ import publicFacilityApi from "@/types/facility/publicFacilityApi";
import { goBack } from "@/Utils/utils";
import { Button } from "@/components/ui/button";
import { FeatureBadge } from "./Utils";
-import { UserCard } from "./components/UserCard";
+import { FacilityUserCard as UserCard } from "./components/UserCard";
interface Props {
id: string;
diff --git a/src/pages/Facility/components/UserCard.tsx b/src/pages/Facility/components/UserCard.tsx
index 1ae440955d1..11907b11100 100644
--- a/src/pages/Facility/components/UserCard.tsx
+++ b/src/pages/Facility/components/UserCard.tsx
@@ -22,7 +22,7 @@ interface Props {
facilityId: string;
}
-export function UserCard({ user, className, facilityId }: Props) {
+export function FacilityUserCard({ user, className, facilityId }: Props) {
const { t } = useTranslation();
const { patientToken: tokenData } = useAuthContext();
diff --git a/src/pages/Facility/settings/activityDefinition/ActivityDefinitionList.tsx b/src/pages/Facility/settings/activityDefinition/ActivityDefinitionList.tsx
index abaea50c050..2090b578340 100644
--- a/src/pages/Facility/settings/activityDefinition/ActivityDefinitionList.tsx
+++ b/src/pages/Facility/settings/activityDefinition/ActivityDefinitionList.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import Page from "@/components/Common/Page";
import { ResourceCategoryList } from "@/components/Common/ResourceCategoryList";
-import { ActivityDefinitionList as ActivityDefinitionListComponent } from "@/pages/Facility/settings/activityDefinition/ActivityDefinitionListComponent";
+import { ActivityDefinitionListContent } from "@/pages/Facility/settings/activityDefinition/ActivityDefinitionListComponent";
import { ResourceCategoryResourceType } from "@/types/base/resourceCategory/resourceCategory";
import { Status } from "@/types/emr/activityDefinition/activityDefinition";
import activityDefinitionApi from "@/types/emr/activityDefinition/activityDefinitionApi";
@@ -58,7 +58,7 @@ export default function ActivityDefinitionList({
}}
>
{categorySlug && (
-
void;
}
-export function ActivityDefinitionList({
+export function ActivityDefinitionListContent({
facilityId,
categorySlug,
setAllowCategoryCreate,
diff --git a/src/pages/Facility/settings/healthcareService/HealthcareServiceShow.tsx b/src/pages/Facility/settings/healthcareService/HealthcareServiceShow.tsx
index 0301a99433d..470e08f9e6a 100644
--- a/src/pages/Facility/settings/healthcareService/HealthcareServiceShow.tsx
+++ b/src/pages/Facility/settings/healthcareService/HealthcareServiceShow.tsx
@@ -31,7 +31,7 @@ import queryClient from "@/Utils/request/queryClient";
type DuoToneIconName = keyof typeof duoToneIcons;
-export default function HealthcareServiceShow({
+export default function SettingsHealthcareServiceShow({
facilityId,
healthcareServiceId,
}: {
diff --git a/src/pages/Facility/settings/locations/LocationSettings.tsx b/src/pages/Facility/settings/locations/LocationSettings.tsx
index f84d5370636..1b0a5f94ba7 100644
--- a/src/pages/Facility/settings/locations/LocationSettings.tsx
+++ b/src/pages/Facility/settings/locations/LocationSettings.tsx
@@ -33,7 +33,7 @@ import locationApi from "@/types/location/locationApi";
import LocationMap from "./LocationMap";
import LocationSheet from "./LocationSheet";
import LocationView from "./LocationView";
-import { LocationCard } from "./components/LocationCard";
+import { SettingsLocationCard as LocationCard } from "./components/LocationCard";
import { LocationTable } from "./components/LocationTable";
interface LocationSettingsProps {
diff --git a/src/pages/Facility/settings/locations/LocationSheet.tsx b/src/pages/Facility/settings/locations/LocationSheet.tsx
index 1a881c20e05..9fb6a21dea0 100644
--- a/src/pages/Facility/settings/locations/LocationSheet.tsx
+++ b/src/pages/Facility/settings/locations/LocationSheet.tsx
@@ -20,7 +20,7 @@ interface Props {
parentId?: string;
}
-export default function LocationSheet({
+export default function SettingsLocationSheet({
open,
onOpenChange,
facilityId,
diff --git a/src/pages/Facility/settings/locations/LocationView.tsx b/src/pages/Facility/settings/locations/LocationView.tsx
index 6cf967ed547..9a150365125 100644
--- a/src/pages/Facility/settings/locations/LocationView.tsx
+++ b/src/pages/Facility/settings/locations/LocationView.tsx
@@ -34,7 +34,7 @@ import { LocationRead } from "@/types/location/location";
import locationApi from "@/types/location/locationApi";
import LocationSheet from "./LocationSheet";
-import { LocationCard } from "./components/LocationCard";
+import { SettingsLocationCard as LocationCard } from "./components/LocationCard";
import { LocationTable } from "./components/LocationTable";
interface Props {
diff --git a/src/pages/Facility/settings/locations/components/LocationCard.tsx b/src/pages/Facility/settings/locations/components/LocationCard.tsx
index 5780d2dd13b..8d0fe7ab0c8 100644
--- a/src/pages/Facility/settings/locations/components/LocationCard.tsx
+++ b/src/pages/Facility/settings/locations/components/LocationCard.tsx
@@ -47,7 +47,7 @@ interface Props {
setPage?: (page: number) => void;
}
-export function LocationCard({
+export function SettingsLocationCard({
location,
onEdit,
onView,
diff --git a/src/pages/Facility/settings/organizations/components/EditFacilityUserRoleSheet.tsx b/src/pages/Facility/settings/organizations/components/EditFacilityUserRoleSheet.tsx
index 2ac4be57d5d..ebdf9388e35 100644
--- a/src/pages/Facility/settings/organizations/components/EditFacilityUserRoleSheet.tsx
+++ b/src/pages/Facility/settings/organizations/components/EditFacilityUserRoleSheet.tsx
@@ -32,7 +32,7 @@ interface Props {
trigger?: React.ReactNode;
}
-export default function EditUserRoleSheet({
+export default function EditFacilityUserRoleSheet({
facilityId,
organizationId,
userRole,
diff --git a/src/pages/Facility/settings/productKnowledge/ProductKnowledgeList.tsx b/src/pages/Facility/settings/productKnowledge/ProductKnowledgeList.tsx
index 0f8d8b0fe0d..283fc674daa 100644
--- a/src/pages/Facility/settings/productKnowledge/ProductKnowledgeList.tsx
+++ b/src/pages/Facility/settings/productKnowledge/ProductKnowledgeList.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import Page from "@/components/Common/Page";
import { ResourceCategoryList } from "@/components/Common/ResourceCategoryList";
-import { ProductKnowledgeList as ProductKnowledgeListComponent } from "@/pages/Facility/settings/productKnowledge/ProductKnowledgeListComponent";
+import { ProductKnowledgeListContent } from "@/pages/Facility/settings/productKnowledge/ProductKnowledgeListComponent";
import { ResourceCategoryResourceType } from "@/types/base/resourceCategory/resourceCategory";
import { ProductKnowledgeStatus } from "@/types/inventory/productKnowledge/productKnowledge";
import productKnowledgeApi from "@/types/inventory/productKnowledge/productKnowledgeApi";
@@ -60,7 +60,7 @@ export default function ProductKnowledgeList({
}}
>
{categorySlug && (
- void;
}
-export function ProductKnowledgeList({
+export function ProductKnowledgeListContent({
facilityId,
categorySlug,
setAllowCategoryCreate,
diff --git a/src/pages/Patient/History/MedicationHistory.tsx b/src/pages/Patient/History/MedicationHistory.tsx
index 280c38fcda3..414581bd8e5 100644
--- a/src/pages/Patient/History/MedicationHistory.tsx
+++ b/src/pages/Patient/History/MedicationHistory.tsx
@@ -9,7 +9,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { TableSkeleton } from "@/components/Common/SkeletonLoading";
import { AdministrationTab } from "@/components/Medicine/MedicationAdministration/AdministrationTab";
-import { EmptyState } from "@/components/Medicine/MedicationRequestTable";
+import { MedicationRequestEmptyState as EmptyState } from "@/components/Medicine/MedicationRequestTable";
import { MedicationsTable } from "@/components/Medicine/MedicationsTable";
import { MedicationStatementList } from "@/components/Patient/MedicationStatementList";
diff --git a/src/pages/Patient/index.tsx b/src/pages/Patient/index.tsx
index f79dedd5d64..86838d559af 100644
--- a/src/pages/Patient/index.tsx
+++ b/src/pages/Patient/index.tsx
@@ -24,7 +24,7 @@ import {
import AppointmentDialog from "./components/AppointmentDialog";
-function PatientIndex() {
+function PatientPortalIndex() {
const { t } = useTranslation();
const [selectedAppointment, setSelectedAppointment] = useState<
@@ -205,4 +205,4 @@ function PatientIndex() {
);
}
-export default PatientIndex;
+export default PatientPortalIndex;
diff --git a/src/pages/PublicAppointments/PatientRegistration.tsx b/src/pages/PublicAppointments/PatientRegistration.tsx
index 3b119c5973f..62a6936456a 100644
--- a/src/pages/PublicAppointments/PatientRegistration.tsx
+++ b/src/pages/PublicAppointments/PatientRegistration.tsx
@@ -40,7 +40,9 @@ type PatientRegistrationProps = {
staffId: string;
};
-export function PatientRegistration(props: PatientRegistrationProps) {
+export default function PublicPatientRegistration(
+ props: PatientRegistrationProps,
+) {
const { staffId } = props;
const { t } = useTranslation();
const [{ slotId, reason }] = useQueryParams();
diff --git a/vite.config.mts b/vite.config.mts
index 481094d2d17..435a3f61c80 100644
--- a/vite.config.mts
+++ b/vite.config.mts
@@ -17,6 +17,7 @@ import { marked } from "marked";
import path from "path";
import checker from "vite-plugin-checker";
import { VitePWA } from "vite-plugin-pwa";
+import { autoRegisterComponents } from "./plugins/autoRegisterComponents";
import { careConsoleArt } from "./plugins/careConsoleArt";
import { fixSonnerPackageJson } from "./plugins/fixSonnerPackageJson";
import { treeShakeCareIcons } from "./plugins/treeShakeCareIcons";
@@ -101,6 +102,23 @@ function getMimeType(filePath: string) {
}
}
+function parseRegisteredComponentNames(value: string | undefined) {
+ if (!value || value.trim() === "") {
+ return new Set();
+ }
+
+ if (value.trim() === "*") {
+ return null;
+ }
+
+ return new Set(
+ value
+ .split(",")
+ .map((name) => name.trim())
+ .filter(Boolean),
+ );
+}
+
function isPluginManifestPath(rootDir: string, filePath: string) {
const normalizedFilePath = normalizePath(filePath);
const appsPrefix = `${normalizePath(path.join(rootDir, "apps"))}/`;
@@ -412,6 +430,11 @@ export default defineConfig(async ({ mode }): Promise => {
careConsoleArt(),
fixSonnerPackageJson(),
localPluginDevSupport(),
+ autoRegisterComponents({
+ include: parseRegisteredComponentNames(
+ env.REACT_MFE_REGISTERED_COMPONENTS,
+ ),
+ }),
tailwindcss(),
federation({
name: "core",