@primocaredentgroup/elettromedicali 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 (197) hide show
  1. package/dist/client/index.d.ts +72 -0
  2. package/dist/client/index.d.ts.map +1 -0
  3. package/dist/client/index.js +233 -0
  4. package/dist/client/index.js.map +1 -0
  5. package/dist/component/_generated/api.d.ts +94 -0
  6. package/dist/component/_generated/api.d.ts.map +1 -0
  7. package/dist/component/_generated/api.js +31 -0
  8. package/dist/component/_generated/api.js.map +1 -0
  9. package/dist/component/_generated/component.d.ts +1444 -0
  10. package/dist/component/_generated/component.d.ts.map +1 -0
  11. package/dist/component/_generated/component.js +11 -0
  12. package/dist/component/_generated/component.js.map +1 -0
  13. package/dist/component/_generated/dataModel.d.ts +46 -0
  14. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  15. package/dist/component/_generated/dataModel.js +11 -0
  16. package/dist/component/_generated/dataModel.js.map +1 -0
  17. package/dist/component/_generated/server.d.ts +121 -0
  18. package/dist/component/_generated/server.d.ts.map +1 -0
  19. package/dist/component/_generated/server.js +78 -0
  20. package/dist/component/_generated/server.js.map +1 -0
  21. package/dist/component/apiKeys.d.ts +69 -0
  22. package/dist/component/apiKeys.d.ts.map +1 -0
  23. package/dist/component/apiKeys.js +207 -0
  24. package/dist/component/apiKeys.js.map +1 -0
  25. package/dist/component/clinics.d.ts +103 -0
  26. package/dist/component/clinics.d.ts.map +1 -0
  27. package/dist/component/clinics.js +126 -0
  28. package/dist/component/clinics.js.map +1 -0
  29. package/dist/component/contracts.d.ts +85 -0
  30. package/dist/component/contracts.d.ts.map +1 -0
  31. package/dist/component/contracts.js +115 -0
  32. package/dist/component/contracts.js.map +1 -0
  33. package/dist/component/convex.config.d.ts +3 -0
  34. package/dist/component/convex.config.d.ts.map +1 -0
  35. package/dist/component/convex.config.js +3 -0
  36. package/dist/component/convex.config.js.map +1 -0
  37. package/dist/component/crons.d.ts +3 -0
  38. package/dist/component/crons.d.ts.map +1 -0
  39. package/dist/component/crons.js +7 -0
  40. package/dist/component/crons.js.map +1 -0
  41. package/dist/component/dashboardStats.d.ts +14 -0
  42. package/dist/component/dashboardStats.d.ts.map +1 -0
  43. package/dist/component/dashboardStats.js +136 -0
  44. package/dist/component/dashboardStats.js.map +1 -0
  45. package/dist/component/dashboardStatsCache.d.ts +32 -0
  46. package/dist/component/dashboardStatsCache.d.ts.map +1 -0
  47. package/dist/component/dashboardStatsCache.js +129 -0
  48. package/dist/component/dashboardStatsCache.js.map +1 -0
  49. package/dist/component/deviceCategories.d.ts +108 -0
  50. package/dist/component/deviceCategories.d.ts.map +1 -0
  51. package/dist/component/deviceCategories.js +254 -0
  52. package/dist/component/deviceCategories.js.map +1 -0
  53. package/dist/component/deviceQuestions.d.ts +129 -0
  54. package/dist/component/deviceQuestions.d.ts.map +1 -0
  55. package/dist/component/deviceQuestions.js +175 -0
  56. package/dist/component/deviceQuestions.js.map +1 -0
  57. package/dist/component/deviceRepairHistory.d.ts +30 -0
  58. package/dist/component/deviceRepairHistory.d.ts.map +1 -0
  59. package/dist/component/deviceRepairHistory.js +84 -0
  60. package/dist/component/deviceRepairHistory.js.map +1 -0
  61. package/dist/component/deviceStatus.d.ts +63 -0
  62. package/dist/component/deviceStatus.d.ts.map +1 -0
  63. package/dist/component/deviceStatus.js +58 -0
  64. package/dist/component/deviceStatus.js.map +1 -0
  65. package/dist/component/devices.d.ts +299 -0
  66. package/dist/component/devices.d.ts.map +1 -0
  67. package/dist/component/devices.js +587 -0
  68. package/dist/component/devices.js.map +1 -0
  69. package/dist/component/emailHelpers.d.ts +17 -0
  70. package/dist/component/emailHelpers.d.ts.map +1 -0
  71. package/dist/component/emailHelpers.js +39 -0
  72. package/dist/component/emailHelpers.js.map +1 -0
  73. package/dist/component/emails.d.ts +56 -0
  74. package/dist/component/emails.d.ts.map +1 -0
  75. package/dist/component/emails.js +58 -0
  76. package/dist/component/emails.js.map +1 -0
  77. package/dist/component/http.d.ts +3 -0
  78. package/dist/component/http.d.ts.map +1 -0
  79. package/dist/component/http.js +229 -0
  80. package/dist/component/http.js.map +1 -0
  81. package/dist/component/maintenanceTasks.d.ts +733 -0
  82. package/dist/component/maintenanceTasks.d.ts.map +1 -0
  83. package/dist/component/maintenanceTasks.js +937 -0
  84. package/dist/component/maintenanceTasks.js.map +1 -0
  85. package/dist/component/roles.d.ts +75 -0
  86. package/dist/component/roles.d.ts.map +1 -0
  87. package/dist/component/roles.js +98 -0
  88. package/dist/component/roles.js.map +1 -0
  89. package/dist/component/schema.d.ts +1295 -0
  90. package/dist/component/schema.d.ts.map +1 -0
  91. package/dist/component/schema.js +724 -0
  92. package/dist/component/schema.js.map +1 -0
  93. package/dist/component/slaMonitoring.d.ts +32 -0
  94. package/dist/component/slaMonitoring.d.ts.map +1 -0
  95. package/dist/component/slaMonitoring.js +111 -0
  96. package/dist/component/slaMonitoring.js.map +1 -0
  97. package/dist/component/slaRules.d.ts +72 -0
  98. package/dist/component/slaRules.d.ts.map +1 -0
  99. package/dist/component/slaRules.js +193 -0
  100. package/dist/component/slaRules.js.map +1 -0
  101. package/dist/component/sparePartOrders.d.ts +177 -0
  102. package/dist/component/sparePartOrders.d.ts.map +1 -0
  103. package/dist/component/sparePartOrders.js +243 -0
  104. package/dist/component/sparePartOrders.js.map +1 -0
  105. package/dist/component/spareParts.d.ts +472 -0
  106. package/dist/component/spareParts.d.ts.map +1 -0
  107. package/dist/component/spareParts.js +319 -0
  108. package/dist/component/spareParts.js.map +1 -0
  109. package/dist/component/supplierCategories.d.ts +22 -0
  110. package/dist/component/supplierCategories.d.ts.map +1 -0
  111. package/dist/component/supplierCategories.js +64 -0
  112. package/dist/component/supplierCategories.js.map +1 -0
  113. package/dist/component/suppliers.d.ts +94 -0
  114. package/dist/component/suppliers.d.ts.map +1 -0
  115. package/dist/component/suppliers.js +195 -0
  116. package/dist/component/suppliers.js.map +1 -0
  117. package/dist/component/ticketComments.d.ts +89 -0
  118. package/dist/component/ticketComments.d.ts.map +1 -0
  119. package/dist/component/ticketComments.js +246 -0
  120. package/dist/component/ticketComments.js.map +1 -0
  121. package/dist/component/ticketCustomFields.d.ts +149 -0
  122. package/dist/component/ticketCustomFields.d.ts.map +1 -0
  123. package/dist/component/ticketCustomFields.js +215 -0
  124. package/dist/component/ticketCustomFields.js.map +1 -0
  125. package/dist/component/ticketExport.d.ts +83 -0
  126. package/dist/component/ticketExport.d.ts.map +1 -0
  127. package/dist/component/ticketExport.js +182 -0
  128. package/dist/component/ticketExport.js.map +1 -0
  129. package/dist/component/ticketHistory.d.ts +57 -0
  130. package/dist/component/ticketHistory.d.ts.map +1 -0
  131. package/dist/component/ticketHistory.js +81 -0
  132. package/dist/component/ticketHistory.js.map +1 -0
  133. package/dist/component/ticketMacros.d.ts +141 -0
  134. package/dist/component/ticketMacros.d.ts.map +1 -0
  135. package/dist/component/ticketMacros.js +255 -0
  136. package/dist/component/ticketMacros.js.map +1 -0
  137. package/dist/component/ticketStatuses.d.ts +60 -0
  138. package/dist/component/ticketStatuses.d.ts.map +1 -0
  139. package/dist/component/ticketStatuses.js +110 -0
  140. package/dist/component/ticketStatuses.js.map +1 -0
  141. package/dist/component/ticketTriggers.d.ts +408 -0
  142. package/dist/component/ticketTriggers.d.ts.map +1 -0
  143. package/dist/component/ticketTriggers.js +941 -0
  144. package/dist/component/ticketTriggers.js.map +1 -0
  145. package/dist/component/userProfiles.d.ts +259 -0
  146. package/dist/component/userProfiles.d.ts.map +1 -0
  147. package/dist/component/userProfiles.js +634 -0
  148. package/dist/component/userProfiles.js.map +1 -0
  149. package/dist/component/vendorArticles.d.ts +64 -0
  150. package/dist/component/vendorArticles.d.ts.map +1 -0
  151. package/dist/component/vendorArticles.js +116 -0
  152. package/dist/component/vendorArticles.js.map +1 -0
  153. package/dist/test.d.ts +1302 -0
  154. package/dist/test.d.ts.map +1 -0
  155. package/dist/test.js +7 -0
  156. package/dist/test.js.map +1 -0
  157. package/package.json +71 -0
  158. package/src/client/index.ts +344 -0
  159. package/src/component/_generated/api.ts +110 -0
  160. package/src/component/_generated/component.ts +2460 -0
  161. package/src/component/_generated/dataModel.ts +60 -0
  162. package/src/component/_generated/server.ts +156 -0
  163. package/src/component/apiKeys.ts +229 -0
  164. package/src/component/clinics.ts +136 -0
  165. package/src/component/contracts.ts +136 -0
  166. package/src/component/convex.config.js +2 -0
  167. package/src/component/convex.config.ts +3 -0
  168. package/src/component/crons.ts +18 -0
  169. package/src/component/dashboardStats.ts +141 -0
  170. package/src/component/dashboardStatsCache.ts +145 -0
  171. package/src/component/deviceCategories.ts +280 -0
  172. package/src/component/deviceQuestions.ts +225 -0
  173. package/src/component/deviceRepairHistory.ts +94 -0
  174. package/src/component/deviceStatus.ts +79 -0
  175. package/src/component/devices.ts +645 -0
  176. package/src/component/emailHelpers.ts +38 -0
  177. package/src/component/emails.ts +61 -0
  178. package/src/component/http.ts +231 -0
  179. package/src/component/maintenanceTasks.ts +1003 -0
  180. package/src/component/roles.ts +99 -0
  181. package/src/component/schema.ts +842 -0
  182. package/src/component/slaMonitoring.ts +125 -0
  183. package/src/component/slaRules.ts +231 -0
  184. package/src/component/sparePartOrders.ts +290 -0
  185. package/src/component/spareParts.ts +362 -0
  186. package/src/component/supplierCategories.ts +65 -0
  187. package/src/component/suppliers.ts +234 -0
  188. package/src/component/ticketComments.ts +288 -0
  189. package/src/component/ticketCustomFields.ts +260 -0
  190. package/src/component/ticketExport.ts +220 -0
  191. package/src/component/ticketHistory.ts +106 -0
  192. package/src/component/ticketMacros.ts +291 -0
  193. package/src/component/ticketStatuses.ts +109 -0
  194. package/src/component/ticketTriggers.ts +1152 -0
  195. package/src/component/userProfiles.ts +745 -0
  196. package/src/component/vendorArticles.ts +139 -0
  197. package/src/test.ts +15 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAKrE,wBAAgB,QAAQ,CACtB,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,EACvD,IAAI,GAAE,MAA0B,QAGjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAED,wBAA6C"}
