@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,277 +1,121 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
2
|
import { query, mutation } from './_generated/server';
|
|
3
|
-
import { paginationOptsValidator } from 'convex/server';
|
|
4
3
|
export const getUserProfile = query({
|
|
5
4
|
args: { auth0Id: v.string() },
|
|
6
5
|
handler: async (ctx, args) => {
|
|
7
|
-
|
|
6
|
+
return await ctx.db
|
|
8
7
|
.query('user_profiles')
|
|
9
8
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
10
9
|
.first();
|
|
11
|
-
return profile;
|
|
12
10
|
},
|
|
13
11
|
});
|
|
14
12
|
export const getCurrentUserProfile = query({
|
|
15
|
-
args: {
|
|
13
|
+
args: {
|
|
14
|
+
auth0Id: v.string(),
|
|
15
|
+
userName: v.optional(v.string()),
|
|
16
|
+
userEmail: v.optional(v.string()),
|
|
17
|
+
userRole: v.optional(v.string()),
|
|
18
|
+
userIsActive: v.optional(v.boolean()),
|
|
19
|
+
},
|
|
16
20
|
handler: async (ctx, args) => {
|
|
17
|
-
const
|
|
21
|
+
const settings = await ctx.db
|
|
18
22
|
.query('user_profiles')
|
|
19
23
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
20
24
|
.first();
|
|
21
|
-
if (!
|
|
25
|
+
if (!settings) {
|
|
22
26
|
return null;
|
|
23
27
|
}
|
|
24
|
-
if (
|
|
28
|
+
if (args.userIsActive === false) {
|
|
25
29
|
return {
|
|
26
|
-
...
|
|
30
|
+
...settings,
|
|
31
|
+
name: args.userName,
|
|
32
|
+
email: args.userEmail,
|
|
27
33
|
role: null,
|
|
28
34
|
isDisabled: true,
|
|
29
35
|
isImpersonating: false,
|
|
30
36
|
};
|
|
31
37
|
}
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
: null;
|
|
38
|
+
const isAdmin = args.userRole === 'admin';
|
|
39
|
+
if (isAdmin && settings.impersonatingUserId) {
|
|
40
|
+
const impersonatedSettings = await ctx.db
|
|
41
|
+
.query('user_profiles')
|
|
42
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', settings.impersonatingUserId))
|
|
43
|
+
.first();
|
|
44
|
+
if (impersonatedSettings) {
|
|
40
45
|
return {
|
|
41
|
-
...
|
|
42
|
-
role:
|
|
46
|
+
...impersonatedSettings,
|
|
47
|
+
role: undefined,
|
|
43
48
|
isImpersonating: true,
|
|
44
|
-
realAdminId:
|
|
45
|
-
realAdminName:
|
|
46
|
-
realAdminEmail:
|
|
49
|
+
realAdminId: settings._id,
|
|
50
|
+
realAdminName: args.userName,
|
|
51
|
+
realAdminEmail: args.userEmail,
|
|
47
52
|
isDisabled: false,
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
return {
|
|
52
|
-
...
|
|
53
|
-
|
|
57
|
+
...settings,
|
|
58
|
+
name: args.userName,
|
|
59
|
+
email: args.userEmail,
|
|
60
|
+
role: args.userRole,
|
|
54
61
|
isImpersonating: false,
|
|
55
62
|
isDisabled: false,
|
|
56
63
|
};
|
|
57
64
|
},
|
|
58
65
|
});
|
|
59
|
-
function isPlaceholderAuth0Id(auth0Id) {
|
|
60
|
-
return auth0Id.startsWith('placeholder_') ||
|
|
61
|
-
auth0Id.startsWith('manual_') ||
|
|
62
|
-
!auth0Id.includes('|');
|
|
63
|
-
}
|
|
64
66
|
export const upsertUserProfile = mutation({
|
|
65
67
|
args: {
|
|
66
68
|
auth0Id: v.string(),
|
|
67
|
-
|
|
68
|
-
name: v.optional(v.string()),
|
|
69
|
-
roleName: v.optional(v.string()),
|
|
70
|
-
clinicId: v.optional(v.id('clinics')),
|
|
69
|
+
clinicId: v.optional(v.string()),
|
|
71
70
|
supplierId: v.optional(v.id('suppliers')),
|
|
72
71
|
},
|
|
73
72
|
handler: async (ctx, args) => {
|
|
74
|
-
|
|
73
|
+
const existing = await ctx.db
|
|
75
74
|
.query('user_profiles')
|
|
76
75
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
77
76
|
.first();
|
|
78
|
-
let shouldUpdateAuth0Id = false;
|
|
79
|
-
if (!existing) {
|
|
80
|
-
const existingByEmail = await ctx.db
|
|
81
|
-
.query('user_profiles')
|
|
82
|
-
.withIndex('by_email', (q) => q.eq('email', args.email))
|
|
83
|
-
.first();
|
|
84
|
-
if (existingByEmail && isPlaceholderAuth0Id(existingByEmail.auth0Id)) {
|
|
85
|
-
existing = existingByEmail;
|
|
86
|
-
shouldUpdateAuth0Id = true;
|
|
87
|
-
console.log(`🔄 Found pre-created user with email ${args.email}, updating auth0Id from placeholder to real Auth0 ID`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
let roleId = existing?.roleId;
|
|
91
|
-
if (args.roleName || !roleId) {
|
|
92
|
-
const roleName = args.roleName || 'user';
|
|
93
|
-
let role = await ctx.db
|
|
94
|
-
.query('roles')
|
|
95
|
-
.withIndex('by_name', (q) => q.eq('name', roleName))
|
|
96
|
-
.first();
|
|
97
|
-
if (!role) {
|
|
98
|
-
const newRoleId = await ctx.db.insert('roles', {
|
|
99
|
-
name: roleName,
|
|
100
|
-
description: roleName === 'admin' ? 'Administrator with full access' :
|
|
101
|
-
roleName === 'user' ? 'Standard user with limited access' :
|
|
102
|
-
roleName === 'supplier' ? 'Supplier with access to assigned tickets' :
|
|
103
|
-
`${roleName} role`,
|
|
104
|
-
createdAt: Date.now(),
|
|
105
|
-
});
|
|
106
|
-
role = await ctx.db.get(newRoleId);
|
|
107
|
-
}
|
|
108
|
-
if (role) {
|
|
109
|
-
roleId = role._id;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
77
|
if (existing) {
|
|
113
|
-
const updateData = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
updateData.auth0Id = args.auth0Id;
|
|
121
|
-
console.log(`✅ Updated auth0Id for user ${args.email}`);
|
|
122
|
-
}
|
|
123
|
-
if (roleId) {
|
|
124
|
-
updateData.roleId = roleId;
|
|
78
|
+
const updateData = {};
|
|
79
|
+
if (args.clinicId !== undefined)
|
|
80
|
+
updateData.clinicId = args.clinicId;
|
|
81
|
+
if (args.supplierId !== undefined)
|
|
82
|
+
updateData.supplierId = args.supplierId;
|
|
83
|
+
if (Object.keys(updateData).length > 0) {
|
|
84
|
+
await ctx.db.patch(existing._id, updateData);
|
|
125
85
|
}
|
|
126
|
-
await ctx.db.patch(existing._id, updateData);
|
|
127
86
|
return existing._id;
|
|
128
87
|
}
|
|
129
88
|
else {
|
|
130
|
-
const
|
|
89
|
+
const profileId = await ctx.db.insert('user_profiles', {
|
|
131
90
|
auth0Id: args.auth0Id,
|
|
132
|
-
email: args.email,
|
|
133
|
-
name: args.name,
|
|
134
91
|
...(args.clinicId && { clinicId: args.clinicId }),
|
|
135
92
|
...(args.supplierId && { supplierId: args.supplierId }),
|
|
136
|
-
};
|
|
137
|
-
if (roleId) {
|
|
138
|
-
newProfile.roleId = roleId;
|
|
139
|
-
}
|
|
140
|
-
const profileId = await ctx.db.insert('user_profiles', newProfile);
|
|
93
|
+
});
|
|
141
94
|
return profileId;
|
|
142
95
|
}
|
|
143
96
|
},
|
|
144
97
|
});
|
|
145
|
-
export const updateUserRole = mutation({
|
|
146
|
-
args: {
|
|
147
|
-
userId: v.id('user_profiles'),
|
|
148
|
-
roleName: v.string(),
|
|
149
|
-
clinicId: v.optional(v.id('clinics')),
|
|
150
|
-
supplierId: v.optional(v.id('suppliers')),
|
|
151
|
-
},
|
|
152
|
-
handler: async (ctx, args) => {
|
|
153
|
-
const newRole = await ctx.db
|
|
154
|
-
.query('roles')
|
|
155
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName))
|
|
156
|
-
.first();
|
|
157
|
-
if (!newRole) {
|
|
158
|
-
throw new Error(`Role ${args.roleName} not found`);
|
|
159
|
-
}
|
|
160
|
-
await ctx.db.patch(args.userId, {
|
|
161
|
-
roleId: newRole._id,
|
|
162
|
-
...(args.clinicId !== undefined && { clinicId: args.clinicId }),
|
|
163
|
-
...(args.supplierId !== undefined && { supplierId: args.supplierId }),
|
|
164
|
-
});
|
|
165
|
-
return args.userId;
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
98
|
export const updateUserSupplier = mutation({
|
|
169
99
|
args: {
|
|
170
|
-
userId: v.
|
|
100
|
+
userId: v.string(),
|
|
171
101
|
supplierId: v.id('suppliers'),
|
|
172
102
|
},
|
|
173
103
|
handler: async (ctx, args) => {
|
|
174
|
-
await ctx.db
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
104
|
+
const profile = await ctx.db
|
|
105
|
+
.query('user_profiles')
|
|
106
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.userId))
|
|
107
|
+
.first();
|
|
108
|
+
if (!profile)
|
|
109
|
+
throw new Error('User settings not found');
|
|
110
|
+
await ctx.db.patch(profile._id, { supplierId: args.supplierId });
|
|
111
|
+
return profile._id;
|
|
178
112
|
},
|
|
179
113
|
});
|
|
180
114
|
export const listUsers = query({
|
|
181
|
-
args: {
|
|
182
|
-
limit: v.optional(v.number()),
|
|
183
|
-
},
|
|
115
|
+
args: { limit: v.optional(v.number()) },
|
|
184
116
|
handler: async (ctx, args) => {
|
|
185
117
|
const maxResults = args.limit || 500;
|
|
186
|
-
|
|
187
|
-
const usersWithRoles = await Promise.all(users.map(async (user) => {
|
|
188
|
-
const role = user.roleId ? await ctx.db.get(user.roleId) : null;
|
|
189
|
-
return {
|
|
190
|
-
...user,
|
|
191
|
-
role: role?.name || user.role,
|
|
192
|
-
};
|
|
193
|
-
}));
|
|
194
|
-
return usersWithRoles;
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
export const listUsersPaginated = query({
|
|
198
|
-
args: {
|
|
199
|
-
paginationOpts: paginationOptsValidator,
|
|
200
|
-
roleId: v.optional(v.id('roles')),
|
|
201
|
-
isActive: v.optional(v.boolean()),
|
|
202
|
-
searchTerm: v.optional(v.string()),
|
|
203
|
-
},
|
|
204
|
-
handler: async (ctx, args) => {
|
|
205
|
-
let baseQuery;
|
|
206
|
-
if (args.roleId) {
|
|
207
|
-
baseQuery = ctx.db
|
|
208
|
-
.query('user_profiles')
|
|
209
|
-
.withIndex('by_roleId', (q) => q.eq('roleId', args.roleId));
|
|
210
|
-
}
|
|
211
|
-
else if (args.isActive !== undefined) {
|
|
212
|
-
baseQuery = ctx.db
|
|
213
|
-
.query('user_profiles')
|
|
214
|
-
.withIndex('by_isActive', (q) => q.eq('isActive', args.isActive));
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
baseQuery = ctx.db.query('user_profiles');
|
|
218
|
-
}
|
|
219
|
-
let filteredQuery = baseQuery.order('desc');
|
|
220
|
-
if (args.isActive !== undefined && args.roleId) {
|
|
221
|
-
filteredQuery = filteredQuery.filter((q) => q.eq(q.field('isActive'), args.isActive));
|
|
222
|
-
}
|
|
223
|
-
const result = await filteredQuery.paginate(args.paginationOpts);
|
|
224
|
-
let filteredPage = result.page;
|
|
225
|
-
if (args.searchTerm) {
|
|
226
|
-
const search = args.searchTerm.toLowerCase();
|
|
227
|
-
filteredPage = filteredPage.filter((user) => user.name?.toLowerCase().includes(search) ||
|
|
228
|
-
user.email?.toLowerCase().includes(search));
|
|
229
|
-
}
|
|
230
|
-
const roleIds = [...new Set(filteredPage.map((u) => u.roleId).filter(Boolean))];
|
|
231
|
-
const roles = await Promise.all(roleIds.map(id => ctx.db.get(id)));
|
|
232
|
-
const roleMap = new Map(roles.filter(Boolean).map((r) => [r._id.toString(), r.name]));
|
|
233
|
-
const minimalPage = filteredPage.map((user) => ({
|
|
234
|
-
_id: user._id,
|
|
235
|
-
auth0Id: user.auth0Id,
|
|
236
|
-
email: user.email,
|
|
237
|
-
name: user.name,
|
|
238
|
-
roleId: user.roleId,
|
|
239
|
-
role: roleMap.get(user.roleId?.toString()) || user.role || 'user',
|
|
240
|
-
isActive: user.isActive,
|
|
241
|
-
clinicId: user.clinicId,
|
|
242
|
-
supplierId: user.supplierId,
|
|
243
|
-
createdAt: user.createdAt || user._creationTime,
|
|
244
|
-
}));
|
|
245
|
-
return {
|
|
246
|
-
page: minimalPage,
|
|
247
|
-
isDone: result.isDone,
|
|
248
|
-
continueCursor: result.continueCursor,
|
|
249
|
-
};
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
export const getUsersCount = query({
|
|
253
|
-
args: {
|
|
254
|
-
roleId: v.optional(v.id('roles')),
|
|
255
|
-
isActive: v.optional(v.boolean()),
|
|
256
|
-
},
|
|
257
|
-
handler: async (ctx, args) => {
|
|
258
|
-
let query;
|
|
259
|
-
if (args.roleId) {
|
|
260
|
-
query = ctx.db.query('user_profiles')
|
|
261
|
-
.withIndex('by_roleId', (q) => q.eq('roleId', args.roleId));
|
|
262
|
-
}
|
|
263
|
-
else if (args.isActive !== undefined) {
|
|
264
|
-
query = ctx.db.query('user_profiles')
|
|
265
|
-
.withIndex('by_isActive', (q) => q.eq('isActive', args.isActive));
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
query = ctx.db.query('user_profiles');
|
|
269
|
-
}
|
|
270
|
-
if (args.isActive !== undefined && args.roleId) {
|
|
271
|
-
query = query.filter((q) => q.eq(q.field('isActive'), args.isActive));
|
|
272
|
-
}
|
|
273
|
-
const users = await query.take(1000);
|
|
274
|
-
return { total: users.length };
|
|
118
|
+
return await ctx.db.query('user_profiles').order('desc').take(maxResults);
|
|
275
119
|
},
|
|
276
120
|
});
|
|
277
121
|
export const savePrimoUPToken = mutation({
|
|
@@ -313,9 +157,8 @@ export const getPrimoUPToken = query({
|
|
|
313
157
|
.query('primoup_tokens')
|
|
314
158
|
.withIndex('by_userId_active', (q) => q.eq('userId', args.auth0Id).eq('isActive', true))
|
|
315
159
|
.first();
|
|
316
|
-
if (!activeToken)
|
|
160
|
+
if (!activeToken)
|
|
317
161
|
return null;
|
|
318
|
-
}
|
|
319
162
|
if (activeToken.expiresAt && activeToken.expiresAt < Date.now()) {
|
|
320
163
|
return {
|
|
321
164
|
token: null,
|
|
@@ -362,43 +205,11 @@ export const getPrimoUPAccessLogs = query({
|
|
|
362
205
|
},
|
|
363
206
|
handler: async (ctx, args) => {
|
|
364
207
|
const targetUserId = args.isAdmin && args.userId ? args.userId : args.auth0Id;
|
|
365
|
-
|
|
208
|
+
return await ctx.db
|
|
366
209
|
.query('primoup_access_logs')
|
|
367
210
|
.withIndex('by_userId_timestamp', (q) => q.eq('userId', targetUserId))
|
|
368
211
|
.order('desc')
|
|
369
212
|
.take(args.limit || 50);
|
|
370
|
-
return logs;
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
export const updateUserRoleByEmail = mutation({
|
|
374
|
-
args: {
|
|
375
|
-
email: v.string(),
|
|
376
|
-
roleName: v.string(),
|
|
377
|
-
},
|
|
378
|
-
handler: async (ctx, args) => {
|
|
379
|
-
const user = await ctx.db
|
|
380
|
-
.query('user_profiles')
|
|
381
|
-
.filter((q) => q.eq(q.field('email'), args.email))
|
|
382
|
-
.first();
|
|
383
|
-
if (!user) {
|
|
384
|
-
throw new Error(`User with email ${args.email} not found`);
|
|
385
|
-
}
|
|
386
|
-
const role = await ctx.db
|
|
387
|
-
.query('roles')
|
|
388
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName))
|
|
389
|
-
.first();
|
|
390
|
-
if (!role) {
|
|
391
|
-
throw new Error(`Role ${args.roleName} not found`);
|
|
392
|
-
}
|
|
393
|
-
await ctx.db.patch(user._id, {
|
|
394
|
-
roleId: role._id,
|
|
395
|
-
});
|
|
396
|
-
return {
|
|
397
|
-
success: true,
|
|
398
|
-
userId: user._id,
|
|
399
|
-
email: user.email,
|
|
400
|
-
newRole: args.roleName,
|
|
401
|
-
};
|
|
402
213
|
},
|
|
403
214
|
});
|
|
404
215
|
export const saveUserClinics = mutation({
|
|
@@ -411,9 +222,8 @@ export const saveUserClinics = mutation({
|
|
|
411
222
|
.query('user_profiles')
|
|
412
223
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
413
224
|
.first();
|
|
414
|
-
if (!profile)
|
|
415
|
-
throw new Error('User
|
|
416
|
-
}
|
|
225
|
+
if (!profile)
|
|
226
|
+
throw new Error('User settings not found');
|
|
417
227
|
const optimizedClinics = args.primoupClinics.map((clinic) => ({
|
|
418
228
|
id: clinic.id,
|
|
419
229
|
name: clinic.name,
|
|
@@ -437,33 +247,16 @@ export const updateSelectedClinic = mutation({
|
|
|
437
247
|
.query('user_profiles')
|
|
438
248
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
439
249
|
.first();
|
|
440
|
-
if (!profile)
|
|
441
|
-
throw new Error('User
|
|
442
|
-
}
|
|
250
|
+
if (!profile)
|
|
251
|
+
throw new Error('User settings not found');
|
|
443
252
|
if (profile.primoupClinics) {
|
|
444
253
|
const hasClinic = profile.primoupClinics.some((clinic) => clinic.id === args.clinicId);
|
|
445
|
-
if (!hasClinic)
|
|
254
|
+
if (!hasClinic)
|
|
446
255
|
throw new Error('Clinic not found in user clinics');
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
const clinic = await ctx.db
|
|
450
|
-
.query('clinics')
|
|
451
|
-
.withIndex('by_primoupId', (q) => q.eq('primoupId', args.clinicId.toString()))
|
|
452
|
-
.first();
|
|
453
|
-
if (!clinic) {
|
|
454
|
-
console.warn(`⚠️ Clinic NOT FOUND in Convex with primoupId: ${args.clinicId}`);
|
|
455
|
-
const newCounter = (profile.clinicChangeCounter || 0) + 1;
|
|
456
|
-
await ctx.db.patch(profile._id, {
|
|
457
|
-
selectedClinicId: args.clinicId,
|
|
458
|
-
clinicId: undefined,
|
|
459
|
-
clinicChangeCounter: newCounter,
|
|
460
|
-
});
|
|
461
|
-
return profile._id;
|
|
462
256
|
}
|
|
463
257
|
const newCounter = (profile.clinicChangeCounter || 0) + 1;
|
|
464
258
|
await ctx.db.patch(profile._id, {
|
|
465
259
|
selectedClinicId: args.clinicId,
|
|
466
|
-
clinicId: clinic._id,
|
|
467
260
|
clinicChangeCounter: newCounter,
|
|
468
261
|
});
|
|
469
262
|
return profile._id;
|
|
@@ -472,38 +265,28 @@ export const updateSelectedClinic = mutation({
|
|
|
472
265
|
export const startImpersonation = mutation({
|
|
473
266
|
args: {
|
|
474
267
|
auth0Id: v.string(),
|
|
475
|
-
|
|
268
|
+
targetUserAuth0Id: v.string(),
|
|
476
269
|
},
|
|
477
270
|
handler: async (ctx, args) => {
|
|
478
271
|
const currentUser = await ctx.db
|
|
479
272
|
.query('user_profiles')
|
|
480
273
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
481
274
|
.first();
|
|
482
|
-
if (!currentUser)
|
|
483
|
-
throw new Error('User
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if (!targetUser) {
|
|
491
|
-
throw new Error('Utente target non trovato');
|
|
492
|
-
}
|
|
275
|
+
if (!currentUser)
|
|
276
|
+
throw new Error('User settings not found');
|
|
277
|
+
const targetUser = await ctx.db
|
|
278
|
+
.query('user_profiles')
|
|
279
|
+
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.targetUserAuth0Id))
|
|
280
|
+
.first();
|
|
281
|
+
if (!targetUser)
|
|
282
|
+
throw new Error('Target user settings not found');
|
|
493
283
|
if (targetUser._id === currentUser._id) {
|
|
494
284
|
throw new Error('Non puoi impersonare te stesso');
|
|
495
285
|
}
|
|
496
286
|
await ctx.db.patch(currentUser._id, {
|
|
497
|
-
impersonatingUserId: args.
|
|
287
|
+
impersonatingUserId: args.targetUserAuth0Id,
|
|
498
288
|
});
|
|
499
|
-
return {
|
|
500
|
-
success: true,
|
|
501
|
-
impersonatedUser: {
|
|
502
|
-
id: targetUser._id,
|
|
503
|
-
name: targetUser.name,
|
|
504
|
-
email: targetUser.email,
|
|
505
|
-
},
|
|
506
|
-
};
|
|
289
|
+
return { success: true };
|
|
507
290
|
},
|
|
508
291
|
});
|
|
509
292
|
export const stopImpersonation = mutation({
|
|
@@ -513,103 +296,14 @@ export const stopImpersonation = mutation({
|
|
|
513
296
|
.query('user_profiles')
|
|
514
297
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
515
298
|
.first();
|
|
516
|
-
if (!currentUser)
|
|
517
|
-
throw new Error('User
|
|
518
|
-
}
|
|
519
|
-
const currentRole = currentUser.roleId ? await ctx.db.get(currentUser.roleId) : null;
|
|
520
|
-
if (!currentRole || currentRole.name !== 'admin') {
|
|
521
|
-
throw new Error('Solo gli amministratori possono terminare l\'impersonazione');
|
|
522
|
-
}
|
|
299
|
+
if (!currentUser)
|
|
300
|
+
throw new Error('User settings not found');
|
|
523
301
|
await ctx.db.patch(currentUser._id, {
|
|
524
302
|
impersonatingUserId: undefined,
|
|
525
303
|
});
|
|
526
304
|
return { success: true };
|
|
527
305
|
},
|
|
528
306
|
});
|
|
529
|
-
export const updateUserProfile = mutation({
|
|
530
|
-
args: {
|
|
531
|
-
userId: v.id('user_profiles'),
|
|
532
|
-
name: v.optional(v.string()),
|
|
533
|
-
email: v.optional(v.string()),
|
|
534
|
-
roleName: v.optional(v.string()),
|
|
535
|
-
clinicId: v.optional(v.id('clinics')),
|
|
536
|
-
supplierId: v.optional(v.id('suppliers')),
|
|
537
|
-
},
|
|
538
|
-
handler: async (ctx, args) => {
|
|
539
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
540
|
-
if (!targetUser) {
|
|
541
|
-
throw new Error('Target user not found');
|
|
542
|
-
}
|
|
543
|
-
const updateData = {};
|
|
544
|
-
if (args.name !== undefined) {
|
|
545
|
-
updateData.name = args.name;
|
|
546
|
-
}
|
|
547
|
-
if (args.email !== undefined) {
|
|
548
|
-
const existingUser = await ctx.db
|
|
549
|
-
.query('user_profiles')
|
|
550
|
-
.withIndex('by_email', (q) => q.eq('email', args.email))
|
|
551
|
-
.first();
|
|
552
|
-
if (existingUser && existingUser._id !== args.userId) {
|
|
553
|
-
throw new Error('Email già in uso da un altro utente');
|
|
554
|
-
}
|
|
555
|
-
updateData.email = args.email;
|
|
556
|
-
}
|
|
557
|
-
if (args.roleName !== undefined) {
|
|
558
|
-
const newRole = await ctx.db
|
|
559
|
-
.query('roles')
|
|
560
|
-
.withIndex('by_name', (q) => q.eq('name', args.roleName))
|
|
561
|
-
.first();
|
|
562
|
-
if (!newRole) {
|
|
563
|
-
throw new Error(`Ruolo ${args.roleName} non trovato`);
|
|
564
|
-
}
|
|
565
|
-
updateData.roleId = newRole._id;
|
|
566
|
-
}
|
|
567
|
-
if (args.clinicId !== undefined) {
|
|
568
|
-
updateData.clinicId = args.clinicId;
|
|
569
|
-
}
|
|
570
|
-
if (args.supplierId !== undefined) {
|
|
571
|
-
updateData.supplierId = args.supplierId;
|
|
572
|
-
}
|
|
573
|
-
if (Object.keys(updateData).length > 0) {
|
|
574
|
-
await ctx.db.patch(args.userId, updateData);
|
|
575
|
-
}
|
|
576
|
-
return args.userId;
|
|
577
|
-
},
|
|
578
|
-
});
|
|
579
|
-
export const disableUser = mutation({
|
|
580
|
-
args: {
|
|
581
|
-
userId: v.id('user_profiles'),
|
|
582
|
-
callerUserId: v.optional(v.id('user_profiles')),
|
|
583
|
-
},
|
|
584
|
-
handler: async (ctx, args) => {
|
|
585
|
-
if (args.callerUserId && args.userId === args.callerUserId) {
|
|
586
|
-
throw new Error('Non puoi disabilitare te stesso');
|
|
587
|
-
}
|
|
588
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
589
|
-
if (!targetUser) {
|
|
590
|
-
throw new Error('Utente non trovato');
|
|
591
|
-
}
|
|
592
|
-
await ctx.db.patch(args.userId, {
|
|
593
|
-
isActive: false,
|
|
594
|
-
});
|
|
595
|
-
return { success: true, userId: args.userId };
|
|
596
|
-
},
|
|
597
|
-
});
|
|
598
|
-
export const enableUser = mutation({
|
|
599
|
-
args: {
|
|
600
|
-
userId: v.id('user_profiles'),
|
|
601
|
-
},
|
|
602
|
-
handler: async (ctx, args) => {
|
|
603
|
-
const targetUser = await ctx.db.get(args.userId);
|
|
604
|
-
if (!targetUser) {
|
|
605
|
-
throw new Error('Utente non trovato');
|
|
606
|
-
}
|
|
607
|
-
await ctx.db.patch(args.userId, {
|
|
608
|
-
isActive: true,
|
|
609
|
-
});
|
|
610
|
-
return { success: true, userId: args.userId };
|
|
611
|
-
},
|
|
612
|
-
});
|
|
613
307
|
export const getRealAdminProfile = query({
|
|
614
308
|
args: { auth0Id: v.string() },
|
|
615
309
|
handler: async (ctx, args) => {
|
|
@@ -617,15 +311,10 @@ export const getRealAdminProfile = query({
|
|
|
617
311
|
.query('user_profiles')
|
|
618
312
|
.withIndex('by_auth0Id', (q) => q.eq('auth0Id', args.auth0Id))
|
|
619
313
|
.first();
|
|
620
|
-
if (!profile)
|
|
314
|
+
if (!profile)
|
|
621
315
|
return null;
|
|
622
|
-
}
|
|
623
|
-
const role = profile.roleId ? await ctx.db.get(profile.roleId) : null;
|
|
624
316
|
return {
|
|
625
317
|
_id: profile._id,
|
|
626
|
-
name: profile.name,
|
|
627
|
-
email: profile.email,
|
|
628
|
-
role: role?.name,
|
|
629
318
|
isImpersonating: !!profile.impersonatingUserId,
|
|
630
319
|
impersonatingUserId: profile.impersonatingUserId,
|
|
631
320
|
};
|