@primocaredentgroup/elettromedicali 0.1.0 → 0.1.1
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.
- package/dist/client/index.d.ts +0 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -28
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +4 -4
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +165 -218
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/contracts.d.ts +9 -9
- package/dist/component/contracts.d.ts.map +1 -1
- package/dist/component/contracts.js +7 -13
- package/dist/component/contracts.js.map +1 -1
- package/dist/component/crons.d.ts.map +1 -1
- package/dist/component/crons.js +1 -2
- package/dist/component/crons.js.map +1 -1
- package/dist/component/dashboardStats.d.ts +8 -3
- package/dist/component/dashboardStats.d.ts.map +1 -1
- package/dist/component/dashboardStats.js +24 -39
- package/dist/component/dashboardStats.js.map +1 -1
- package/dist/component/dashboardStatsCache.d.ts +5 -11
- package/dist/component/dashboardStatsCache.d.ts.map +1 -1
- package/dist/component/dashboardStatsCache.js +12 -53
- package/dist/component/dashboardStatsCache.js.map +1 -1
- package/dist/component/deviceCategories.d.ts +22 -15
- package/dist/component/deviceCategories.d.ts.map +1 -1
- package/dist/component/deviceCategories.js +10 -4
- package/dist/component/deviceCategories.js.map +1 -1
- package/dist/component/deviceQuestions.d.ts +36 -27
- package/dist/component/deviceQuestions.d.ts.map +1 -1
- package/dist/component/deviceQuestions.js +22 -5
- package/dist/component/deviceQuestions.js.map +1 -1
- package/dist/component/deviceRepairHistory.d.ts +3 -3
- package/dist/component/deviceRepairHistory.js +1 -1
- package/dist/component/deviceRepairHistory.js.map +1 -1
- package/dist/component/deviceStatus.d.ts +8 -57
- package/dist/component/deviceStatus.d.ts.map +1 -1
- package/dist/component/deviceStatus.js +32 -30
- package/dist/component/deviceStatus.js.map +1 -1
- package/dist/component/devices.d.ts +39 -22
- package/dist/component/devices.d.ts.map +1 -1
- package/dist/component/devices.js +85 -96
- package/dist/component/devices.js.map +1 -1
- package/dist/component/emailHelpers.d.ts +10 -3
- package/dist/component/emailHelpers.d.ts.map +1 -1
- package/dist/component/emailHelpers.js +9 -20
- package/dist/component/emailHelpers.js.map +1 -1
- package/dist/component/emails.d.ts +5 -5
- package/dist/component/emails.js +2 -2
- package/dist/component/emails.js.map +1 -1
- package/dist/component/http.d.ts.map +1 -1
- package/dist/component/http.js +3 -108
- package/dist/component/http.js.map +1 -1
- package/dist/component/migrationHelpers.d.ts +29 -0
- package/dist/component/migrationHelpers.d.ts.map +1 -0
- package/dist/component/migrationHelpers.js +84 -0
- package/dist/component/migrationHelpers.js.map +1 -0
- package/dist/component/roles.d.ts +1 -0
- package/dist/component/roles.d.ts.map +1 -1
- package/dist/component/roles.js +5 -6
- package/dist/component/roles.js.map +1 -1
- package/dist/component/schema.d.ts +69 -150
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +35 -88
- package/dist/component/schema.js.map +1 -1
- package/dist/component/slaMonitoring.d.ts +16 -30
- package/dist/component/slaMonitoring.d.ts.map +1 -1
- package/dist/component/slaMonitoring.js +48 -99
- package/dist/component/slaMonitoring.js.map +1 -1
- package/dist/component/spareParts.d.ts +11 -48
- package/dist/component/spareParts.d.ts.map +1 -1
- package/dist/component/spareParts.js +41 -11
- package/dist/component/spareParts.js.map +1 -1
- package/dist/component/suppliers.d.ts +38 -19
- package/dist/component/suppliers.d.ts.map +1 -1
- package/dist/component/suppliers.js +63 -44
- package/dist/component/suppliers.js.map +1 -1
- package/dist/component/ticketComments.d.ts +18 -12
- package/dist/component/ticketComments.d.ts.map +1 -1
- package/dist/component/ticketComments.js +28 -59
- package/dist/component/ticketComments.js.map +1 -1
- package/dist/component/ticketDeviceData.d.ts +63 -0
- package/dist/component/ticketDeviceData.d.ts.map +1 -0
- package/dist/component/ticketDeviceData.js +103 -0
- package/dist/component/ticketDeviceData.js.map +1 -0
- package/dist/component/ticketExport.d.ts +22 -40
- package/dist/component/ticketExport.d.ts.map +1 -1
- package/dist/component/ticketExport.js +43 -109
- package/dist/component/ticketExport.js.map +1 -1
- package/dist/component/ticketHistory.d.ts +4 -4
- package/dist/component/ticketHistory.d.ts.map +1 -1
- package/dist/component/ticketHistory.js +6 -9
- package/dist/component/ticketHistory.js.map +1 -1
- package/dist/component/ticketMacros.d.ts +19 -18
- package/dist/component/ticketMacros.d.ts.map +1 -1
- package/dist/component/ticketMacros.js +24 -30
- package/dist/component/ticketMacros.js.map +1 -1
- package/dist/component/ticketStatuses.d.ts +1 -0
- package/dist/component/ticketStatuses.d.ts.map +1 -1
- package/dist/component/ticketStatuses.js +5 -6
- package/dist/component/ticketStatuses.js.map +1 -1
- package/dist/component/ticketTriggers.d.ts +36 -16
- package/dist/component/ticketTriggers.d.ts.map +1 -1
- package/dist/component/ticketTriggers.js +115 -153
- package/dist/component/ticketTriggers.js.map +1 -1
- package/dist/component/userProfiles.d.ts +25 -120
- package/dist/component/userProfiles.d.ts.map +1 -1
- package/dist/component/userProfiles.js +73 -384
- package/dist/component/userProfiles.js.map +1 -1
- package/dist/test.d.ts +69 -150
- package/dist/test.d.ts.map +1 -1
- package/package.json +12 -3
- package/src/client/index.ts +2 -30
- package/src/component/_generated/api.ts +4 -4
- package/src/component/_generated/component.ts +228 -350
- package/src/component/contracts.ts +7 -14
- package/src/component/crons.ts +2 -7
- package/src/component/dashboardStats.ts +24 -41
- package/src/component/dashboardStatsCache.ts +12 -61
- package/src/component/deviceCategories.ts +12 -4
- package/src/component/deviceQuestions.ts +28 -5
- package/src/component/deviceRepairHistory.ts +1 -1
- package/src/component/deviceStatus.ts +43 -45
- package/src/component/devices.ts +87 -106
- package/src/component/emailHelpers.ts +9 -19
- package/src/component/emails.ts +2 -2
- package/src/component/http.ts +3 -108
- package/src/component/migrationHelpers.ts +96 -0
- package/src/component/roles.ts +5 -6
- package/src/component/schema.ts +35 -93
- package/src/component/slaMonitoring.ts +52 -107
- package/src/component/spareParts.ts +46 -12
- package/src/component/suppliers.ts +71 -48
- package/src/component/ticketComments.ts +28 -71
- package/src/component/ticketDeviceData.ts +113 -0
- package/src/component/ticketExport.ts +52 -137
- package/src/component/ticketHistory.ts +6 -9
- package/src/component/ticketMacros.ts +25 -37
- package/src/component/ticketStatuses.ts +5 -6
- package/src/component/ticketTriggers.ts +121 -217
- package/src/component/userProfiles.ts +67 -451
- package/dist/component/clinics.d.ts +0 -103
- package/dist/component/clinics.d.ts.map +0 -1
- package/dist/component/clinics.js +0 -126
- package/dist/component/clinics.js.map +0 -1
- package/dist/component/maintenanceTasks.d.ts +0 -733
- package/dist/component/maintenanceTasks.d.ts.map +0 -1
- package/dist/component/maintenanceTasks.js +0 -937
- package/dist/component/maintenanceTasks.js.map +0 -1
- package/src/component/clinics.ts +0 -136
- package/src/component/maintenanceTasks.ts +0 -1003
|
@@ -2,10 +2,34 @@ import { v } from 'convex/values';
|
|
|
2
2
|
import { query, mutation } from './_generated/server';
|
|
3
3
|
|
|
4
4
|
export const listSuppliers = query({
|
|
5
|
-
args: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
args: {
|
|
6
|
+
page: v.optional(v.number()),
|
|
7
|
+
pageSize: v.optional(v.number()),
|
|
8
|
+
search: v.optional(v.string()),
|
|
9
|
+
},
|
|
10
|
+
handler: async (ctx, args) => {
|
|
11
|
+
const page = args.page || 1;
|
|
12
|
+
const pageSize = args.pageSize || 20;
|
|
13
|
+
|
|
14
|
+
let suppliers = await ctx.db.query('suppliers').collect();
|
|
15
|
+
|
|
16
|
+
if (args.search) {
|
|
17
|
+
const s = args.search.toLowerCase();
|
|
18
|
+
suppliers = suppliers.filter((sup: any) =>
|
|
19
|
+
sup.name?.toLowerCase().includes(s) ||
|
|
20
|
+
sup.contact_email?.toLowerCase().includes(s) ||
|
|
21
|
+
sup.contactPerson?.toLowerCase().includes(s)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
suppliers.sort((a: any, b: any) => (a.name || '').localeCompare(b.name || ''));
|
|
26
|
+
|
|
27
|
+
const totalCount = suppliers.length;
|
|
28
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
29
|
+
const startIndex = (page - 1) * pageSize;
|
|
30
|
+
const pageSuppliers = suppliers.slice(startIndex, startIndex + pageSize);
|
|
31
|
+
|
|
32
|
+
return { page: pageSuppliers, totalCount, totalPages, currentPage: page };
|
|
9
33
|
},
|
|
10
34
|
});
|
|
11
35
|
|
|
@@ -115,7 +139,10 @@ export const updateSupplier = mutation({
|
|
|
115
139
|
});
|
|
116
140
|
|
|
117
141
|
export const deleteSupplier = mutation({
|
|
118
|
-
args: {
|
|
142
|
+
args: {
|
|
143
|
+
supplierId: v.id('suppliers'),
|
|
144
|
+
hasLinkedTickets: v.optional(v.boolean()),
|
|
145
|
+
},
|
|
119
146
|
handler: async (ctx, args) => {
|
|
120
147
|
const contracts = await ctx.db
|
|
121
148
|
.query('contracts')
|
|
@@ -129,14 +156,9 @@ export const deleteSupplier = mutation({
|
|
|
129
156
|
);
|
|
130
157
|
}
|
|
131
158
|
|
|
132
|
-
|
|
133
|
-
.query('maintenance_tasks')
|
|
134
|
-
.withIndex('by_supplierId', (q: any) => q.eq('supplierId', args.supplierId))
|
|
135
|
-
.collect();
|
|
136
|
-
|
|
137
|
-
if (tasks.length > 0) {
|
|
159
|
+
if (args.hasLinkedTickets) {
|
|
138
160
|
throw new Error(
|
|
139
|
-
`Impossibile eliminare il fornitore: ha
|
|
161
|
+
`Impossibile eliminare il fornitore: ha ticket di manutenzione associati. ` +
|
|
140
162
|
`Riassegna o elimina prima i ticket.`
|
|
141
163
|
);
|
|
142
164
|
}
|
|
@@ -159,34 +181,31 @@ export const getSuppliersByCategory = query({
|
|
|
159
181
|
});
|
|
160
182
|
|
|
161
183
|
export const listSupplierUsers = query({
|
|
162
|
-
args: {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const supplierUsers = await ctx.db
|
|
174
|
-
.query('user_profiles')
|
|
175
|
-
.withIndex('by_roleId', (q: any) => q.eq('roleId', supplierRole._id))
|
|
176
|
-
.collect();
|
|
184
|
+
args: {
|
|
185
|
+
supplierUserList: v.optional(v.array(v.object({
|
|
186
|
+
auth0Id: v.string(),
|
|
187
|
+
name: v.string(),
|
|
188
|
+
email: v.string(),
|
|
189
|
+
}))),
|
|
190
|
+
},
|
|
191
|
+
handler: async (ctx, args) => {
|
|
192
|
+
const supplierUsers = args.supplierUserList || [];
|
|
193
|
+
const settings = await ctx.db.query('user_profiles').withIndex('by_supplierId').collect();
|
|
194
|
+
const settingsMap = new Map(settings.filter(s => s.supplierId).map(s => [s.auth0Id, s]));
|
|
177
195
|
|
|
178
196
|
const result = await Promise.all(
|
|
179
197
|
supplierUsers.map(async (user) => {
|
|
180
|
-
const
|
|
181
|
-
|
|
198
|
+
const userSettings = settingsMap.get(user.auth0Id);
|
|
199
|
+
const linkedSupplier = userSettings?.supplierId
|
|
200
|
+
? await ctx.db.get(userSettings.supplierId)
|
|
182
201
|
: null;
|
|
183
202
|
|
|
184
203
|
return {
|
|
185
|
-
_id: user.
|
|
186
|
-
userId: user.
|
|
204
|
+
_id: user.auth0Id,
|
|
205
|
+
userId: user.auth0Id,
|
|
187
206
|
email: user.email,
|
|
188
207
|
name: user.name || user.email,
|
|
189
|
-
supplierId:
|
|
208
|
+
supplierId: userSettings?.supplierId || null,
|
|
190
209
|
supplierName: linkedSupplier?.name || null,
|
|
191
210
|
supplierEmail: linkedSupplier?.contact_email || null,
|
|
192
211
|
};
|
|
@@ -199,35 +218,39 @@ export const listSupplierUsers = query({
|
|
|
199
218
|
|
|
200
219
|
export const getOrCreateSupplierForUser = mutation({
|
|
201
220
|
args: {
|
|
202
|
-
userId: v.
|
|
221
|
+
userId: v.string(),
|
|
222
|
+
userName: v.optional(v.string()),
|
|
223
|
+
userEmail: v.optional(v.string()),
|
|
224
|
+
userRole: v.optional(v.string()),
|
|
203
225
|
},
|
|
204
226
|
handler: async (ctx, args) => {
|
|
205
|
-
|
|
206
|
-
if (!user) {
|
|
207
|
-
throw new Error('User not found');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const role = user.roleId ? await ctx.db.get(user.roleId) : null;
|
|
211
|
-
if (role?.name !== 'supplier') {
|
|
227
|
+
if (args.userRole !== 'supplier') {
|
|
212
228
|
throw new Error('User is not a supplier');
|
|
213
229
|
}
|
|
214
230
|
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
const settings = await ctx.db
|
|
232
|
+
.query('user_profiles')
|
|
233
|
+
.withIndex('by_auth0Id', (q: any) => q.eq('auth0Id', args.userId))
|
|
234
|
+
.first();
|
|
235
|
+
|
|
236
|
+
if (settings?.supplierId) {
|
|
237
|
+
return settings.supplierId;
|
|
217
238
|
}
|
|
218
239
|
|
|
219
240
|
const supplierId = await ctx.db.insert('suppliers', {
|
|
220
|
-
name:
|
|
221
|
-
contact_email:
|
|
241
|
+
name: args.userName || args.userEmail || 'Unknown',
|
|
242
|
+
contact_email: args.userEmail || '',
|
|
222
243
|
contact_phone: '',
|
|
223
244
|
categories: [],
|
|
224
245
|
sla_days: 7,
|
|
225
|
-
notes: `Creato automaticamente dall'utente ${
|
|
246
|
+
notes: `Creato automaticamente dall'utente ${args.userEmail}`,
|
|
226
247
|
});
|
|
227
248
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
249
|
+
if (settings) {
|
|
250
|
+
await ctx.db.patch(settings._id, {
|
|
251
|
+
supplierId: supplierId,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
231
254
|
|
|
232
255
|
return supplierId;
|
|
233
256
|
},
|
|
@@ -3,18 +3,13 @@ import { query, mutation, internalQuery } from './_generated/server';
|
|
|
3
3
|
|
|
4
4
|
export const listComments = query({
|
|
5
5
|
args: {
|
|
6
|
-
|
|
6
|
+
ticketId: v.string(),
|
|
7
7
|
userRole: v.optional(v.string()),
|
|
8
8
|
},
|
|
9
9
|
handler: async (ctx, args) => {
|
|
10
|
-
const task = await ctx.db.get(args.taskId);
|
|
11
|
-
if (!task) {
|
|
12
|
-
throw new Error('Ticket not found');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
10
|
const comments = await ctx.db
|
|
16
11
|
.query('ticket_comments')
|
|
17
|
-
.withIndex('
|
|
12
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
18
13
|
.collect();
|
|
19
14
|
|
|
20
15
|
const filteredComments = comments.filter((comment) => {
|
|
@@ -49,12 +44,12 @@ export const listComments = query({
|
|
|
49
44
|
|
|
50
45
|
export const addComment = mutation({
|
|
51
46
|
args: {
|
|
52
|
-
|
|
47
|
+
ticketId: v.string(),
|
|
53
48
|
content: v.string(),
|
|
54
49
|
isInternal: v.optional(v.boolean()),
|
|
55
50
|
attachments: v.optional(v.array(v.id('_storage'))),
|
|
56
51
|
mentions: v.optional(v.array(v.object({
|
|
57
|
-
userId: v.
|
|
52
|
+
userId: v.string(),
|
|
58
53
|
name: v.string(),
|
|
59
54
|
email: v.string(),
|
|
60
55
|
}))),
|
|
@@ -64,11 +59,6 @@ export const addComment = mutation({
|
|
|
64
59
|
authorRole: v.optional(v.string()),
|
|
65
60
|
},
|
|
66
61
|
handler: async (ctx, args) => {
|
|
67
|
-
const task = await ctx.db.get(args.taskId);
|
|
68
|
-
if (!task) {
|
|
69
|
-
throw new Error('Ticket not found');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
62
|
if (args.attachments && args.attachments.length > 5) {
|
|
73
63
|
throw new Error('Maximum 5 attachments allowed');
|
|
74
64
|
}
|
|
@@ -80,7 +70,7 @@ export const addComment = mutation({
|
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
const commentId = await ctx.db.insert('ticket_comments', {
|
|
83
|
-
|
|
73
|
+
ticketId: args.ticketId,
|
|
84
74
|
authorId: args.authorId,
|
|
85
75
|
authorName: args.authorName || args.authorEmail || 'Unknown',
|
|
86
76
|
authorEmail: args.authorEmail || '',
|
|
@@ -93,10 +83,6 @@ export const addComment = mutation({
|
|
|
93
83
|
createdAt: Date.now(),
|
|
94
84
|
});
|
|
95
85
|
|
|
96
|
-
await ctx.db.patch(args.taskId, {
|
|
97
|
-
updated_at: Date.now(),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
86
|
return commentId;
|
|
101
87
|
},
|
|
102
88
|
});
|
|
@@ -110,20 +96,15 @@ export const generateUploadUrl = mutation({
|
|
|
110
96
|
|
|
111
97
|
export const addCommentFromApi = mutation({
|
|
112
98
|
args: {
|
|
113
|
-
|
|
99
|
+
ticketId: v.string(),
|
|
114
100
|
content: v.string(),
|
|
115
101
|
authorEmail: v.optional(v.string()),
|
|
116
102
|
authorName: v.optional(v.string()),
|
|
117
103
|
isInternal: v.optional(v.boolean()),
|
|
118
104
|
},
|
|
119
105
|
handler: async (ctx, args) => {
|
|
120
|
-
const task = await ctx.db.get(args.taskId);
|
|
121
|
-
if (!task) {
|
|
122
|
-
throw new Error('Ticket not found');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
106
|
const commentId = await ctx.db.insert('ticket_comments', {
|
|
126
|
-
|
|
107
|
+
ticketId: args.ticketId,
|
|
127
108
|
authorId: 'api',
|
|
128
109
|
authorName: args.authorName || 'Sistema Esterno',
|
|
129
110
|
authorEmail: args.authorEmail || 'api@external',
|
|
@@ -134,10 +115,6 @@ export const addCommentFromApi = mutation({
|
|
|
134
115
|
createdAt: Date.now(),
|
|
135
116
|
});
|
|
136
117
|
|
|
137
|
-
await ctx.db.patch(args.taskId, {
|
|
138
|
-
updated_at: Date.now(),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
118
|
return commentId;
|
|
142
119
|
},
|
|
143
120
|
});
|
|
@@ -190,11 +167,11 @@ export const updateComment = mutation({
|
|
|
190
167
|
});
|
|
191
168
|
|
|
192
169
|
export const getCommentCount = query({
|
|
193
|
-
args: {
|
|
170
|
+
args: { ticketId: v.string() },
|
|
194
171
|
handler: async (ctx, args) => {
|
|
195
172
|
const comments = await ctx.db
|
|
196
173
|
.query('ticket_comments')
|
|
197
|
-
.withIndex('
|
|
174
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
198
175
|
.collect();
|
|
199
176
|
|
|
200
177
|
return comments.length;
|
|
@@ -203,24 +180,21 @@ export const getCommentCount = query({
|
|
|
203
180
|
|
|
204
181
|
export const getUsersForMention = query({
|
|
205
182
|
args: {
|
|
206
|
-
|
|
183
|
+
ticketId: v.string(),
|
|
207
184
|
searchQuery: v.optional(v.string()),
|
|
208
|
-
excludeUserId: v.optional(v.
|
|
185
|
+
excludeUserId: v.optional(v.string()),
|
|
186
|
+
userList: v.optional(v.array(v.object({
|
|
187
|
+
auth0Id: v.string(),
|
|
188
|
+
name: v.string(),
|
|
189
|
+
email: v.string(),
|
|
190
|
+
role: v.string(),
|
|
191
|
+
}))),
|
|
209
192
|
},
|
|
210
193
|
handler: async (ctx, args) => {
|
|
211
|
-
|
|
212
|
-
if (!task) {
|
|
213
|
-
throw new Error('Ticket not found');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
let users = await ctx.db
|
|
217
|
-
.query('user_profiles')
|
|
218
|
-
.take(500);
|
|
219
|
-
|
|
220
|
-
users = users.filter(u => u.isActive !== false);
|
|
194
|
+
let users = args.userList || [];
|
|
221
195
|
|
|
222
196
|
if (args.excludeUserId) {
|
|
223
|
-
users = users.filter(u => u.
|
|
197
|
+
users = users.filter(u => u.auth0Id !== args.excludeUserId);
|
|
224
198
|
}
|
|
225
199
|
|
|
226
200
|
if (args.searchQuery && args.searchQuery.trim()) {
|
|
@@ -231,44 +205,27 @@ export const getUsersForMention = query({
|
|
|
231
205
|
);
|
|
232
206
|
}
|
|
233
207
|
|
|
234
|
-
const
|
|
235
|
-
users.map(async (u) => {
|
|
236
|
-
const role = u.roleId ? await ctx.db.get(u.roleId) : null;
|
|
237
|
-
const roleName = (role as any)?.name || u.role || 'user';
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
_id: u._id,
|
|
241
|
-
name: u.name || u.email,
|
|
242
|
-
email: u.email,
|
|
243
|
-
role: roleName,
|
|
244
|
-
};
|
|
245
|
-
})
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const sortedUsers = usersWithRoles.sort((a, b) => {
|
|
208
|
+
const sortedUsers = [...users].sort((a, b) => {
|
|
249
209
|
if (a.role === 'admin' && b.role !== 'admin') return -1;
|
|
250
210
|
if (b.role === 'admin' && a.role !== 'admin') return 1;
|
|
251
|
-
|
|
252
211
|
return (a.name || '').localeCompare(b.name || '');
|
|
253
212
|
});
|
|
254
213
|
|
|
255
|
-
return sortedUsers.slice(0, 20)
|
|
214
|
+
return sortedUsers.slice(0, 20).map(u => ({
|
|
215
|
+
_id: u.auth0Id,
|
|
216
|
+
name: u.name || u.email,
|
|
217
|
+
email: u.email,
|
|
218
|
+
role: u.role,
|
|
219
|
+
}));
|
|
256
220
|
},
|
|
257
221
|
});
|
|
258
222
|
|
|
259
223
|
export const listCommentsInternal = internalQuery({
|
|
260
|
-
args: {
|
|
224
|
+
args: { ticketId: v.string() },
|
|
261
225
|
handler: async (ctx, args) => {
|
|
262
|
-
let taskIdTyped;
|
|
263
|
-
try {
|
|
264
|
-
taskIdTyped = args.taskId as any;
|
|
265
|
-
} catch {
|
|
266
|
-
throw new Error('Invalid taskId format');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
226
|
const comments = await ctx.db
|
|
270
227
|
.query('ticket_comments')
|
|
271
|
-
.withIndex('
|
|
228
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
272
229
|
.collect();
|
|
273
230
|
|
|
274
231
|
return comments
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { v } from 'convex/values';
|
|
2
|
+
import { query, mutation } from './_generated/server';
|
|
3
|
+
|
|
4
|
+
export const getByTicketId = query({
|
|
5
|
+
args: { ticketId: v.string() },
|
|
6
|
+
handler: async (ctx, args) => {
|
|
7
|
+
return await ctx.db
|
|
8
|
+
.query('ticket_device_data')
|
|
9
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
10
|
+
.first();
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const getByDeviceId = query({
|
|
15
|
+
args: { deviceId: v.id('devices') },
|
|
16
|
+
handler: async (ctx, args) => {
|
|
17
|
+
return await ctx.db
|
|
18
|
+
.query('ticket_device_data')
|
|
19
|
+
.withIndex('by_deviceId', (q) => q.eq('deviceId', args.deviceId))
|
|
20
|
+
.collect();
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const create = mutation({
|
|
25
|
+
args: {
|
|
26
|
+
ticketId: v.string(),
|
|
27
|
+
deviceId: v.optional(v.id('devices')),
|
|
28
|
+
deviceQuestionAnswers: v.optional(v.array(v.object({
|
|
29
|
+
questionId: v.id('device_questions'),
|
|
30
|
+
question: v.string(),
|
|
31
|
+
answer: v.any(),
|
|
32
|
+
}))),
|
|
33
|
+
photoStorageIds: v.optional(v.array(v.id('_storage'))),
|
|
34
|
+
isExternal: v.optional(v.boolean()),
|
|
35
|
+
needsAssignment: v.optional(v.boolean()),
|
|
36
|
+
},
|
|
37
|
+
handler: async (ctx, args) => {
|
|
38
|
+
return await ctx.db.insert('ticket_device_data', {
|
|
39
|
+
ticketId: args.ticketId,
|
|
40
|
+
deviceId: args.deviceId,
|
|
41
|
+
deviceQuestionAnswers: args.deviceQuestionAnswers,
|
|
42
|
+
photoStorageIds: args.photoStorageIds,
|
|
43
|
+
isExternal: args.isExternal,
|
|
44
|
+
needsAssignment: args.needsAssignment,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const update = mutation({
|
|
50
|
+
args: {
|
|
51
|
+
ticketId: v.string(),
|
|
52
|
+
deviceId: v.optional(v.id('devices')),
|
|
53
|
+
deviceQuestionAnswers: v.optional(v.array(v.object({
|
|
54
|
+
questionId: v.id('device_questions'),
|
|
55
|
+
question: v.string(),
|
|
56
|
+
answer: v.any(),
|
|
57
|
+
}))),
|
|
58
|
+
photoStorageIds: v.optional(v.array(v.id('_storage'))),
|
|
59
|
+
},
|
|
60
|
+
handler: async (ctx, args) => {
|
|
61
|
+
const existing = await ctx.db
|
|
62
|
+
.query('ticket_device_data')
|
|
63
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
64
|
+
.first();
|
|
65
|
+
|
|
66
|
+
if (!existing) throw new Error('Device data not found for this ticket');
|
|
67
|
+
|
|
68
|
+
const updates: any = {};
|
|
69
|
+
if (args.deviceId !== undefined) updates.deviceId = args.deviceId;
|
|
70
|
+
if (args.deviceQuestionAnswers !== undefined) updates.deviceQuestionAnswers = args.deviceQuestionAnswers;
|
|
71
|
+
if (args.photoStorageIds !== undefined) updates.photoStorageIds = args.photoStorageIds;
|
|
72
|
+
|
|
73
|
+
await ctx.db.patch(existing._id, updates);
|
|
74
|
+
return existing._id;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export const deleteByTicketId = mutation({
|
|
79
|
+
args: { ticketId: v.string() },
|
|
80
|
+
handler: async (ctx, args) => {
|
|
81
|
+
const entries = await ctx.db
|
|
82
|
+
.query('ticket_device_data')
|
|
83
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
84
|
+
.collect();
|
|
85
|
+
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
await ctx.db.delete(entry._id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { deleted: entries.length };
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const getPhotoUrls = query({
|
|
95
|
+
args: { ticketId: v.string() },
|
|
96
|
+
handler: async (ctx, args) => {
|
|
97
|
+
const data = await ctx.db
|
|
98
|
+
.query('ticket_device_data')
|
|
99
|
+
.withIndex('by_ticketId', (q) => q.eq('ticketId', args.ticketId))
|
|
100
|
+
.first();
|
|
101
|
+
|
|
102
|
+
if (!data?.photoStorageIds) return [];
|
|
103
|
+
|
|
104
|
+
const urls = await Promise.all(
|
|
105
|
+
data.photoStorageIds.map(async (storageId) => {
|
|
106
|
+
const url = await ctx.storage.getUrl(storageId);
|
|
107
|
+
return url || '';
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return urls.filter(u => u !== '');
|
|
112
|
+
},
|
|
113
|
+
});
|