package/dist/test.js ADDED
@@ -0,0 +1,7 @@
1
+ import schema from "./component/schema.js";
2
+ const modules = import.meta.glob("./component/**/*.ts");
3
+ export function register(t, name = "elettromedicali") {
4
+ t.registerComponent(name, schema, modules);
5
+ }
6
+ export default { register, schema, modules };
7
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAGA,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAE3C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AAExD,MAAM,UAAU,QAAQ,CACtB,CAAuD,EACvD,OAAe,iBAAiB;IAEhC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@primocaredentgroup/elettromedicali",
3
+ "description": "Convex component for medical device management, maintenance ticketing, SLA monitoring, and clinic/supplier management.",
4
+ "repository": "github:primocaredentgroup/elettromedicali_componente",
5
+ "version": "0.1.0",
6
+ "license": "Apache-2.0",
7
+ "keywords": ["convex", "component", "medical-devices", "maintenance", "ticketing"],
8
+ "type": "module",
9
+ "publishConfig": {
10
+ "access": "restricted"
11
+ },
12
+ "scripts": {
13
+ "dev": "npm-run-all --parallel dev:*",
14
+ "dev:backend": "npx convex dev --typecheck-components",
15
+ "dev:frontend": "cd example && npx vite --clearScreen false",
16
+ "dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build:codegen' --initial",
17
+ "predev": "npm run build && npx convex dev --once 2>/dev/null || true",
18
+ "build": "tsc --project ./tsconfig.build.json",
19
+ "build:codegen": "npx convex codegen --component-dir ./src/component && npm run build",
20
+ "build:clean": "rm -rf dist *.tsbuildinfo && npm run build:codegen",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "eslint .",
23
+ "test": "vitest run --typecheck",
24
+ "test:watch": "vitest --typecheck --clearScreen false",
25
+ "prepublishOnly": "npm run build:clean",
26
+ "alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags",
27
+ "release": "npm version patch && npm publish && git push --follow-tags"
28
+ },
29
+ "files": ["dist", "src"],
30
+ "exports": {
31
+ "./package.json": "./package.json",
32
+ ".": {
33
+ "types": "./dist/client/index.d.ts",
34
+ "default": "./dist/client/index.js"
35
+ },
36
+ "./test": "./src/test.ts",
37
+ "./_generated/component.js": {
38
+ "types": "./dist/component/_generated/component.d.ts"
39
+ },
40
+ "./_generated/component": {
41
+ "types": "./dist/component/_generated/component.d.ts"
42
+ },
43
+ "./convex.config.js": {
44
+ "types": "./dist/component/convex.config.d.ts",
45
+ "default": "./dist/component/convex.config.js"
46
+ },
47
+ "./convex.config": {
48
+ "types": "./dist/component/convex.config.d.ts",
49
+ "default": "./dist/component/convex.config.js"
50
+ }
51
+ },
52
+ "peerDependencies": {
53
+ "convex": "^1.33.1"
54
+ },
55
+ "dependencies": {
56
+ "convex-helpers": "^0.1.75"
57
+ },
58
+ "devDependencies": {
59
+ "@edge-runtime/vm": "^5.0.0",
60
+ "@types/node": "^24.12.0",
61
+ "chokidar-cli": "3.0.0",
62
+ "convex": "^1.33.1",
63
+ "convex-test": "^0.0.41",
64
+ "npm-run-all2": "^8.0.4",
65
+ "path-exists-cli": "2.0.0",
66
+ "typescript": "~5.9.3",
67
+ "vitest": "^4.1.0"
68
+ },
69
+ "types": "./dist/client/index.d.ts",
70
+ "module": "./dist/client/index.js"
71
+ }
@@ -0,0 +1,344 @@
1
+ import {
2
+ actionGeneric,
3
+ httpActionGeneric,
4
+ mutationGeneric,
5
+ queryGeneric,
6
+ } from "convex/server";
7
+ import type {
8
+ Auth,
9
+ GenericActionCtx,
10
+ GenericDataModel,
11
+ GenericMutationCtx,
12
+ GenericQueryCtx,
13
+ HttpRouter,
14
+ } from "convex/server";
15
+ import { v } from "convex/values";
16
+ import type { ComponentApi } from "../component/_generated/component.js";
17
+
18
+ type QueryCtx = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">;
19
+ type MutationCtx = Pick<
20
+ GenericMutationCtx<GenericDataModel>,
21
+ "runQuery" | "runMutation"
22
+ >;
23
+ type ActionCtx = Pick<
24
+ GenericActionCtx<GenericDataModel>,
25
+ "runQuery" | "runMutation" | "runAction"
26
+ >;
27
+
28
+ interface UserIdentity {
29
+ auth0Id: string;
30
+ email: string;
31
+ name?: string;
32
+ role?: string;
33
+ clinicId?: string;
34
+ supplierId?: string;
35
+ selectedClinicId?: number;
36
+ }
37
+
38
+ interface ElettromedicaliOptions {
39
+ RESEND_API?: string;
40
+ MAIL_SENDER?: string;
41
+ PRIMOUP_BASE_URL?: string;
42
+ PRIMOUP_USER?: string;
43
+ PRIMOUP_PASSWORD?: string;
44
+ EXTERNAL_API_URL?: string;
45
+ EXTERNAL_API_KEY?: string;
46
+ EXTERNAL_CATEGORY_ID?: string;
47
+ }
48
+
49
+ export class ElettromedicaliClient {
50
+ constructor(
51
+ public component: ComponentApi,
52
+ private options?: ElettromedicaliOptions,
53
+ ) {
54
+ if (!options) {
55
+ this.options = {
56
+ RESEND_API: process.env.RESEND_API,
57
+ MAIL_SENDER: process.env.MAIL_SENDER,
58
+ PRIMOUP_BASE_URL: process.env.PRIMOUP_BASE_URL,
59
+ PRIMOUP_USER: process.env.PRIMOUP_USER,
60
+ PRIMOUP_PASSWORD: process.env.PRIMOUP_PASSWORD,
61
+ EXTERNAL_API_URL: process.env.EXTERNAL_API_URL,
62
+ EXTERNAL_API_KEY: process.env.EXTERNAL_API_KEY,
63
+ EXTERNAL_CATEGORY_ID: process.env.EXTERNAL_CATEGORY_ID,
64
+ };
65
+ }
66
+ }
67
+
68
+ async sendEmail(
69
+ ctx: ActionCtx,
70
+ args: {
71
+ to: string | string[];
72
+ from?: string;
73
+ subject: string;
74
+ html: string;
75
+ ticketId?: string;
76
+ },
77
+ ) {
78
+ const resendApiKey = this.options?.RESEND_API;
79
+ if (!resendApiKey) {
80
+ throw new Error("RESEND_API not configured");
81
+ }
82
+
83
+ const fromEmail =
84
+ args.from || this.options?.MAIL_SENDER || "no-reply@primogroup.it";
85
+
86
+ const response = await fetch("https://api.resend.com/emails", {
87
+ method: "POST",
88
+ headers: {
89
+ Authorization: `Bearer ${resendApiKey}`,
90
+ "Content-Type": "application/json",
91
+ },
92
+ body: JSON.stringify({
93
+ to: args.to,
94
+ from: fromEmail,
95
+ subject: args.subject,
96
+ html: args.html,
97
+ }),
98
+ });
99
+
100
+ if (!response.ok) {
101
+ const errorData = await response.text();
102
+ await ctx.runMutation(this.component.emails.logEmail, {
103
+ from: fromEmail,
104
+ to: args.to,
105
+ subject: args.subject,
106
+ html: args.html,
107
+ ticketId: args.ticketId,
108
+ status: "failed",
109
+ errorMessage: `Resend API error: ${response.status} - ${errorData}`,
110
+ });
111
+ throw new Error(`Resend API error: ${response.status} - ${errorData}`);
112
+ }
113
+
114
+ const result = (await response.json()) as { id: string };
115
+
116
+ await ctx.runMutation(this.component.emails.logEmail, {
117
+ from: fromEmail,
118
+ to: args.to,
119
+ subject: args.subject,
120
+ html: args.html,
121
+ ticketId: args.ticketId,
122
+ status: "sent",
123
+ resendId: result.id,
124
+ });
125
+
126
+ return result;
127
+ }
128
+
129
+ async loginToPrimoUP(ctx: ActionCtx) {
130
+ const baseUrl = this.options?.PRIMOUP_BASE_URL;
131
+ const username = this.options?.PRIMOUP_USER;
132
+ const password = this.options?.PRIMOUP_PASSWORD;
133
+
134
+ if (!baseUrl || !username || !password) {
135
+ throw new Error("PrimoUP credentials not configured");
136
+ }
137
+
138
+ const response = await fetch(`${baseUrl}/api/v2/auth/login`, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/json" },
141
+ body: JSON.stringify({ email: username, password }),
142
+ });
143
+
144
+ if (!response.ok) {
145
+ const errorText = await response.text();
146
+ throw new Error(
147
+ `PrimoUP login failed: ${response.status} - ${errorText}`,
148
+ );
149
+ }
150
+
151
+ const data = (await response.json()) as Record<string, any>;
152
+ const token =
153
+ data.meta?.token || data.token || data.access_token || data.accessToken;
154
+
155
+ if (!token) {
156
+ throw new Error("No token found in PrimoUP response");
157
+ }
158
+
159
+ return {
160
+ token,
161
+ expiresAt: data.expiresAt || data.expires_at,
162
+ refreshToken: data.refreshToken || data.refresh_token,
163
+ };
164
+ }
165
+
166
+ async getUserClinicsFromPrimoUP(
167
+ ctx: ActionCtx,
168
+ args: { email: string; token: string; auth0Id: string },
169
+ ) {
170
+ const baseUrl = this.options?.PRIMOUP_BASE_URL;
171
+ if (!baseUrl) throw new Error("PrimoUP base URL not configured");
172
+
173
+ const response = await fetch(
174
+ `${baseUrl}/api/v2/users/by-email/?email=${encodeURIComponent(args.email)}&include=clinics,clinics.roles,clinics.area_managers`,
175
+ {
176
+ method: "GET",
177
+ headers: {
178
+ "Content-Type": "application/json",
179
+ Authorization: `Bearer ${args.token}`,
180
+ },
181
+ },
182
+ );
183
+
184
+ if (!response.ok) {
185
+ const errorText = await response.text();
186
+ await ctx.runMutation(this.component.userProfiles.logPrimoUPAccess, {
187
+ auth0Id: args.auth0Id,
188
+ action: "get_user_clinics",
189
+ endpoint: "/api/v2/users/by-email",
190
+ success: false,
191
+ errorMessage: `Failed: ${response.status} - ${errorText}`,
192
+ });
193
+ throw new Error(`Failed to fetch user clinics: ${response.status}`);
194
+ }
195
+
196
+ const data = (await response.json()) as Record<string, any>;
197
+
198
+ await ctx.runMutation(this.component.userProfiles.logPrimoUPAccess, {
199
+ auth0Id: args.auth0Id,
200
+ action: "get_user_clinics",
201
+ endpoint: "/api/v2/users/by-email",
202
+ success: true,
203
+ });
204
+
205
+ return data.data;
206
+ }
207
+ }
208
+
209
+ export function exposeApi(
210
+ component: ComponentApi,
211
+ options: {
212
+ auth: (
213
+ ctx: { auth: Auth },
214
+ ) => Promise<UserIdentity | null>;
215
+ },
216
+ ) {
217
+ return {
218
+ // Clinics
219
+ listClinics: queryGeneric({
220
+ args: {},
221
+ handler: async (ctx) => {
222
+ const user = await options.auth(ctx);
223
+ if (!user || user.role !== "admin") throw new Error("Not authorized");
224
+ return await ctx.runQuery(component.clinics.listClinics);
225
+ },
226
+ }),
227
+
228
+ // Devices
229
+ listDevices: queryGeneric({
230
+ args: {
231
+ clinicId: v.optional(v.string()),
232
+ _triggerReload: v.optional(v.number()),
233
+ },
234
+ handler: async (ctx, args) => {
235
+ const user = await options.auth(ctx);
236
+ if (!user) throw new Error("Not authenticated");
237
+ return await ctx.runQuery(component.devices.listDevices, {
238
+ userRole: user.role,
239
+ userClinicId: user.clinicId,
240
+ userSelectedClinicId: user.selectedClinicId,
241
+ clinicId: args.clinicId,
242
+ _triggerReload: args._triggerReload,
243
+ });
244
+ },
245
+ }),
246
+
247
+ // Current user profile
248
+ getCurrentUserProfile: queryGeneric({
249
+ args: {},
250
+ handler: async (ctx) => {
251
+ const identity = await ctx.auth.getUserIdentity();
252
+ if (!identity) return null;
253
+ return await ctx.runQuery(
254
+ component.userProfiles.getCurrentUserProfile,
255
+ { auth0Id: identity.subject },
256
+ );
257
+ },
258
+ }),
259
+
260
+ // Upsert user profile (for login sync)
261
+ upsertUserProfile: mutationGeneric({
262
+ args: {
263
+ auth0Id: v.string(),
264
+ email: v.string(),
265
+ name: v.optional(v.string()),
266
+ roleName: v.optional(v.string()),
267
+ },
268
+ handler: async (ctx, args) => {
269
+ return await ctx.runMutation(
270
+ component.userProfiles.upsertUserProfile,
271
+ args,
272
+ );
273
+ },
274
+ }),
275
+
276
+ // Tickets
277
+ listMaintenanceTasks: queryGeneric({
278
+ args: {},
279
+ handler: async (ctx) => {
280
+ const user = await options.auth(ctx);
281
+ if (!user) throw new Error("Not authenticated");
282
+ return await ctx.runQuery(
283
+ component.maintenanceTasks.listMaintenanceTasks,
284
+ {
285
+ userRole: user.role,
286
+ userAuth0Id: user.auth0Id,
287
+ userSupplierId: user.supplierId,
288
+ userClinicId: user.clinicId,
289
+ userSelectedClinicId: user.selectedClinicId,
290
+ },
291
+ );
292
+ },
293
+ }),
294
+
295
+ // Suppliers
296
+ listSuppliers: queryGeneric({
297
+ args: {},
298
+ handler: async (ctx) => {
299
+ const user = await options.auth(ctx);
300
+ if (!user || user.role !== "admin") throw new Error("Not authorized");
301
+ return await ctx.runQuery(component.suppliers.listSuppliers);
302
+ },
303
+ }),
304
+
305
+ // Dashboard stats
306
+ getAdminDashboardStats: queryGeneric({
307
+ args: {},
308
+ handler: async (ctx) => {
309
+ const user = await options.auth(ctx);
310
+ if (!user || user.role !== "admin") throw new Error("Not authorized");
311
+ return await ctx.runQuery(
312
+ component.dashboardStats.getAdminDashboardStats,
313
+ );
314
+ },
315
+ }),
316
+ };
317
+ }
318
+
319
+ export function registerRoutes(
320
+ http: HttpRouter,
321
+ component: ComponentApi,
322
+ { pathPrefix = "/elettromedicali" }: { pathPrefix?: string } = {},
323
+ ) {
324
+ http.route({
325
+ path: `${pathPrefix}/health`,
326
+ method: "GET",
327
+ handler: httpActionGeneric(async (ctx) => {
328
+ return new Response(
329
+ JSON.stringify({
330
+ success: true,
331
+ status: "healthy",
332
+ timestamp: new Date().toISOString(),
333
+ component: "elettromedicali",
334
+ }),
335
+ {
336
+ status: 200,
337
+ headers: { "Content-Type": "application/json" },
338
+ },
339
+ );
340
+ }),
341
+ });
342
+ }
343
+
344
+ export type { ComponentApi, UserIdentity, ElettromedicaliOptions };
@@ -0,0 +1,110 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated `api` utility.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+
11
+ import type * as apiKeys from "../apiKeys.js";
12
+ import type * as clinics from "../clinics.js";
13
+ import type * as contracts from "../contracts.js";
14
+ import type * as crons from "../crons.js";
15
+ import type * as dashboardStats from "../dashboardStats.js";
16
+ import type * as dashboardStatsCache from "../dashboardStatsCache.js";
17
+ import type * as deviceCategories from "../deviceCategories.js";
18
+ import type * as deviceQuestions from "../deviceQuestions.js";
19
+ import type * as deviceRepairHistory from "../deviceRepairHistory.js";
20
+ import type * as deviceStatus from "../deviceStatus.js";
21
+ import type * as devices from "../devices.js";
22
+ import type * as emailHelpers from "../emailHelpers.js";
23
+ import type * as emails from "../emails.js";
24
+ import type * as http from "../http.js";
25
+ import type * as maintenanceTasks from "../maintenanceTasks.js";
26
+ import type * as roles from "../roles.js";
27
+ import type * as slaMonitoring from "../slaMonitoring.js";
28
+ import type * as slaRules from "../slaRules.js";
29
+ import type * as sparePartOrders from "../sparePartOrders.js";
30
+ import type * as spareParts from "../spareParts.js";
31
+ import type * as supplierCategories from "../supplierCategories.js";
32
+ import type * as suppliers from "../suppliers.js";
33
+ import type * as ticketComments from "../ticketComments.js";
34
+ import type * as ticketCustomFields from "../ticketCustomFields.js";
35
+ import type * as ticketExport from "../ticketExport.js";
36
+ import type * as ticketHistory from "../ticketHistory.js";
37
+ import type * as ticketMacros from "../ticketMacros.js";
38
+ import type * as ticketStatuses from "../ticketStatuses.js";
39
+ import type * as ticketTriggers from "../ticketTriggers.js";
40
+ import type * as userProfiles from "../userProfiles.js";
41
+ import type * as vendorArticles from "../vendorArticles.js";
42
+
43
+ import type {
44
+ ApiFromModules,
45
+ FilterApi,
46
+ FunctionReference,
47
+ } from "convex/server";
48
+ import { anyApi, componentsGeneric } from "convex/server";
49
+
50
+ const fullApi: ApiFromModules<{
51
+ apiKeys: typeof apiKeys;
52
+ clinics: typeof clinics;
53
+ contracts: typeof contracts;
54
+ crons: typeof crons;
55
+ dashboardStats: typeof dashboardStats;
56
+ dashboardStatsCache: typeof dashboardStatsCache;
57
+ deviceCategories: typeof deviceCategories;
58
+ deviceQuestions: typeof deviceQuestions;
59
+ deviceRepairHistory: typeof deviceRepairHistory;
60
+ deviceStatus: typeof deviceStatus;
61
+ devices: typeof devices;
62
+ emailHelpers: typeof emailHelpers;
63
+ emails: typeof emails;
64
+ http: typeof http;
65
+ maintenanceTasks: typeof maintenanceTasks;
66
+ roles: typeof roles;
67
+ slaMonitoring: typeof slaMonitoring;
68
+ slaRules: typeof slaRules;
69
+ sparePartOrders: typeof sparePartOrders;
70
+ spareParts: typeof spareParts;
71
+ supplierCategories: typeof supplierCategories;
72
+ suppliers: typeof suppliers;
73
+ ticketComments: typeof ticketComments;
74
+ ticketCustomFields: typeof ticketCustomFields;
75
+ ticketExport: typeof ticketExport;
76
+ ticketHistory: typeof ticketHistory;
77
+ ticketMacros: typeof ticketMacros;
78
+ ticketStatuses: typeof ticketStatuses;
79
+ ticketTriggers: typeof ticketTriggers;
80
+ userProfiles: typeof userProfiles;
81
+ vendorArticles: typeof vendorArticles;
82
+ }> = anyApi as any;
83
+
84
+ /**
85
+ * A utility for referencing Convex functions in your app's public API.
86
+ *
87
+ * Usage:
88
+ * ```js
89
+ * const myFunctionReference = api.myModule.myFunction;
90
+ * ```
91
+ */
92
+ export const api: FilterApi<
93
+ typeof fullApi,
94
+ FunctionReference<any, "public">
95
+ > = anyApi as any;
96
+
97
+ /**
98
+ * A utility for referencing Convex functions in your app's internal API.
99
+ *
100
+ * Usage:
101
+ * ```js
102
+ * const myFunctionReference = internal.myModule.myFunction;
103
+ * ```
104
+ */
105
+ export const internal: FilterApi<
106
+ typeof fullApi,
107
+ FunctionReference<any, "internal">
108
+ > = anyApi as any;
109
+
110
+ export const components = componentsGeneric() as unknown as {};