@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
|
@@ -1,319 +1,129 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
2
|
import { query, mutation } from './_generated/server';
|
|
3
|
-
import { paginationOptsValidator } from 'convex/server';
|
|
4
3
|
|
|
5
4
|
export const getUserProfile = query({
|
|
6
5
|
args: { auth0Id: v.string() },
|
|
7
6
|
handler: async (ctx, args) => {
|
|
8
|
-
|
|
7
|
+
return await ctx.db
|
|
9
8
|
.query('user_profiles')
|
|
10
9
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
11
10
|
.first();
|
|
12
|
-
|
|
13
|
-
return profile;
|
|
14
11
|
},
|
|
15
12
|
});
|
|
16
13
|
|
|
17
14
|
export const getCurrentUserProfile = query({
|
|
18
|
-
args: {
|
|
15
|
+
args: {
|
|
16
|
+
auth0Id: v.string(),
|
|
17
|
+
userName: v.optional(v.string()),
|
|
18
|
+
userEmail: v.optional(v.string()),
|
|
19
|
+
userRole: v.optional(v.string()),
|
|
20
|
+
userIsActive: v.optional(v.boolean()),
|
|
21
|
+
},
|
|
19
22
|
handler: async (ctx, args) => {
|
|
20
|
-
const
|
|
23
|
+
const settings = await ctx.db
|
|
21
24
|
.query('user_profiles')
|
|
22
25
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
23
26
|
.first();
|
|
24
27
|
|
|
25
|
-
if (!
|
|
28
|
+
if (!settings) {
|
|
26
29
|
return null;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
if (
|
|
32
|
+
if (args.userIsActive === false) {
|
|
30
33
|
return {
|
|
31
|
-
...
|
|
34
|
+
...settings,
|
|
35
|
+
name: args.userName,
|
|
36
|
+
email: args.userEmail,
|
|
32
37
|
role: null,
|
|
33
38
|
isDisabled: true,
|
|
34
39
|
isImpersonating: false,
|
|
35
40
|
};
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
const
|
|
39
|
-
const isAdmin = realRole?.name === 'admin';
|
|
43
|
+
const isAdmin = args.userRole === 'admin';
|
|
40
44
|
|
|
41
|
-
if (isAdmin &&
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
? await ctx.db.get(impersonatedProfile.roleId)
|
|
47
|
-
: null;
|
|
45
|
+
if (isAdmin && settings.impersonatingUserId) {
|
|
46
|
+
const impersonatedSettings = await ctx.db
|
|
47
|
+
.query('user_profiles')
|
|
48
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', settings.impersonatingUserId!))
|
|
49
|
+
.first();
|
|
48
50
|
|
|
51
|
+
if (impersonatedSettings) {
|
|
49
52
|
return {
|
|
50
|
-
...
|
|
51
|
-
role:
|
|
53
|
+
...impersonatedSettings,
|
|
54
|
+
role: undefined,
|
|
52
55
|
isImpersonating: true,
|
|
53
|
-
realAdminId:
|
|
54
|
-
realAdminName:
|
|
55
|
-
realAdminEmail:
|
|
56
|
+
realAdminId: settings._id,
|
|
57
|
+
realAdminName: args.userName,
|
|
58
|
+
realAdminEmail: args.userEmail,
|
|
56
59
|
isDisabled: false,
|
|
57
60
|
};
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
return {
|
|
62
|
-
...
|
|
63
|
-
|
|
65
|
+
...settings,
|
|
66
|
+
name: args.userName,
|
|
67
|
+
email: args.userEmail,
|
|
68
|
+
role: args.userRole,
|
|
64
69
|
isImpersonating: false,
|
|
65
70
|
isDisabled: false,
|
|
66
71
|
};
|
|
67
72
|
},
|
|
68
73
|
});
|
|
69
74
|
|
|
70
|
-
function isPlaceholderAuth0Id(auth0Id: string): boolean {
|
|
71
|
-
return auth0Id.startsWith('placeholder_') ||
|
|
72
|
-
auth0Id.startsWith('manual_') ||
|
|
73
|
-
!auth0Id.includes('|');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
75
|
export const upsertUserProfile = mutation({
|
|
77
76
|
args: {
|
|
78
77
|
auth0Id: v.string(),
|
|
79
|
-
|
|
80
|
-
name: v.optional(v.string()),
|
|
81
|
-
roleName: v.optional(v.string()),
|
|
82
|
-
clinicId: v.optional(v.id('clinics')),
|
|
78
|
+
clinicId: v.optional(v.string()),
|
|
83
79
|
supplierId: v.optional(v.id('suppliers')),
|
|
84
80
|
},
|
|
85
81
|
handler: async (ctx, args) => {
|
|
86
|
-
|
|
82
|
+
const existing = await ctx.db
|
|
87
83
|
.query('user_profiles')
|
|
88
84
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
89
85
|
.first();
|
|
90
86
|
|
|
91
|
-
let shouldUpdateAuth0Id = false;
|
|
92
|
-
if (!existing) {
|
|
93
|
-
const existingByEmail = await ctx.db
|
|
94
|
-
.query('user_profiles')
|
|
95
|
-
.withIndex('by_email', (q) => q.eq('email', args.email))
|
|
96
|
-
.first();
|
|
97
|
-
|
|
98
|
-
if (existingByEmail && isPlaceholderAuth0Id(existingByEmail.auth0Id)) {
|
|
99
|
-
existing = existingByEmail;
|
|
100
|
-
shouldUpdateAuth0Id = true;
|
|
101
|
-
console.log(`🔄 Found pre-created user with email ${args.email}, updating auth0Id from placeholder to real Auth0 ID`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let roleId = existing?.roleId;
|
|
106
|
-
if (args.roleName || !roleId) {
|
|
107
|
-
const roleName = args.roleName || 'user';
|
|
108
|
-
let role = await ctx.db
|
|
109
|
-
.query('roles')
|
|
110
|
-
.withIndex('by_name', (q) => q.eq('name', roleName))
|
|
111
|
-
.first();
|
|
112
|
-
|
|
113
|
-
if (!role) {
|
|
114
|
-
const newRoleId = await ctx.db.insert('roles', {
|
|
115
|
-
name: roleName,
|
|
116
|
-
description: roleName === 'admin' ? 'Administrator with full access' :
|
|
117
|
-
roleName === 'user' ? 'Standard user with limited access' :
|
|
118
|
-
roleName === 'supplier' ? 'Supplier with access to assigned tickets' :
|
|
119
|
-
`${roleName} role`,
|
|
120
|
-
createdAt: Date.now(),
|
|
121
|
-
});
|
|
122
|
-
role = await ctx.db.get(newRoleId);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (role) {
|
|
126
|
-
roleId = role._id;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
87
|
if (existing) {
|
|
131
|
-
const updateData: any = {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
if (shouldUpdateAuth0Id) {
|
|
139
|
-
updateData.auth0Id = args.auth0Id;
|
|
140
|
-
console.log(`✅ Updated auth0Id for user ${args.email}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (roleId) {
|
|
144
|
-
updateData.roleId = roleId;
|
|
88
|
+
const updateData: any = {};
|
|
89
|
+
if (args.clinicId !== undefined) updateData.clinicId = args.clinicId;
|
|
90
|
+
if (args.supplierId !== undefined) updateData.supplierId = args.supplierId;
|
|
91
|
+
if (Object.keys(updateData).length > 0) {
|
|
92
|
+
await ctx.db.patch(existing._id, updateData);
|
|
145
93
|
}
|
|
146
|
-
await ctx.db.patch(existing._id, updateData);
|
|
147
94
|
return existing._id;
|
|
148
95
|
} else {
|
|
149
|
-
const
|
|
96
|
+
const profileId = await ctx.db.insert('user_profiles', {
|
|
150
97
|
auth0Id: args.auth0Id,
|
|
151
|
-
email: args.email,
|
|
152
|
-
name: args.name,
|
|
153
98
|
...(args.clinicId && { clinicId: args.clinicId }),
|
|
154
99
|
...(args.supplierId && { supplierId: args.supplierId }),
|
|
155
|
-
};
|
|
156
|
-
if (roleId) {
|
|
157
|
-
newProfile.roleId = roleId;
|
|
158
|
-
}
|
|
159
|
-
const profileId = await ctx.db.insert('user_profiles', newProfile);
|
|
100
|
+
});
|
|
160
101
|
return profileId;
|
|
161
102
|
}
|
|
162
103
|
},
|
|
163
104
|
});
|
|
164
105
|
|
|
165
|
-
export const updateUserRole = mutation({
|
|
166
|
-
args: {
|
|
167
|
-
userId: v.id('user_profiles'),
|
|
168
|
-
roleName: v.string(),
|
|
169
|
-
clinicId: v.optional(v.id('clinics')),
|
|
170
|
-
supplierId: v.optional(v.id('suppliers')),
|
|
171
|
-
},
|
|
172
|
-
handler: async (ctx, args) => {
|
|
173
|
-
const newRole = await ctx.db
|
|
174
|
-
.query('roles')
|
|
175
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName))
|
|
176
|
-
.first();
|
|
177
|
-
|
|
178
|
-
if (!newRole) {
|
|
179
|
-
throw new Error(`Role ${args.roleName} not found`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
await ctx.db.patch(args.userId, {
|
|
183
|
-
roleId: newRole._id,
|
|
184
|
-
...(args.clinicId !== undefined && { clinicId: args.clinicId }),
|
|
185
|
-
...(args.supplierId !== undefined && { supplierId: args.supplierId }),
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return args.userId;
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
|
|
192
106
|
export const updateUserSupplier = mutation({
|
|
193
107
|
args: {
|
|
194
|
-
userId: v.
|
|
108
|
+
userId: v.string(),
|
|
195
109
|
supplierId: v.id('suppliers'),
|
|
196
110
|
},
|
|
197
111
|
handler: async (ctx, args) => {
|
|
198
|
-
await ctx.db
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
112
|
+
const profile = await ctx.db
|
|
113
|
+
.query('user_profiles')
|
|
114
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.userId))
|
|
115
|
+
.first();
|
|
116
|
+
if (!profile) throw new Error('User settings not found');
|
|
117
|
+
await ctx.db.patch(profile._id, { supplierId: args.supplierId });
|
|
118
|
+
return profile._id;
|
|
203
119
|
},
|
|
204
120
|
});
|
|
205
121
|
|
|
206
122
|
export const listUsers = query({
|
|
207
|
-
args: {
|
|
208
|
-
limit: v.optional(v.number()),
|
|
209
|
-
},
|
|
123
|
+
args: { limit: v.optional(v.number()) },
|
|
210
124
|
handler: async (ctx, args) => {
|
|
211
125
|
const maxResults = args.limit || 500;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const usersWithRoles = await Promise.all(
|
|
215
|
-
users.map(async (user) => {
|
|
216
|
-
const role = user.roleId ? await ctx.db.get(user.roleId) : null;
|
|
217
|
-
return {
|
|
218
|
-
...user,
|
|
219
|
-
role: role?.name || user.role,
|
|
220
|
-
};
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
return usersWithRoles;
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
export const listUsersPaginated = query({
|
|
229
|
-
args: {
|
|
230
|
-
paginationOpts: paginationOptsValidator,
|
|
231
|
-
roleId: v.optional(v.id('roles')),
|
|
232
|
-
isActive: v.optional(v.boolean()),
|
|
233
|
-
searchTerm: v.optional(v.string()),
|
|
234
|
-
},
|
|
235
|
-
handler: async (ctx, args) => {
|
|
236
|
-
let baseQuery;
|
|
237
|
-
|
|
238
|
-
if (args.roleId) {
|
|
239
|
-
baseQuery = ctx.db
|
|
240
|
-
.query('user_profiles')
|
|
241
|
-
.withIndex('by_roleId', (q: any) => q.eq('roleId', args.roleId));
|
|
242
|
-
} else if (args.isActive !== undefined) {
|
|
243
|
-
baseQuery = ctx.db
|
|
244
|
-
.query('user_profiles')
|
|
245
|
-
.withIndex('by_isActive', (q: any) => q.eq('isActive', args.isActive));
|
|
246
|
-
} else {
|
|
247
|
-
baseQuery = ctx.db.query('user_profiles');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let filteredQuery = baseQuery.order('desc');
|
|
251
|
-
|
|
252
|
-
if (args.isActive !== undefined && args.roleId) {
|
|
253
|
-
filteredQuery = filteredQuery.filter((q: any) => q.eq(q.field('isActive'), args.isActive));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const result = await filteredQuery.paginate(args.paginationOpts);
|
|
257
|
-
|
|
258
|
-
let filteredPage = result.page;
|
|
259
|
-
|
|
260
|
-
if (args.searchTerm) {
|
|
261
|
-
const search = args.searchTerm.toLowerCase();
|
|
262
|
-
filteredPage = filteredPage.filter((user: any) =>
|
|
263
|
-
user.name?.toLowerCase().includes(search) ||
|
|
264
|
-
user.email?.toLowerCase().includes(search)
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const roleIds = [...new Set(filteredPage.map((u: any) => u.roleId).filter(Boolean))];
|
|
269
|
-
const roles = await Promise.all(roleIds.map(id => ctx.db.get(id)));
|
|
270
|
-
const roleMap = new Map(roles.filter(Boolean).map((r: any) => [r._id.toString(), r.name]));
|
|
271
|
-
|
|
272
|
-
const minimalPage = filteredPage.map((user: any) => ({
|
|
273
|
-
_id: user._id,
|
|
274
|
-
auth0Id: user.auth0Id,
|
|
275
|
-
email: user.email,
|
|
276
|
-
name: user.name,
|
|
277
|
-
roleId: user.roleId,
|
|
278
|
-
role: roleMap.get(user.roleId?.toString()) || user.role || 'user',
|
|
279
|
-
isActive: user.isActive,
|
|
280
|
-
clinicId: user.clinicId,
|
|
281
|
-
supplierId: user.supplierId,
|
|
282
|
-
createdAt: user.createdAt || user._creationTime,
|
|
283
|
-
}));
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
page: minimalPage,
|
|
287
|
-
isDone: result.isDone,
|
|
288
|
-
continueCursor: result.continueCursor,
|
|
289
|
-
};
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
export const getUsersCount = query({
|
|
294
|
-
args: {
|
|
295
|
-
roleId: v.optional(v.id('roles')),
|
|
296
|
-
isActive: v.optional(v.boolean()),
|
|
297
|
-
},
|
|
298
|
-
handler: async (ctx, args) => {
|
|
299
|
-
let query;
|
|
300
|
-
|
|
301
|
-
if (args.roleId) {
|
|
302
|
-
query = ctx.db.query('user_profiles')
|
|
303
|
-
.withIndex('by_roleId', (q: any) => q.eq('roleId', args.roleId));
|
|
304
|
-
} else if (args.isActive !== undefined) {
|
|
305
|
-
query = ctx.db.query('user_profiles')
|
|
306
|
-
.withIndex('by_isActive', (q: any) => q.eq('isActive', args.isActive));
|
|
307
|
-
} else {
|
|
308
|
-
query = ctx.db.query('user_profiles');
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (args.isActive !== undefined && args.roleId) {
|
|
312
|
-
query = query.filter((q: any) => q.eq(q.field('isActive'), args.isActive));
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const users = await query.take(1000);
|
|
316
|
-
return { total: users.length };
|
|
126
|
+
return await ctx.db.query('user_profiles').order('desc').take(maxResults);
|
|
317
127
|
},
|
|
318
128
|
});
|
|
319
129
|
|
|
@@ -364,9 +174,7 @@ export const getPrimoUPToken = query({
|
|
|
364
174
|
)
|
|
365
175
|
.first();
|
|
366
176
|
|
|
367
|
-
if (!activeToken)
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
177
|
+
if (!activeToken) return null;
|
|
370
178
|
|
|
371
179
|
if (activeToken.expiresAt && activeToken.expiresAt < Date.now()) {
|
|
372
180
|
return {
|
|
@@ -404,7 +212,6 @@ export const logPrimoUPAccess = mutation({
|
|
|
404
212
|
timestamp: Date.now(),
|
|
405
213
|
metadata: args.metadata,
|
|
406
214
|
});
|
|
407
|
-
|
|
408
215
|
return { success: true };
|
|
409
216
|
},
|
|
410
217
|
});
|
|
@@ -418,51 +225,11 @@ export const getPrimoUPAccessLogs = query({
|
|
|
418
225
|
},
|
|
419
226
|
handler: async (ctx, args) => {
|
|
420
227
|
const targetUserId = args.isAdmin && args.userId ? args.userId : args.auth0Id;
|
|
421
|
-
|
|
422
|
-
const logs = await ctx.db
|
|
228
|
+
return await ctx.db
|
|
423
229
|
.query('primoup_access_logs')
|
|
424
230
|
.withIndex('by_userId_timestamp', (q) => q.eq('userId', targetUserId))
|
|
425
231
|
.order('desc')
|
|
426
232
|
.take(args.limit || 50);
|
|
427
|
-
|
|
428
|
-
return logs;
|
|
429
|
-
},
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
export const updateUserRoleByEmail = mutation({
|
|
433
|
-
args: {
|
|
434
|
-
email: v.string(),
|
|
435
|
-
roleName: v.string(),
|
|
436
|
-
},
|
|
437
|
-
handler: async (ctx, args) => {
|
|
438
|
-
const user = await ctx.db
|
|
439
|
-
.query('user_profiles')
|
|
440
|
-
.filter((q) => q.eq(q.field('email'), args.email))
|
|
441
|
-
.first();
|
|
442
|
-
|
|
443
|
-
if (!user) {
|
|
444
|
-
throw new Error(`User with email ${args.email} not found`);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const role = await ctx.db
|
|
448
|
-
.query('roles')
|
|
449
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName))
|
|
450
|
-
.first();
|
|
451
|
-
|
|
452
|
-
if (!role) {
|
|
453
|
-
throw new Error(`Role ${args.roleName} not found`);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
await ctx.db.patch(user._id, {
|
|
457
|
-
roleId: role._id,
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
return {
|
|
461
|
-
success: true,
|
|
462
|
-
userId: user._id,
|
|
463
|
-
email: user.email,
|
|
464
|
-
newRole: args.roleName,
|
|
465
|
-
};
|
|
466
233
|
},
|
|
467
234
|
});
|
|
468
235
|
|
|
@@ -477,9 +244,7 @@ export const saveUserClinics = mutation({
|
|
|
477
244
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
478
245
|
.first();
|
|
479
246
|
|
|
480
|
-
if (!profile)
|
|
481
|
-
throw new Error('User profile not found');
|
|
482
|
-
}
|
|
247
|
+
if (!profile) throw new Error('User settings not found');
|
|
483
248
|
|
|
484
249
|
const optimizedClinics = args.primoupClinics.map((clinic: any) => ({
|
|
485
250
|
id: clinic.id,
|
|
@@ -509,41 +274,20 @@ export const updateSelectedClinic = mutation({
|
|
|
509
274
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
510
275
|
.first();
|
|
511
276
|
|
|
512
|
-
if (!profile)
|
|
513
|
-
throw new Error('User profile not found');
|
|
514
|
-
}
|
|
277
|
+
if (!profile) throw new Error('User settings not found');
|
|
515
278
|
|
|
516
279
|
if (profile.primoupClinics) {
|
|
517
280
|
const hasClinic = profile.primoupClinics.some((clinic: any) => clinic.id === args.clinicId);
|
|
518
|
-
if (!hasClinic)
|
|
519
|
-
throw new Error('Clinic not found in user clinics');
|
|
520
|
-
}
|
|
281
|
+
if (!hasClinic) throw new Error('Clinic not found in user clinics');
|
|
521
282
|
}
|
|
522
283
|
|
|
523
|
-
const clinic = await ctx.db
|
|
524
|
-
.query('clinics')
|
|
525
|
-
.withIndex('by_primoupId', (q) => q.eq('primoupId', args.clinicId.toString()))
|
|
526
|
-
.first();
|
|
527
|
-
|
|
528
|
-
if (!clinic) {
|
|
529
|
-
console.warn(`⚠️ Clinic NOT FOUND in Convex with primoupId: ${args.clinicId}`);
|
|
530
|
-
const newCounter = (profile.clinicChangeCounter || 0) + 1;
|
|
531
|
-
await ctx.db.patch(profile._id, {
|
|
532
|
-
selectedClinicId: args.clinicId,
|
|
533
|
-
clinicId: undefined,
|
|
534
|
-
clinicChangeCounter: newCounter,
|
|
535
|
-
});
|
|
536
|
-
return profile._id;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
284
|
const newCounter = (profile.clinicChangeCounter || 0) + 1;
|
|
540
|
-
|
|
285
|
+
|
|
541
286
|
await ctx.db.patch(profile._id, {
|
|
542
287
|
selectedClinicId: args.clinicId,
|
|
543
|
-
clinicId: clinic._id,
|
|
544
288
|
clinicChangeCounter: newCounter,
|
|
545
289
|
});
|
|
546
|
-
|
|
290
|
+
|
|
547
291
|
return profile._id;
|
|
548
292
|
},
|
|
549
293
|
});
|
|
@@ -551,7 +295,7 @@ export const updateSelectedClinic = mutation({
|
|
|
551
295
|
export const startImpersonation = mutation({
|
|
552
296
|
args: {
|
|
553
297
|
auth0Id: v.string(),
|
|
554
|
-
|
|
298
|
+
targetUserAuth0Id: v.string(),
|
|
555
299
|
},
|
|
556
300
|
handler: async (ctx, args) => {
|
|
557
301
|
const currentUser = await ctx.db
|
|
@@ -559,36 +303,24 @@ export const startImpersonation = mutation({
|
|
|
559
303
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
560
304
|
.first();
|
|
561
305
|
|
|
562
|
-
if (!currentUser)
|
|
563
|
-
throw new Error('User profile not found');
|
|
564
|
-
}
|
|
306
|
+
if (!currentUser) throw new Error('User settings not found');
|
|
565
307
|
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
308
|
+
const targetUser = await ctx.db
|
|
309
|
+
.query('user_profiles')
|
|
310
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.targetUserAuth0Id))
|
|
311
|
+
.first();
|
|
570
312
|
|
|
571
|
-
|
|
572
|
-
if (!targetUser) {
|
|
573
|
-
throw new Error('Utente target non trovato');
|
|
574
|
-
}
|
|
313
|
+
if (!targetUser) throw new Error('Target user settings not found');
|
|
575
314
|
|
|
576
315
|
if (targetUser._id === currentUser._id) {
|
|
577
316
|
throw new Error('Non puoi impersonare te stesso');
|
|
578
317
|
}
|
|
579
318
|
|
|
580
319
|
await ctx.db.patch(currentUser._id, {
|
|
581
|
-
impersonatingUserId: args.
|
|
320
|
+
impersonatingUserId: args.targetUserAuth0Id,
|
|
582
321
|
});
|
|
583
322
|
|
|
584
|
-
return {
|
|
585
|
-
success: true,
|
|
586
|
-
impersonatedUser: {
|
|
587
|
-
id: targetUser._id,
|
|
588
|
-
name: targetUser.name,
|
|
589
|
-
email: targetUser.email,
|
|
590
|
-
},
|
|
591
|
-
};
|
|
323
|
+
return { success: true };
|
|
592
324
|
},
|
|
593
325
|
});
|
|
594
326
|
|
|
@@ -600,14 +332,7 @@ export const stopImpersonation = mutation({
|
|
|
600
332
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
601
333
|
.first();
|
|
602
334
|
|
|
603
|
-
if (!currentUser)
|
|
604
|
-
throw new Error('User profile not found');
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const currentRole = currentUser.roleId ? await ctx.db.get(currentUser.roleId) : null;
|
|
608
|
-
if (!currentRole || currentRole.name !== 'admin') {
|
|
609
|
-
throw new Error('Solo gli amministratori possono terminare l\'impersonazione');
|
|
610
|
-
}
|
|
335
|
+
if (!currentUser) throw new Error('User settings not found');
|
|
611
336
|
|
|
612
337
|
await ctx.db.patch(currentUser._id, {
|
|
613
338
|
impersonatingUserId: undefined,
|
|
@@ -617,108 +342,6 @@ export const stopImpersonation = mutation({
|
|
|
617
342
|
},
|
|
618
343
|
});
|
|
619
344
|
|
|
620
|
-
export const updateUserProfile = mutation({
|
|
621
|
-
args: {
|
|
622
|
-
userId: v.id('user_profiles'),
|
|
623
|
-
name: v.optional(v.string()),
|
|
624
|
-
email: v.optional(v.string()),
|
|
625
|
-
roleName: v.optional(v.string()),
|
|
626
|
-
clinicId: v.optional(v.id('clinics')),
|
|
627
|
-
supplierId: v.optional(v.id('suppliers')),
|
|
628
|
-
},
|
|
629
|
-
handler: async (ctx, args) => {
|
|
630
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
631
|
-
if (!targetUser) {
|
|
632
|
-
throw new Error('Target user not found');
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const updateData: Record<string, any> = {};
|
|
636
|
-
|
|
637
|
-
if (args.name !== undefined) {
|
|
638
|
-
updateData.name = args.name;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (args.email !== undefined) {
|
|
642
|
-
const existingUser = await ctx.db
|
|
643
|
-
.query('user_profiles')
|
|
644
|
-
.withIndex('by_email', (q) => q.eq('email', args.email!))
|
|
645
|
-
.first();
|
|
646
|
-
|
|
647
|
-
if (existingUser && existingUser._id !== args.userId) {
|
|
648
|
-
throw new Error('Email già in uso da un altro utente');
|
|
649
|
-
}
|
|
650
|
-
updateData.email = args.email;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (args.roleName !== undefined) {
|
|
654
|
-
const newRole = await ctx.db
|
|
655
|
-
.query('roles')
|
|
656
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName!))
|
|
657
|
-
.first();
|
|
658
|
-
|
|
659
|
-
if (!newRole) {
|
|
660
|
-
throw new Error(`Ruolo ${args.roleName} non trovato`);
|
|
661
|
-
}
|
|
662
|
-
updateData.roleId = newRole._id;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
if (args.clinicId !== undefined) {
|
|
666
|
-
updateData.clinicId = args.clinicId;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (args.supplierId !== undefined) {
|
|
670
|
-
updateData.supplierId = args.supplierId;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
if (Object.keys(updateData).length > 0) {
|
|
674
|
-
await ctx.db.patch(args.userId, updateData);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return args.userId;
|
|
678
|
-
},
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
export const disableUser = mutation({
|
|
682
|
-
args: {
|
|
683
|
-
userId: v.id('user_profiles'),
|
|
684
|
-
callerUserId: v.optional(v.id('user_profiles')),
|
|
685
|
-
},
|
|
686
|
-
handler: async (ctx, args) => {
|
|
687
|
-
if (args.callerUserId && args.userId === args.callerUserId) {
|
|
688
|
-
throw new Error('Non puoi disabilitare te stesso');
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
692
|
-
if (!targetUser) {
|
|
693
|
-
throw new Error('Utente non trovato');
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
await ctx.db.patch(args.userId, {
|
|
697
|
-
isActive: false,
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
return { success: true, userId: args.userId };
|
|
701
|
-
},
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
export const enableUser = mutation({
|
|
705
|
-
args: {
|
|
706
|
-
userId: v.id('user_profiles'),
|
|
707
|
-
},
|
|
708
|
-
handler: async (ctx, args) => {
|
|
709
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
710
|
-
if (!targetUser) {
|
|
711
|
-
throw new Error('Utente non trovato');
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
await ctx.db.patch(args.userId, {
|
|
715
|
-
isActive: true,
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
return { success: true, userId: args.userId };
|
|
719
|
-
},
|
|
720
|
-
});
|
|
721
|
-
|
|
722
345
|
export const getRealAdminProfile = query({
|
|
723
346
|
args: { auth0Id: v.string() },
|
|
724
347
|
handler: async (ctx, args) => {
|
|
@@ -727,17 +350,10 @@ export const getRealAdminProfile = query({
|
|
|
727
350
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
728
351
|
.first();
|
|
729
352
|
|
|
730
|
-
if (!profile)
|
|
731
|
-
return null;
|
|
732
|
-
}
|
|
353
|
+
if (!profile) return null;
|
|
733
354
|
|
|
734
|
-
const role = profile.roleId ? await ctx.db.get(profile.roleId) : null;
|
|
735
|
-
|
|
736
355
|
return {
|
|
737
356
|
_id: profile._id,
|
|
738
|
-
name: profile.name,
|
|
739
|
-
email: profile.email,
|
|
740
|
-
role: role?.name,
|
|
741
357
|
isImpersonating: !!profile.impersonatingUserId,
|
|
742
358
|
impersonatingUserId: profile.impersonatingUserId,
|
|
743
359
|
};
|