@primocaredentgroup/prescriptions-component 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/dist/_internal/client/PrescriptionsComponentClient.d.ts +25 -0
  4. package/dist/_internal/client/PrescriptionsComponentClient.d.ts.map +1 -0
  5. package/dist/_internal/client/PrescriptionsComponentClient.js +61 -0
  6. package/dist/_internal/client/PrescriptionsComponentClient.js.map +1 -0
  7. package/dist/_internal/client/index.d.ts +3 -0
  8. package/dist/_internal/client/index.d.ts.map +1 -0
  9. package/dist/_internal/client/index.js +2 -0
  10. package/dist/_internal/client/index.js.map +1 -0
  11. package/dist/_internal/client/types.d.ts +47 -0
  12. package/dist/_internal/client/types.d.ts.map +1 -0
  13. package/dist/_internal/client/types.js +2 -0
  14. package/dist/_internal/client/types.js.map +1 -0
  15. package/dist/_internal/component/convex.config.d.ts +7 -0
  16. package/dist/_internal/component/convex.config.d.ts.map +1 -0
  17. package/dist/_internal/component/convex.config.js +8 -0
  18. package/dist/_internal/component/convex.config.js.map +1 -0
  19. package/dist/_internal/component/functions.d.ts +9 -0
  20. package/dist/_internal/component/functions.d.ts.map +1 -0
  21. package/dist/_internal/component/functions.js +11 -0
  22. package/dist/_internal/component/functions.js.map +1 -0
  23. package/dist/_internal/component/index.d.ts +18 -0
  24. package/dist/_internal/component/index.d.ts.map +1 -0
  25. package/dist/_internal/component/index.js +20 -0
  26. package/dist/_internal/component/index.js.map +1 -0
  27. package/dist/_internal/component/schema.d.ts +2 -0
  28. package/dist/_internal/component/schema.d.ts.map +1 -0
  29. package/dist/_internal/component/schema.js +2 -0
  30. package/dist/_internal/component/schema.js.map +1 -0
  31. package/dist/_internal/react/PrescriptionsWidget.d.ts +18 -0
  32. package/dist/_internal/react/PrescriptionsWidget.d.ts.map +1 -0
  33. package/dist/_internal/react/PrescriptionsWidget.js +137 -0
  34. package/dist/_internal/react/PrescriptionsWidget.js.map +1 -0
  35. package/dist/_internal/react/index.d.ts +3 -0
  36. package/dist/_internal/react/index.d.ts.map +1 -0
  37. package/dist/_internal/react/index.js +2 -0
  38. package/dist/_internal/react/index.js.map +1 -0
  39. package/dist/client/index.d.ts +1 -0
  40. package/dist/client/index.js +1 -0
  41. package/dist/component/convex.config.d.ts +2 -0
  42. package/dist/component/convex.config.js +2 -0
  43. package/dist/component/index.d.ts +1 -0
  44. package/dist/component/index.js +1 -0
  45. package/dist/convex/lib/auth.d.ts +7 -0
  46. package/dist/convex/lib/auth.d.ts.map +1 -0
  47. package/dist/convex/lib/auth.js +38 -0
  48. package/dist/convex/lib/auth.js.map +1 -0
  49. package/dist/convex/lib/clinicalNormalize.d.ts +13 -0
  50. package/dist/convex/lib/clinicalNormalize.d.ts.map +1 -0
  51. package/dist/convex/lib/clinicalNormalize.js +82 -0
  52. package/dist/convex/lib/clinicalNormalize.js.map +1 -0
  53. package/dist/convex/lib/dental.d.ts +13 -0
  54. package/dist/convex/lib/dental.d.ts.map +1 -0
  55. package/dist/convex/lib/dental.js +79 -0
  56. package/dist/convex/lib/dental.js.map +1 -0
  57. package/dist/convex/lib/dynamicFieldsStrict.d.ts +9 -0
  58. package/dist/convex/lib/dynamicFieldsStrict.d.ts.map +1 -0
  59. package/dist/convex/lib/dynamicFieldsStrict.js +65 -0
  60. package/dist/convex/lib/dynamicFieldsStrict.js.map +1 -0
  61. package/dist/convex/lib/dynamicRules.d.ts +61 -0
  62. package/dist/convex/lib/dynamicRules.d.ts.map +1 -0
  63. package/dist/convex/lib/dynamicRules.js +221 -0
  64. package/dist/convex/lib/dynamicRules.js.map +1 -0
  65. package/dist/convex/lib/storage.d.ts +59 -0
  66. package/dist/convex/lib/storage.d.ts.map +1 -0
  67. package/dist/convex/lib/storage.js +120 -0
  68. package/dist/convex/lib/storage.js.map +1 -0
  69. package/dist/convex/lib/utils.d.ts +61 -0
  70. package/dist/convex/lib/utils.d.ts.map +1 -0
  71. package/dist/convex/lib/utils.js +135 -0
  72. package/dist/convex/lib/utils.js.map +1 -0
  73. package/dist/convex/lib/validation.d.ts +24 -0
  74. package/dist/convex/lib/validation.d.ts.map +1 -0
  75. package/dist/convex/lib/validation.js +333 -0
  76. package/dist/convex/lib/validation.js.map +1 -0
  77. package/dist/convex/mutations/digitalAssets.d.ts +54 -0
  78. package/dist/convex/mutations/digitalAssets.d.ts.map +1 -0
  79. package/dist/convex/mutations/digitalAssets.js +297 -0
  80. package/dist/convex/mutations/digitalAssets.js.map +1 -0
  81. package/dist/convex/mutations/operational.d.ts +38 -0
  82. package/dist/convex/mutations/operational.d.ts.map +1 -0
  83. package/dist/convex/mutations/operational.js +226 -0
  84. package/dist/convex/mutations/operational.js.map +1 -0
  85. package/dist/convex/mutations/phases.d.ts +45 -0
  86. package/dist/convex/mutations/phases.d.ts.map +1 -0
  87. package/dist/convex/mutations/phases.js +334 -0
  88. package/dist/convex/mutations/phases.js.map +1 -0
  89. package/dist/convex/mutations/prescriptions.d.ts +191 -0
  90. package/dist/convex/mutations/prescriptions.d.ts.map +1 -0
  91. package/dist/convex/mutations/prescriptions.js +1263 -0
  92. package/dist/convex/mutations/prescriptions.js.map +1 -0
  93. package/dist/convex/mutations/syncJobs.d.ts +37 -0
  94. package/dist/convex/mutations/syncJobs.d.ts.map +1 -0
  95. package/dist/convex/mutations/syncJobs.js +238 -0
  96. package/dist/convex/mutations/syncJobs.js.map +1 -0
  97. package/dist/convex/prescriptions/fields.d.ts +50 -0
  98. package/dist/convex/prescriptions/fields.d.ts.map +1 -0
  99. package/dist/convex/prescriptions/fields.js +242 -0
  100. package/dist/convex/prescriptions/fields.js.map +1 -0
  101. package/dist/convex/queries/dynamicFields.d.ts +27 -0
  102. package/dist/convex/queries/dynamicFields.d.ts.map +1 -0
  103. package/dist/convex/queries/dynamicFields.js +119 -0
  104. package/dist/convex/queries/dynamicFields.js.map +1 -0
  105. package/dist/convex/queries/prescriptions.d.ts +583 -0
  106. package/dist/convex/queries/prescriptions.d.ts.map +1 -0
  107. package/dist/convex/queries/prescriptions.js +208 -0
  108. package/dist/convex/queries/prescriptions.js.map +1 -0
  109. package/dist/convex/schema.d.ts +962 -0
  110. package/dist/convex/schema.d.ts.map +1 -0
  111. package/dist/convex/schema.js +434 -0
  112. package/dist/convex/schema.js.map +1 -0
  113. package/dist/convex/types.d.ts +267 -0
  114. package/dist/convex/types.d.ts.map +1 -0
  115. package/dist/convex/types.js +2 -0
  116. package/dist/convex/types.js.map +1 -0
  117. package/dist/react/index.d.ts +1 -0
  118. package/dist/react/index.js +1 -0
  119. package/dist/react/styles.css +54 -0
  120. package/package.json +82 -0
