@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
|
@@ -10,21 +10,14 @@ export const listContracts = query({
|
|
|
10
10
|
contracts.map(async (contract) => {
|
|
11
11
|
const supplier = await ctx.db.get(contract.supplierId);
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
contract.clinicIds.map(id => ctx.db.get(id))
|
|
17
|
-
);
|
|
18
|
-
clinicNames = clinics.map(c => c?.name || 'Sconosciuta');
|
|
19
|
-
} else if (contract.clinicId) {
|
|
20
|
-
const clinic = await ctx.db.get(contract.clinicId);
|
|
21
|
-
clinicNames = clinic ? [clinic.name] : [];
|
|
22
|
-
}
|
|
13
|
+
const clinicNames = contract.clinicIds && contract.clinicIds.length > 0
|
|
14
|
+
? contract.clinicIds
|
|
15
|
+
: (contract.clinicId ? [contract.clinicId] : ['Tutte le cliniche']);
|
|
23
16
|
|
|
24
17
|
return {
|
|
25
18
|
...contract,
|
|
26
19
|
supplierName: supplier?.name || 'Unknown',
|
|
27
|
-
clinicNames
|
|
20
|
+
clinicNames,
|
|
28
21
|
};
|
|
29
22
|
})
|
|
30
23
|
);
|
|
@@ -46,7 +39,7 @@ export const getContractsBySupplier = query({
|
|
|
46
39
|
});
|
|
47
40
|
|
|
48
41
|
export const getContractsByClinic = query({
|
|
49
|
-
args: { clinicId: v.
|
|
42
|
+
args: { clinicId: v.string() },
|
|
50
43
|
handler: async (ctx, args) => {
|
|
51
44
|
const contracts = await ctx.db
|
|
52
45
|
.query('contracts')
|
|
@@ -60,7 +53,7 @@ export const getContractsByClinic = query({
|
|
|
60
53
|
export const createContract = mutation({
|
|
61
54
|
args: {
|
|
62
55
|
supplierId: v.id('suppliers'),
|
|
63
|
-
clinicIds: v.optional(v.array(v.
|
|
56
|
+
clinicIds: v.optional(v.array(v.string())),
|
|
64
57
|
start_date: v.number(),
|
|
65
58
|
end_date: v.number(),
|
|
66
59
|
amount: v.number(),
|
|
@@ -91,7 +84,7 @@ export const createContract = mutation({
|
|
|
91
84
|
export const updateContract = mutation({
|
|
92
85
|
args: {
|
|
93
86
|
contractId: v.id('contracts'),
|
|
94
|
-
clinicIds: v.optional(v.array(v.
|
|
87
|
+
clinicIds: v.optional(v.array(v.string())),
|
|
95
88
|
start_date: v.optional(v.number()),
|
|
96
89
|
end_date: v.optional(v.number()),
|
|
97
90
|
description: v.optional(v.string()),
|
package/src/component/crons.ts
CHANGED
|
@@ -3,16 +3,11 @@ import { internal } from './_generated/api';
|
|
|
3
3
|
|
|
4
4
|
const crons = cronJobs();
|
|
5
5
|
|
|
6
|
-
crons.cron(
|
|
7
|
-
'check-sla-status',
|
|
8
|
-
'0 2 * * *',
|
|
9
|
-
internal.slaMonitoring.checkSLAStatus
|
|
10
|
-
);
|
|
11
|
-
|
|
12
6
|
crons.cron(
|
|
13
7
|
'compute-dashboard-stats',
|
|
14
8
|
'*/10 * * * *',
|
|
15
|
-
internal.dashboardStatsCache.computeAndStoreStats
|
|
9
|
+
internal.dashboardStatsCache.computeAndStoreStats,
|
|
10
|
+
{}
|
|
16
11
|
);
|
|
17
12
|
|
|
18
13
|
export default crons;
|
|
@@ -1,31 +1,28 @@
|
|
|
1
1
|
import { query } from './_generated/server';
|
|
2
|
+
import { v } from 'convex/values';
|
|
2
3
|
|
|
3
4
|
export const getAdminDashboardStats = query({
|
|
4
|
-
args: {
|
|
5
|
-
|
|
5
|
+
args: {
|
|
6
|
+
totalClinics: v.optional(v.number()),
|
|
7
|
+
ticketStats: v.optional(v.any()),
|
|
8
|
+
},
|
|
9
|
+
handler: async (ctx, args) => {
|
|
6
10
|
try {
|
|
7
11
|
const cache = await ctx.db.query('dashboard_stats_cache').first();
|
|
8
12
|
if (cache && cache.lastUpdated) {
|
|
9
13
|
const ageMs = Date.now() - cache.lastUpdated;
|
|
10
14
|
if (ageMs < 15 * 60 * 1000) {
|
|
11
15
|
return {
|
|
12
|
-
totalClinics: cache.totalClinics,
|
|
16
|
+
totalClinics: args.totalClinics ?? cache.totalClinics,
|
|
13
17
|
totalDevices: cache.totalDevices,
|
|
14
18
|
totalSuppliers: cache.totalSuppliers,
|
|
15
19
|
activeContracts: 0,
|
|
16
|
-
ticketStats: cache.ticketStats
|
|
20
|
+
ticketStats: args.ticketStats ?? cache.ticketStats ?? {},
|
|
17
21
|
};
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
const
|
|
22
|
-
ctx.db.query('clinics').take(500),
|
|
23
|
-
ctx.db.query('suppliers').take(500),
|
|
24
|
-
ctx.db
|
|
25
|
-
.query('ticket_statuses')
|
|
26
|
-
.withIndex('by_isActive', (q: any) => q.eq('isActive', true))
|
|
27
|
-
.collect(),
|
|
28
|
-
]);
|
|
25
|
+
const suppliers = await ctx.db.query('suppliers').take(500);
|
|
29
26
|
|
|
30
27
|
const DEVICE_LIMIT = 300;
|
|
31
28
|
const deviceStatuses = ['active', 'in_maintenance', 'out_of_service'];
|
|
@@ -37,22 +34,12 @@ export const getAdminDashboardStats = query({
|
|
|
37
34
|
.take(DEVICE_LIMIT)).length;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
const TICKET_LIMIT = 300;
|
|
41
|
-
const ticketStats: any = {};
|
|
42
|
-
for (const status of statuses) {
|
|
43
|
-
const count = (await ctx.db
|
|
44
|
-
.query('maintenance_tasks')
|
|
45
|
-
.withIndex('by_status', (q: any) => q.eq('status', status.name))
|
|
46
|
-
.take(TICKET_LIMIT)).length;
|
|
47
|
-
ticketStats[status.name] = count;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
37
|
return {
|
|
51
|
-
totalClinics:
|
|
38
|
+
totalClinics: args.totalClinics ?? 0,
|
|
52
39
|
totalDevices,
|
|
53
40
|
totalSuppliers: suppliers.length,
|
|
54
41
|
activeContracts: 0,
|
|
55
|
-
ticketStats,
|
|
42
|
+
ticketStats: args.ticketStats ?? {},
|
|
56
43
|
};
|
|
57
44
|
} catch (error) {
|
|
58
45
|
console.error('Error in getAdminDashboardStats:', error);
|
|
@@ -68,41 +55,37 @@ export const getAdminDashboardStats = query({
|
|
|
68
55
|
});
|
|
69
56
|
|
|
70
57
|
export const getRecentActivity = query({
|
|
71
|
-
args: {
|
|
72
|
-
|
|
58
|
+
args: {
|
|
59
|
+
recentTickets: v.optional(v.array(v.any())),
|
|
60
|
+
},
|
|
61
|
+
handler: async (ctx, args) => {
|
|
73
62
|
try {
|
|
74
|
-
const recentTasks =
|
|
75
|
-
.query('maintenance_tasks')
|
|
76
|
-
.order('desc')
|
|
77
|
-
.take(10);
|
|
78
|
-
|
|
63
|
+
const recentTasks = args.recentTickets || [];
|
|
79
64
|
const statuses = await ctx.db.query('ticket_statuses').collect();
|
|
80
65
|
const statusMap = new Map(statuses.map(s => [s.name, s.label]));
|
|
81
66
|
const activities = [];
|
|
82
67
|
|
|
83
68
|
for (const task of recentTasks) {
|
|
84
|
-
const device = task.deviceId ? await ctx.db.get(task.deviceId) : null;
|
|
85
|
-
const clinic = task.clinicId ? await ctx.db.get(task.clinicId) : null;
|
|
86
69
|
const statusLabel = statusMap.get(task.status) || task.status;
|
|
70
|
+
const deviceName = task.deviceName || 'Unknown';
|
|
87
71
|
|
|
88
72
|
let message = '';
|
|
89
73
|
if (task.status === 'open') {
|
|
90
|
-
message = `Nuovo ticket creato per dispositivo ${
|
|
74
|
+
message = `Nuovo ticket creato per dispositivo ${deviceName}`;
|
|
91
75
|
} else if (task.status === 'completed' || task.status === 'closed') {
|
|
92
|
-
message = `Ticket completato per dispositivo ${
|
|
76
|
+
message = `Ticket completato per dispositivo ${deviceName}`;
|
|
93
77
|
} else if (task.status === 'assigned_to_supplier') {
|
|
94
|
-
|
|
95
|
-
message = `Ticket assegnato a ${supplier?.name || 'fornitore'}`;
|
|
78
|
+
message = `Ticket assegnato a ${task.supplierName || 'fornitore'}`;
|
|
96
79
|
} else {
|
|
97
|
-
message = `Ticket ${statusLabel} per dispositivo ${
|
|
80
|
+
message = `Ticket ${statusLabel} per dispositivo ${deviceName}`;
|
|
98
81
|
}
|
|
99
82
|
|
|
100
83
|
activities.push({
|
|
101
|
-
id: task._id,
|
|
84
|
+
id: task._id || task.ticketId,
|
|
102
85
|
type: 'ticket',
|
|
103
86
|
message,
|
|
104
|
-
time: task.created_at,
|
|
105
|
-
|
|
87
|
+
time: task.created_at || task.lastActivityAt,
|
|
88
|
+
clinicId: task.clinicId,
|
|
106
89
|
});
|
|
107
90
|
}
|
|
108
91
|
|
|
@@ -2,33 +2,6 @@ import { v } from 'convex/values';
|
|
|
2
2
|
import { internalQuery, internalMutation, internalAction } from './_generated/server';
|
|
3
3
|
import { internal } from './_generated/api';
|
|
4
4
|
|
|
5
|
-
const BATCH_SIZE = 50;
|
|
6
|
-
|
|
7
|
-
export const countTicketsBatch = internalQuery({
|
|
8
|
-
args: {
|
|
9
|
-
status: v.string(),
|
|
10
|
-
cursor: v.optional(v.number()),
|
|
11
|
-
},
|
|
12
|
-
handler: async (ctx, args) => {
|
|
13
|
-
const q = ctx.db
|
|
14
|
-
.query('maintenance_tasks')
|
|
15
|
-
.withIndex('by_status_created_at', (q) =>
|
|
16
|
-
args.cursor != null
|
|
17
|
-
? q.eq('status', args.status).lt('created_at', args.cursor)
|
|
18
|
-
: q.eq('status', args.status)
|
|
19
|
-
)
|
|
20
|
-
.order('desc')
|
|
21
|
-
.take(BATCH_SIZE);
|
|
22
|
-
|
|
23
|
-
const batch = await q;
|
|
24
|
-
return {
|
|
25
|
-
count: batch.length,
|
|
26
|
-
hasMore: batch.length === BATCH_SIZE,
|
|
27
|
-
lastCursor: batch.length > 0 ? batch[batch.length - 1].created_at : null,
|
|
28
|
-
};
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
5
|
export const countDevicesBatch = internalQuery({
|
|
33
6
|
args: { status: v.union(v.literal('active'), v.literal('in_maintenance'), v.literal('out_of_service')) },
|
|
34
7
|
handler: async (ctx, args) => {
|
|
@@ -41,63 +14,41 @@ export const countDevicesBatch = internalQuery({
|
|
|
41
14
|
});
|
|
42
15
|
|
|
43
16
|
export const computeAndStoreStats = internalAction({
|
|
44
|
-
args: {
|
|
45
|
-
|
|
17
|
+
args: {
|
|
18
|
+
ticketStats: v.optional(v.any()),
|
|
19
|
+
totalTickets: v.optional(v.number()),
|
|
20
|
+
},
|
|
21
|
+
handler: async (ctx, args) => {
|
|
46
22
|
const now = Date.now();
|
|
47
23
|
|
|
48
|
-
const
|
|
49
|
-
ctx.runQuery(internal.dashboardStatsCache.countTable, { table: 'clinics' }),
|
|
50
|
-
ctx.runQuery(internal.dashboardStatsCache.countTable, { table: 'suppliers' }),
|
|
51
|
-
ctx.runQuery(internal.dashboardStatsCache.getActiveStatuses, {}),
|
|
52
|
-
]);
|
|
24
|
+
const suppliers = await ctx.runQuery(internal.dashboardStatsCache.countTable, { table: 'suppliers' });
|
|
53
25
|
|
|
54
26
|
const deviceStatuses = ['active', 'in_maintenance', 'out_of_service'] as const;
|
|
55
27
|
const deviceCountByStatus: Record<string, number> = {};
|
|
56
28
|
let totalDevices = 0;
|
|
57
29
|
|
|
58
30
|
for (const status of deviceStatuses) {
|
|
59
|
-
const r = await ctx.runQuery(internal.dashboardStatsCache.countDevicesBatch, {
|
|
60
|
-
status,
|
|
61
|
-
});
|
|
31
|
+
const r = await ctx.runQuery(internal.dashboardStatsCache.countDevicesBatch, { status });
|
|
62
32
|
deviceCountByStatus[status] = r.count;
|
|
63
33
|
totalDevices += r.count;
|
|
64
34
|
}
|
|
65
35
|
|
|
66
|
-
const ticketStats: Record<string, number> = {};
|
|
67
|
-
let totalTickets = 0;
|
|
68
|
-
|
|
69
|
-
for (const status of statuses) {
|
|
70
|
-
let cursor: number | null | undefined = undefined;
|
|
71
|
-
let total = 0;
|
|
72
|
-
do {
|
|
73
|
-
const batchResult: { count: number; hasMore: boolean; lastCursor: number | null } =
|
|
74
|
-
await ctx.runQuery(internal.dashboardStatsCache.countTicketsBatch, {
|
|
75
|
-
status,
|
|
76
|
-
cursor: cursor ?? undefined,
|
|
77
|
-
});
|
|
78
|
-
total += batchResult.count;
|
|
79
|
-
cursor = batchResult.hasMore ? batchResult.lastCursor ?? null : null;
|
|
80
|
-
} while (cursor != null);
|
|
81
|
-
ticketStats[status] = total;
|
|
82
|
-
totalTickets += total;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
36
|
await ctx.runMutation(internal.dashboardStatsCache.writeCache, {
|
|
86
37
|
lastUpdated: now,
|
|
87
|
-
totalClinics:
|
|
38
|
+
totalClinics: 0,
|
|
88
39
|
totalSuppliers: suppliers,
|
|
89
40
|
totalDevices,
|
|
90
41
|
deviceCountByStatus,
|
|
91
|
-
ticketStats,
|
|
92
|
-
totalTickets,
|
|
42
|
+
ticketStats: args.ticketStats ?? {},
|
|
43
|
+
totalTickets: args.totalTickets ?? 0,
|
|
93
44
|
});
|
|
94
45
|
|
|
95
|
-
return { ok: true,
|
|
46
|
+
return { ok: true, totalDevices };
|
|
96
47
|
},
|
|
97
48
|
});
|
|
98
49
|
|
|
99
50
|
export const countTable = internalQuery({
|
|
100
|
-
args: { table: v.union(v.literal('
|
|
51
|
+
args: { table: v.union(v.literal('suppliers')) },
|
|
101
52
|
handler: async (ctx, args) => {
|
|
102
53
|
const r = await ctx.db.query(args.table).take(2000);
|
|
103
54
|
return r.length;
|
|
@@ -84,14 +84,18 @@ export const importCategories = mutation({
|
|
|
84
84
|
// Get all categories
|
|
85
85
|
export const list = query({
|
|
86
86
|
args: {
|
|
87
|
+
page: v.optional(v.number()),
|
|
88
|
+
pageSize: v.optional(v.number()),
|
|
87
89
|
search: v.optional(v.string()),
|
|
88
|
-
noteFilter: v.optional(v.string()),
|
|
90
|
+
noteFilter: v.optional(v.string()),
|
|
89
91
|
typeFilter: v.optional(v.string()),
|
|
90
92
|
},
|
|
91
93
|
handler: async (ctx, args) => {
|
|
94
|
+
const page = args.page || 1;
|
|
95
|
+
const pageSize = args.pageSize || 20;
|
|
96
|
+
|
|
92
97
|
let categories = await ctx.db.query('device_categories').collect();
|
|
93
98
|
|
|
94
|
-
// Apply filters
|
|
95
99
|
if (args.search) {
|
|
96
100
|
const searchLower = args.search.toLowerCase();
|
|
97
101
|
categories = categories.filter((c) =>
|
|
@@ -112,10 +116,14 @@ export const list = query({
|
|
|
112
116
|
categories = categories.filter((c) => c.type === args.typeFilter);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
// Sort by name
|
|
116
119
|
categories.sort((a, b) => a.name.localeCompare(b.name));
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
const totalCount = categories.length;
|
|
122
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
123
|
+
const startIndex = (page - 1) * pageSize;
|
|
124
|
+
const pageCategories = categories.slice(startIndex, startIndex + pageSize);
|
|
125
|
+
|
|
126
|
+
return { page: pageCategories, totalCount, totalPages, currentPage: page };
|
|
119
127
|
},
|
|
120
128
|
});
|
|
121
129
|
|
|
@@ -17,18 +17,25 @@ export const listQuestionsByDevice = query({
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
export const listAllQuestions = query({
|
|
20
|
-
args: {
|
|
21
|
-
|
|
20
|
+
args: {
|
|
21
|
+
page: v.optional(v.number()),
|
|
22
|
+
pageSize: v.optional(v.number()),
|
|
23
|
+
search: v.optional(v.string()),
|
|
24
|
+
},
|
|
25
|
+
handler: async (ctx, args) => {
|
|
26
|
+
const page = args.page || 1;
|
|
27
|
+
const pageSize = args.pageSize || 20;
|
|
28
|
+
|
|
22
29
|
try {
|
|
23
30
|
const questions = await ctx.db
|
|
24
31
|
.query('device_questions')
|
|
25
32
|
.collect();
|
|
26
33
|
|
|
27
34
|
if (questions.length === 0) {
|
|
28
|
-
return [];
|
|
35
|
+
return { page: [], totalCount: 0, totalPages: 0, currentPage: page };
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
let questionsWithDevices = await Promise.all(
|
|
32
39
|
questions.map(async (question) => {
|
|
33
40
|
const device = await ctx.db.get(question.deviceId);
|
|
34
41
|
return {
|
|
@@ -39,12 +46,28 @@ export const listAllQuestions = query({
|
|
|
39
46
|
})
|
|
40
47
|
);
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
questionsWithDevices.sort((a, b) => {
|
|
43
50
|
if (a.deviceName !== b.deviceName) {
|
|
44
51
|
return a.deviceName.localeCompare(b.deviceName);
|
|
45
52
|
}
|
|
46
53
|
return a.order - b.order;
|
|
47
54
|
});
|
|
55
|
+
|
|
56
|
+
if (args.search) {
|
|
57
|
+
const s = args.search.toLowerCase();
|
|
58
|
+
questionsWithDevices = questionsWithDevices.filter((q) =>
|
|
59
|
+
q.question?.toLowerCase().includes(s) ||
|
|
60
|
+
q.deviceName?.toLowerCase().includes(s) ||
|
|
61
|
+
q.deviceCategory?.toLowerCase().includes(s)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const totalCount = questionsWithDevices.length;
|
|
66
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
67
|
+
const startIndex = (page - 1) * pageSize;
|
|
68
|
+
const pageQuestions = questionsWithDevices.slice(startIndex, startIndex + pageSize);
|
|
69
|
+
|
|
70
|
+
return { page: pageQuestions, totalCount, totalPages, currentPage: page };
|
|
48
71
|
} catch (error: any) {
|
|
49
72
|
console.error('Error in listAllQuestions:', error);
|
|
50
73
|
throw error;
|
|
@@ -18,7 +18,7 @@ export const getRepairHistory = query({
|
|
|
18
18
|
export const sendToRepair = mutation({
|
|
19
19
|
args: {
|
|
20
20
|
deviceId: v.id('devices'),
|
|
21
|
-
ticketId: v.optional(v.
|
|
21
|
+
ticketId: v.optional(v.string()),
|
|
22
22
|
notes: v.optional(v.string()),
|
|
23
23
|
createdBy: v.string(),
|
|
24
24
|
},
|
|
@@ -2,35 +2,31 @@ import { query } from './_generated/server';
|
|
|
2
2
|
import { v } from 'convex/values';
|
|
3
3
|
|
|
4
4
|
export const computeDeviceStatus = query({
|
|
5
|
-
args: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.
|
|
5
|
+
args: {
|
|
6
|
+
deviceId: v.id('devices'),
|
|
7
|
+
tickets: v.optional(v.array(v.object({
|
|
8
|
+
status: v.string(),
|
|
9
|
+
description: v.optional(v.string()),
|
|
10
|
+
created_at: v.optional(v.number()),
|
|
11
|
+
updated_at: v.optional(v.number()),
|
|
12
|
+
}))),
|
|
13
|
+
},
|
|
14
|
+
handler: async (ctx, args) => {
|
|
15
|
+
const tickets = args.tickets || [];
|
|
11
16
|
|
|
12
|
-
if (tickets.length === 0)
|
|
13
|
-
return 'in_uso';
|
|
14
|
-
}
|
|
17
|
+
if (tickets.length === 0) return 'in_uso';
|
|
15
18
|
|
|
16
19
|
const smaltito = tickets.find(
|
|
17
|
-
(t) =>
|
|
18
|
-
t.status === 'smaltito' &&
|
|
19
|
-
t.updated_at &&
|
|
20
|
-
t.created_at < t.updated_at
|
|
20
|
+
(t) => t.status === 'smaltito' && t.updated_at && t.created_at && t.created_at < t.updated_at
|
|
21
21
|
);
|
|
22
|
-
if (smaltito)
|
|
23
|
-
return 'smaltito';
|
|
24
|
-
}
|
|
22
|
+
if (smaltito) return 'smaltito';
|
|
25
23
|
|
|
26
24
|
const raee = tickets.find(
|
|
27
25
|
(t) =>
|
|
28
26
|
t.description?.toLowerCase().includes('raee') ||
|
|
29
27
|
t.description?.toLowerCase().includes('smaltimento')
|
|
30
28
|
);
|
|
31
|
-
if (raee && raee.status !== 'closed' && raee.status !== 'completed')
|
|
32
|
-
return 'da_smaltire';
|
|
33
|
-
}
|
|
29
|
+
if (raee && raee.status !== 'closed' && raee.status !== 'completed') return 'da_smaltire';
|
|
34
30
|
|
|
35
31
|
const openTickets = tickets.filter(
|
|
36
32
|
(t) =>
|
|
@@ -40,40 +36,42 @@ export const computeDeviceStatus = query({
|
|
|
40
36
|
t.status === 'pending'
|
|
41
37
|
);
|
|
42
38
|
|
|
43
|
-
if (openTickets.length > 0)
|
|
44
|
-
return 'guasto';
|
|
45
|
-
}
|
|
46
|
-
|
|
39
|
+
if (openTickets.length > 0) return 'guasto';
|
|
47
40
|
return 'in_uso';
|
|
48
41
|
},
|
|
49
42
|
});
|
|
50
43
|
|
|
51
44
|
export const getDeviceTicketHistory = query({
|
|
52
|
-
args: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
args: {
|
|
46
|
+
deviceId: v.id('devices'),
|
|
47
|
+
tickets: v.optional(v.array(v.any())),
|
|
48
|
+
},
|
|
49
|
+
handler: async (ctx, args) => {
|
|
50
|
+
const tickets = args.tickets || [];
|
|
51
|
+
|
|
52
|
+
const deviceDataEntries = await ctx.db
|
|
53
|
+
.query('ticket_device_data')
|
|
54
|
+
.withIndex('by_deviceId', (q) => q.eq('deviceId', args.deviceId))
|
|
55
|
+
.collect();
|
|
56
|
+
|
|
57
|
+
const ticketIdSet = new Set(deviceDataEntries.map(d => d.ticketId));
|
|
59
58
|
|
|
60
|
-
const
|
|
61
|
-
tickets
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
: null;
|
|
59
|
+
const enriched = await Promise.all(
|
|
60
|
+
tickets
|
|
61
|
+
.filter((t: any) => ticketIdSet.has(t._id?.toString() || t.ticketId))
|
|
62
|
+
.map(async (ticket: any) => {
|
|
63
|
+
const supplier = ticket.supplierId
|
|
64
|
+
? await ctx.db.get(ticket.supplierId)
|
|
65
|
+
: null;
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
return {
|
|
68
|
+
...ticket,
|
|
69
|
+
clinic: null,
|
|
70
|
+
supplier,
|
|
71
|
+
};
|
|
72
|
+
})
|
|
75
73
|
);
|
|
76
74
|
|
|
77
|
-
return
|
|
75
|
+
return enriched;
|
|
78
76
|
},
|
|
79
77
|
});
|