@@ -0,0 +1,7 @@
1
+ import type { QueryCtx, MutationCtx } from "../_generated/server";
2
+ import type { Doc } from "../_generated/dataModel";
3
+ type Ctx = QueryCtx | MutationCtx;
4
+ export declare function requireIdentityUser(ctx: Ctx): Promise<Doc<"users">>;
5
+ export declare function assertPrescriptionAccess(user: Doc<"users">, prescription: Doc<"prescriptions">): void;
6
+ export {};
7
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../convex/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,KAAK,GAAG,GAAG,QAAQ,GAAG,WAAW,CAAC;AAYlC,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAqBzE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,eAAe,CAAC,GAAG,IAAI,CAOrG"}
@@ -0,0 +1,38 @@
1
+ function extractIdentityEmail(identity) {
2
+ if (typeof identity.email === "string" && identity.email.length > 0)
3
+ return identity.email;
4
+ const claims = identity["claims"];
5
+ if (claims && typeof claims === "object") {
6
+ const email = claims["email"];
7
+ if (typeof email === "string" && email.length > 0)
8
+ return email;
9
+ }
10
+ return undefined;
11
+ }
12
+ export async function requireIdentityUser(ctx) {
13
+ const identity = await ctx.auth.getUserIdentity();
14
+ if (!identity) {
15
+ throw new Error("Unauthorized: token mancante o non valido");
16
+ }
17
+ const email = extractIdentityEmail(identity);
18
+ if (!email) {
19
+ throw new Error("Unauthorized: claim email non presente nel token");
20
+ }
21
+ const user = await ctx.db
22
+ .query("users")
23
+ .withIndex("by_email", (q) => q.eq("email", email))
24
+ .first();
25
+ if (!user || user.deleted_at || !user.active) {
26
+ throw new Error("Forbidden: utente non autorizzato");
27
+ }
28
+ return user;
29
+ }
30
+ export function assertPrescriptionAccess(user, prescription) {
31
+ const isBuilderAdmin = user.adminRole === "admin" || user.adminRole === "superadmin";
32
+ if (isBuilderAdmin)
33
+ return;
34
+ if (user.clinicId !== prescription.clinicId) {
35
+ throw new Error("Forbidden: prescrizione non accessibile per questa clinic");
36
+ }
37
+ }
38
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../convex/lib/auth.ts"],"names":[],"mappings":"AAKA,SAAS,oBAAoB,CAAC,QAAoD;IAChF,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC;IAC3F,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,KAAK,GAAI,MAAkC,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAQ;IAChD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;IAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,KAAK,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,EAAE;SACtB,KAAK,CAAC,OAAO,CAAC;SACd,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;SAClD,KAAK,EAAE,CAAC;IAEX,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAAkB,EAAE,YAAkC;IAC7F,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,KAAK,OAAO,IAAI,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC;IACrF,IAAI,cAAc;QAAE,OAAO;IAE3B,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ApplicationType } from "../types";
2
+ export type ClinicalDraftInputPatch = {
3
+ prostheticService?: {
4
+ serviceCode?: string;
5
+ serviceLabel?: string;
6
+ };
7
+ application?: ApplicationType | null;
8
+ shade?: string | null;
9
+ notePreparazione?: string | null;
10
+ lineaMargine?: string | null;
11
+ };
12
+ export declare function normalizeClinicalDraftInput(input: ClinicalDraftInputPatch): ClinicalDraftInputPatch;
13
+ //# sourceMappingURL=clinicalNormalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clinicalNormalize.d.ts","sourceRoot":"","sources":["../../../convex/lib/clinicalNormalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAGhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,iBAAiB,CAAC,EAAE;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAiDF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,uBAAuB,GAAG,uBAAuB,CAoCnG"}
@@ -0,0 +1,82 @@
1
+ import { sortTeethByArchPosition } from "./dental";
2
+ function hasOwn(obj, key) {
3
+ return Object.prototype.hasOwnProperty.call(obj, key);
4
+ }
5
+ function normalizeText(value) {
6
+ if (value === null)
7
+ return null;
8
+ if (value === undefined)
9
+ return undefined;
10
+ const trimmed = value.trim();
11
+ return trimmed.length > 0 ? trimmed : null;
12
+ }
13
+ function normalizeApplication(input) {
14
+ switch (input.type) {
15
+ case "TOOTH":
16
+ return { type: "TOOTH", toothNumber: Math.trunc(input.toothNumber) };
17
+ case "MULTI_TOOTH": {
18
+ const unique = Array.from(new Set(input.teeth.map((t) => Math.trunc(t))));
19
+ return { type: "MULTI_TOOTH", teeth: sortTeethByArchPosition(unique) };
20
+ }
21
+ case "BRIDGE": {
22
+ const byTooth = new Map();
23
+ for (const element of input.teeth) {
24
+ const normalizedTooth = Math.trunc(element.toothNumber);
25
+ if (!byTooth.has(normalizedTooth)) {
26
+ byTooth.set(normalizedTooth, element.role);
27
+ }
28
+ }
29
+ const sortedTeeth = sortTeethByArchPosition(Array.from(byTooth.keys()));
30
+ return {
31
+ type: "BRIDGE",
32
+ teeth: sortedTeeth.map((toothNumber) => ({
33
+ toothNumber,
34
+ role: byTooth.get(toothNumber) ?? "pontic",
35
+ })),
36
+ };
37
+ }
38
+ case "QUADRANT":
39
+ return { type: "QUADRANT", quadrant: Math.trunc(input.quadrant) };
40
+ case "SEXTANT":
41
+ return { type: "SEXTANT", sextant: Math.trunc(input.sextant) };
42
+ case "ARCH":
43
+ return { type: "ARCH", arch: input.arch };
44
+ case "FULL_MOUTH":
45
+ return { type: "FULL_MOUTH" };
46
+ }
47
+ }
48
+ export function normalizeClinicalDraftInput(input) {
49
+ const normalized = {};
50
+ if (input.prostheticService) {
51
+ const serviceCode = normalizeText(input.prostheticService.serviceCode);
52
+ const serviceLabel = normalizeText(input.prostheticService.serviceLabel);
53
+ if (serviceCode !== undefined || serviceLabel !== undefined) {
54
+ normalized.prostheticService = {};
55
+ if (serviceCode !== null) {
56
+ normalized.prostheticService.serviceCode = serviceCode;
57
+ }
58
+ if (serviceLabel !== null) {
59
+ normalized.prostheticService.serviceLabel = serviceLabel;
60
+ }
61
+ }
62
+ }
63
+ if (hasOwn(input, "application")) {
64
+ if (input.application === null) {
65
+ normalized.application = null;
66
+ }
67
+ else if (input.application !== undefined) {
68
+ normalized.application = normalizeApplication(input.application);
69
+ }
70
+ }
71
+ if (hasOwn(input, "shade")) {
72
+ normalized.shade = normalizeText(input.shade);
73
+ }
74
+ if (hasOwn(input, "notePreparazione")) {
75
+ normalized.notePreparazione = normalizeText(input.notePreparazione);
76
+ }
77
+ if (hasOwn(input, "lineaMargine")) {
78
+ normalized.lineaMargine = normalizeText(input.lineaMargine);
79
+ }
80
+ return normalized;
81
+ }
82
+ //# sourceMappingURL=clinicalNormalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clinicalNormalize.js","sourceRoot":"","sources":["../../../convex/lib/clinicalNormalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Ordine fisico dell'arcata superiore */
2
+ export declare const UPPER_ARCH_ORDER: number[];
3
+ /** Ordine fisico dell'arcata inferiore */
4
+ export declare const LOWER_ARCH_ORDER: number[];
5
+ /** Tutti i numeri dente validi nella numerazione FDI */
6
+ export declare const ALL_VALID_TEETH: number[];
7
+ export declare function isValidToothNumber(n: number): boolean;
8
+ export declare function getArchForTooth(tooth: number): "upper" | "lower";
9
+ export declare function areTeethAdjacent(a: number, b: number): boolean;
10
+ export declare function sortTeethByArchPosition(teeth: number[]): number[];
11
+ export declare function findAdjacentGroups(teeth: number[]): number[][];
12
+ export declare function canFormBridge(teeth: number[]): boolean;
13
+ //# sourceMappingURL=dental.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dental.d.ts","sourceRoot":"","sources":["../../../convex/lib/dental.ts"],"names":[],"mappings":"AAOA,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,UAAmE,CAAC;AAEjG,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,UAAmE,CAAC;AAEjG,wDAAwD;AACxD,eAAO,MAAM,eAAe,UAA6C,CAAC;AAE1E,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAGhE;AAWD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAM9D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CA0B9D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAOtD"}
@@ -0,0 +1,79 @@
1
+ // ============================================
2
+ // DENTAL ADJACENCY - Logica adiacenza FDI (Backend)
3
+ // ============================================
4
+ // Funzioni pure per determinare se denti sono adiacenti
5
+ // nell'arcata dentale secondo la numerazione FDI.
6
+ // Usate dalla validazione server-side.
7
+ /** Ordine fisico dell'arcata superiore */
8
+ export const UPPER_ARCH_ORDER = [18, 17, 16, 15, 14, 13, 12, 11, 21, 22, 23, 24, 25, 26, 27, 28];
9
+ /** Ordine fisico dell'arcata inferiore */
10
+ export const LOWER_ARCH_ORDER = [48, 47, 46, 45, 44, 43, 42, 41, 31, 32, 33, 34, 35, 36, 37, 38];
11
+ /** Tutti i numeri dente validi nella numerazione FDI */
12
+ export const ALL_VALID_TEETH = [...UPPER_ARCH_ORDER, ...LOWER_ARCH_ORDER];
13
+ export function isValidToothNumber(n) {
14
+ return ALL_VALID_TEETH.includes(n);
15
+ }
16
+ export function getArchForTooth(tooth) {
17
+ const quadrant = Math.floor(tooth / 10);
18
+ return quadrant <= 2 ? "upper" : "lower";
19
+ }
20
+ function getArchOrder(tooth) {
21
+ return getArchForTooth(tooth) === "upper" ? UPPER_ARCH_ORDER : LOWER_ARCH_ORDER;
22
+ }
23
+ function getArchPosition(tooth) {
24
+ const order = getArchOrder(tooth);
25
+ return order.indexOf(tooth);
26
+ }
27
+ export function areTeethAdjacent(a, b) {
28
+ if (!isValidToothNumber(a) || !isValidToothNumber(b))
29
+ return false;
30
+ if (getArchForTooth(a) !== getArchForTooth(b))
31
+ return false;
32
+ const posA = getArchPosition(a);
33
+ const posB = getArchPosition(b);
34
+ return Math.abs(posA - posB) === 1;
35
+ }
36
+ export function sortTeethByArchPosition(teeth) {
37
+ if (teeth.length === 0)
38
+ return [];
39
+ const order = getArchOrder(teeth[0]);
40
+ return [...teeth].sort((a, b) => order.indexOf(a) - order.indexOf(b));
41
+ }
42
+ export function findAdjacentGroups(teeth) {
43
+ if (teeth.length === 0)
44
+ return [];
45
+ if (teeth.length === 1)
46
+ return [[teeth[0]]];
47
+ const upperTeeth = teeth.filter((t) => getArchForTooth(t) === "upper");
48
+ const lowerTeeth = teeth.filter((t) => getArchForTooth(t) === "lower");
49
+ const groups = [];
50
+ for (const archTeeth of [upperTeeth, lowerTeeth]) {
51
+ if (archTeeth.length === 0)
52
+ continue;
53
+ const sorted = sortTeethByArchPosition(archTeeth);
54
+ let currentGroup = [sorted[0]];
55
+ for (let i = 1; i < sorted.length; i++) {
56
+ if (areTeethAdjacent(sorted[i - 1], sorted[i])) {
57
+ currentGroup.push(sorted[i]);
58
+ }
59
+ else {
60
+ groups.push(currentGroup);
61
+ currentGroup = [sorted[i]];
62
+ }
63
+ }
64
+ groups.push(currentGroup);
65
+ }
66
+ return groups;
67
+ }
68
+ export function canFormBridge(teeth) {
69
+ if (teeth.length < 2)
70
+ return false;
71
+ if (!teeth.every(isValidToothNumber))
72
+ return false;
73
+ const arch = getArchForTooth(teeth[0]);
74
+ if (!teeth.every((t) => getArchForTooth(t) === arch))
75
+ return false;
76
+ const groups = findAdjacentGroups(teeth);
77
+ return groups.length === 1 && groups[0].length === teeth.length;
78
+ }
79
+ //# sourceMappingURL=dental.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dental.js","sourceRoot":"","sources":["../../../convex/lib/dental.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { MutationCtx, QueryCtx } from "../_generated/server";
2
+ import type { Doc } from "../_generated/dataModel";
3
+ import { type RuntimeEvaluationResult } from "./dynamicRules";
4
+ type Ctx = MutationCtx | QueryCtx;
5
+ export declare function computeDynamicEvaluation(ctx: Ctx, prescription: Doc<"prescriptions">): Promise<RuntimeEvaluationResult | null>;
6
+ export declare function assertDynamicRequiredComplete(ctx: Ctx, prescription: Doc<"prescriptions">): Promise<void>;
7
+ export declare const computeDynamicEvaluationForPrescription: typeof computeDynamicEvaluation;
8
+ export {};
9
+ //# sourceMappingURL=dynamicFieldsStrict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamicFieldsStrict.d.ts","sourceRoot":"","sources":["../../../convex/lib/dynamicFieldsStrict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAEnD,OAAO,EAA8D,KAAK,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAE1H,KAAK,GAAG,GAAG,WAAW,GAAG,QAAQ,CAAC;AAsClC,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,GAAG,CAAC,eAAe,CAAC,GACjC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAUzC;AAED,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,GAAG,CAAC,eAAe,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAoBf;AAGD,eAAO,MAAM,uCAAuC,iCAA2B,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { ConvexError } from "convex/values";
2
+ import { evaluateDynamicRules, parseRulesetSnapshot } from "./dynamicRules";
3
+ function parseValuesByFieldId(valuesByFieldId) {
4
+ if (!valuesByFieldId)
5
+ return {};
6
+ try {
7
+ return JSON.parse(valuesByFieldId);
8
+ }
9
+ catch {
10
+ return {};
11
+ }
12
+ }
13
+ async function buildPhaseData(ctx, prescriptionId) {
14
+ const phaseInstances = await ctx.db
15
+ .query("phaseInstances")
16
+ .withIndex("by_prescriptionRef", (q) => q.eq("prescriptionRef", prescriptionId))
17
+ .collect();
18
+ const phaseData = {};
19
+ const latestByPhaseType = new Map();
20
+ for (const phase of phaseInstances) {
21
+ const current = latestByPhaseType.get(phase.phaseTypeKey);
22
+ if (!current || phase.ordinal > current.ordinal) {
23
+ latestByPhaseType.set(phase.phaseTypeKey, {
24
+ ordinal: phase.ordinal,
25
+ status: phase.status,
26
+ });
27
+ }
28
+ }
29
+ for (const [phaseTypeKey, data] of latestByPhaseType.entries()) {
30
+ phaseData[phaseTypeKey] = data.status;
31
+ }
32
+ return phaseData;
33
+ }
34
+ export async function computeDynamicEvaluation(ctx, prescription) {
35
+ if (!prescription.rulesetVersionId)
36
+ return null;
37
+ const rulesetVersion = await ctx.db.get(prescription.rulesetVersionId);
38
+ const parsedRules = parseRulesetSnapshot(rulesetVersion?.rulesJsonSnapshot);
39
+ if (!parsedRules)
40
+ return null;
41
+ const fieldValues = parseValuesByFieldId(prescription.valuesByFieldId);
42
+ const phaseData = await buildPhaseData(ctx, prescription._id);
43
+ return evaluateDynamicRules(parsedRules, fieldValues, phaseData);
44
+ }
45
+ export async function assertDynamicRequiredComplete(ctx, prescription) {
46
+ const evaluation = await computeDynamicEvaluation(ctx, prescription);
47
+ if (!evaluation)
48
+ return;
49
+ // Semantica strict runtime:
50
+ // blocca soltanto i required mancanti che il motore dichiara anche
51
+ // visibili e non disabilitati (required AND visible AND NOT disabled).
52
+ const visibleSet = new Set(evaluation.visibleFieldIds);
53
+ const disabledSet = new Set(evaluation.disabledFieldIds);
54
+ const blockingMissing = evaluation.missingRequiredFieldIds.filter((fieldId) => visibleSet.has(fieldId) && !disabledSet.has(fieldId));
55
+ if (blockingMissing.length > 0) {
56
+ throw new ConvexError({
57
+ code: "MISSING_REQUIRED_DYNAMIC_FIELDS",
58
+ message: "Campi dinamici obbligatori mancanti",
59
+ fieldIds: blockingMissing,
60
+ });
61
+ }
62
+ }
63
+ // Alias retrocompatibile per chiamanti introdotti prima del rename.
64
+ export const computeDynamicEvaluationForPrescription = computeDynamicEvaluation;
65
+ //# sourceMappingURL=dynamicFieldsStrict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamicFieldsStrict.js","sourceRoot":"","sources":["../../../convex/lib/dynamicFieldsStrict.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ export interface RuntimeStep {
2
+ id: string;
3
+ title: string;
4
+ order: number;
5
+ visibility?: {
6
+ type?: string;
7
+ conditionId?: string;
8
+ };
9
+ }
10
+ export interface RuntimeField {
11
+ id: string;
12
+ stepId: string;
13
+ required?: {
14
+ type?: string;
15
+ conditionId?: string;
16
+ };
17
+ }
18
+ export interface RuntimeConditionRule {
19
+ ruleType?: "field" | "phase_status";
20
+ fieldId?: string;
21
+ phaseTypeKey?: string;
22
+ op: string;
23
+ value?: unknown;
24
+ }
25
+ export interface RuntimeConditionAction {
26
+ type: "show_field" | "hide_field" | "require_field" | "show_step" | "hide_step" | "show_modal" | "set_field_value" | "disable_field";
27
+ targetFieldId?: string;
28
+ targetStepId?: string;
29
+ value?: unknown;
30
+ modalTitle?: string;
31
+ modalMessage?: string;
32
+ }
33
+ export interface RuntimeCondition {
34
+ id: string;
35
+ operator: "AND" | "OR";
36
+ rules: RuntimeConditionRule[];
37
+ actions?: RuntimeConditionAction[];
38
+ }
39
+ export interface RuntimeBuilderState {
40
+ schemaVersion: number;
41
+ steps: RuntimeStep[];
42
+ fields: RuntimeField[];
43
+ conditions: RuntimeCondition[];
44
+ }
45
+ export interface RuntimeEvaluationResult {
46
+ visibleStepIds: string[];
47
+ visibleFieldIds: string[];
48
+ requiredFieldIds: string[];
49
+ disabledFieldIds: string[];
50
+ fieldValueOverrides: Record<string, unknown>;
51
+ activeModals: {
52
+ title: string;
53
+ message: string;
54
+ }[];
55
+ missingRequiredFieldIds: string[];
56
+ conditionResults: Record<string, boolean>;
57
+ }
58
+ export type PhaseData = Record<string, string>;
59
+ export declare function parseRulesetSnapshot(rulesJsonSnapshot: string | undefined): RuntimeBuilderState | null;
60
+ export declare function evaluateDynamicRules(state: RuntimeBuilderState, fieldValues: Record<string, unknown>, phaseData?: PhaseData): RuntimeEvaluationResult;
61
+ //# sourceMappingURL=dynamicRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamicRules.d.ts","sourceRoot":"","sources":["../../../convex/lib/dynamicRules.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EACA,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,WAAW,GACX,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,eAAe,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,KAAK,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,sBAAsB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnD,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C,wBAAgB,oBAAoB,CAClC,iBAAiB,EAAE,MAAM,GAAG,SAAS,GACpC,mBAAmB,GAAG,IAAI,CAmB5B;AAiOD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,mBAAmB,EAC1B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,SAAS,CAAC,EAAE,SAAS,GACpB,uBAAuB,CASzB"}
@@ -0,0 +1,221 @@
1
+ // Backend runtime rules engine for dynamic fields.
2
+ // Keeps parity with frontend behavior for visibility/required/disabled.
3
+ export function parseRulesetSnapshot(rulesJsonSnapshot) {
4
+ if (!rulesJsonSnapshot)
5
+ return null;
6
+ try {
7
+ const parsed = JSON.parse(rulesJsonSnapshot);
8
+ if (!parsed || typeof parsed !== "object")
9
+ return null;
10
+ const steps = Array.isArray(parsed.steps) ? parsed.steps : [];
11
+ const fields = Array.isArray(parsed.fields) ? parsed.fields : [];
12
+ const conditions = Array.isArray(parsed.conditions) ? parsed.conditions : [];
13
+ const schemaVersion = typeof parsed.schemaVersion === "number" ? parsed.schemaVersion : 1;
14
+ return {
15
+ schemaVersion,
16
+ steps: steps,
17
+ fields: fields,
18
+ conditions: conditions,
19
+ };
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ function evaluateComparison(actual, op, expected) {
26
+ switch (op) {
27
+ case "exists":
28
+ return actual !== undefined && actual !== "" && actual !== null;
29
+ case "eq":
30
+ // eslint-disable-next-line eqeqeq
31
+ return actual == expected;
32
+ case "neq":
33
+ // eslint-disable-next-line eqeqeq
34
+ return actual != expected;
35
+ case "gt":
36
+ return typeof actual === "number" && typeof expected === "number" && actual > expected;
37
+ case "lt":
38
+ return typeof actual === "number" && typeof expected === "number" && actual < expected;
39
+ case "gte":
40
+ return typeof actual === "number" && typeof expected === "number" && actual >= expected;
41
+ case "lte":
42
+ return typeof actual === "number" && typeof expected === "number" && actual <= expected;
43
+ case "contains":
44
+ return typeof actual === "string" && typeof expected === "string" && actual.includes(expected);
45
+ case "in":
46
+ return Array.isArray(expected) && expected.includes(actual);
47
+ default:
48
+ return false;
49
+ }
50
+ }
51
+ function evaluateRule(rule, fieldValues, phaseData) {
52
+ if (rule.ruleType === "phase_status" && rule.phaseTypeKey) {
53
+ return evaluateComparison(phaseData?.[rule.phaseTypeKey], rule.op, rule.value);
54
+ }
55
+ if (!rule.fieldId)
56
+ return false;
57
+ return evaluateComparison(fieldValues[rule.fieldId], rule.op, rule.value);
58
+ }
59
+ function evaluateCondition(condition, fieldValues, phaseData) {
60
+ if (!condition.rules || condition.rules.length === 0)
61
+ return false;
62
+ const ruleResults = condition.rules.map((r) => evaluateRule(r, fieldValues, phaseData));
63
+ return condition.operator === "AND" ? ruleResults.every(Boolean) : ruleResults.some(Boolean);
64
+ }
65
+ function applyAction(action, acc) {
66
+ switch (action.type) {
67
+ case "show_step":
68
+ if (action.targetStepId) {
69
+ acc.shownStepIds.add(action.targetStepId);
70
+ acc.hiddenStepIds.delete(action.targetStepId);
71
+ }
72
+ break;
73
+ case "hide_step":
74
+ if (action.targetStepId) {
75
+ acc.hiddenStepIds.add(action.targetStepId);
76
+ acc.shownStepIds.delete(action.targetStepId);
77
+ }
78
+ break;
79
+ case "show_field":
80
+ if (action.targetFieldId) {
81
+ acc.shownFieldIds.add(action.targetFieldId);
82
+ acc.hiddenFieldIds.delete(action.targetFieldId);
83
+ }
84
+ break;
85
+ case "hide_field":
86
+ if (action.targetFieldId) {
87
+ acc.hiddenFieldIds.add(action.targetFieldId);
88
+ acc.shownFieldIds.delete(action.targetFieldId);
89
+ }
90
+ break;
91
+ case "require_field":
92
+ if (action.targetFieldId)
93
+ acc.requiredFieldIds.add(action.targetFieldId);
94
+ break;
95
+ case "disable_field":
96
+ if (action.targetFieldId)
97
+ acc.disabledFieldIds.add(action.targetFieldId);
98
+ break;
99
+ case "set_field_value":
100
+ if (action.targetFieldId && action.value !== undefined) {
101
+ acc.fieldValueOverrides[action.targetFieldId] = action.value;
102
+ }
103
+ break;
104
+ case "show_modal":
105
+ if (action.modalTitle || action.modalMessage) {
106
+ acc.activeModals.push({
107
+ title: action.modalTitle || "",
108
+ message: action.modalMessage || "",
109
+ });
110
+ }
111
+ break;
112
+ }
113
+ }
114
+ function evaluateV1(state, fieldValues, conditionResults) {
115
+ const visibleStepIds = state.steps
116
+ .filter((step) => {
117
+ if (!step.visibility || step.visibility.type === "always")
118
+ return true;
119
+ if (step.visibility.type === "condition" && step.visibility.conditionId) {
120
+ return Boolean(conditionResults[step.visibility.conditionId]);
121
+ }
122
+ return false;
123
+ })
124
+ .map((s) => s.id);
125
+ const visibleStepSet = new Set(visibleStepIds);
126
+ const visibleFieldIds = state.fields
127
+ .filter((f) => visibleStepSet.has(f.stepId))
128
+ .map((f) => f.id);
129
+ const visibleFieldSet = new Set(visibleFieldIds);
130
+ const requiredFieldIds = [];
131
+ for (const field of state.fields) {
132
+ if (!visibleFieldSet.has(field.id))
133
+ continue;
134
+ if (field.required?.type === "always")
135
+ requiredFieldIds.push(field.id);
136
+ if (field.required?.type === "condition" &&
137
+ field.required.conditionId &&
138
+ conditionResults[field.required.conditionId]) {
139
+ requiredFieldIds.push(field.id);
140
+ }
141
+ }
142
+ const missingRequiredFieldIds = requiredFieldIds.filter((fieldId) => {
143
+ const value = fieldValues[fieldId];
144
+ return value === undefined || value === null || value === "";
145
+ });
146
+ return {
147
+ visibleStepIds,
148
+ visibleFieldIds,
149
+ requiredFieldIds,
150
+ disabledFieldIds: [],
151
+ fieldValueOverrides: {},
152
+ activeModals: [],
153
+ missingRequiredFieldIds,
154
+ conditionResults,
155
+ };
156
+ }
157
+ function evaluateV2(state, fieldValues, conditionResults) {
158
+ const hiddenStepIds = new Set();
159
+ const shownStepIds = new Set();
160
+ const hiddenFieldIds = new Set();
161
+ const shownFieldIds = new Set();
162
+ const requiredFieldIds = new Set();
163
+ const disabledFieldIds = new Set();
164
+ const fieldValueOverrides = {};
165
+ const activeModals = [];
166
+ for (const field of state.fields) {
167
+ if (field.required?.type === "always")
168
+ requiredFieldIds.add(field.id);
169
+ }
170
+ for (const condition of state.conditions) {
171
+ if (!conditionResults[condition.id])
172
+ continue;
173
+ for (const action of condition.actions || []) {
174
+ applyAction(action, {
175
+ hiddenStepIds,
176
+ shownStepIds,
177
+ hiddenFieldIds,
178
+ shownFieldIds,
179
+ requiredFieldIds,
180
+ disabledFieldIds,
181
+ fieldValueOverrides,
182
+ activeModals,
183
+ });
184
+ }
185
+ }
186
+ const visibleStepIds = state.steps
187
+ .filter((s) => !(hiddenStepIds.has(s.id) && !shownStepIds.has(s.id)))
188
+ .map((s) => s.id);
189
+ const visibleStepSet = new Set(visibleStepIds);
190
+ const visibleFieldIds = state.fields
191
+ .filter((f) => visibleStepSet.has(f.stepId) && !(hiddenFieldIds.has(f.id) && !shownFieldIds.has(f.id)))
192
+ .map((f) => f.id);
193
+ const visibleFieldSet = new Set(visibleFieldIds);
194
+ const finalRequired = [...requiredFieldIds].filter((id) => visibleFieldSet.has(id));
195
+ const finalDisabled = [...disabledFieldIds].filter((id) => visibleFieldSet.has(id));
196
+ const missingRequiredFieldIds = finalRequired.filter((fieldId) => {
197
+ const value = fieldValues[fieldId];
198
+ return value === undefined || value === null || value === "";
199
+ });
200
+ return {
201
+ visibleStepIds,
202
+ visibleFieldIds,
203
+ requiredFieldIds: finalRequired,
204
+ disabledFieldIds: finalDisabled,
205
+ fieldValueOverrides,
206
+ activeModals,
207
+ missingRequiredFieldIds,
208
+ conditionResults,
209
+ };
210
+ }
211
+ export function evaluateDynamicRules(state, fieldValues, phaseData) {
212
+ const conditionResults = {};
213
+ for (const condition of state.conditions) {
214
+ conditionResults[condition.id] = evaluateCondition(condition, fieldValues, phaseData);
215
+ }
216
+ if (state.schemaVersion >= 2) {
217
+ return evaluateV2(state, fieldValues, conditionResults);
218
+ }
219
+ return evaluateV1(state, fieldValues, conditionResults);
220
+ }
221
+ //# sourceMappingURL=dynamicRules.js.map