mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r3.4): corrections de recette (8 écarts) + recherche globale ⌘K
Arbitrage du référent sur la revue pixel : tout corriger, activer la recherche. - Stock : filtre fournisseur, sous-seuil en tête, « Entrée de stock » depuis la liste ; fiche pièce : fournisseur → lien Tiers. - Statistiques : période 3/6/12 mois (paramètre months au contrat). - Tiers : rattachements syndic→site (migration r3_recette_fixes, Location.partnerId gardé CLIENT), éditable sur la fiche site, seedé. - Bibliothèque : filtre « Rattaché à » + glisser-déposer (modale préremplie, rattachement toujours requis). - Recherche globale : GET /search (73 opérations) — familles OT/ ascenseurs/sites filtrées par la matrice, « voir autre » respecté ; topbar ⌘K, debounce, résultats groupés, navigation clavier. 74 tests API (8 nouveaux sur le scoping de la recherche), 14/14 Playwright dont un parcours « recette corrigée », 18/18 contrôles en navigateur réel, zéro erreur console. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,16 +38,20 @@ export type DocumentsResponse = z.infer<typeof DocumentsResponseSchema>;
|
||||
|
||||
// ————— Analytics (écran Statistiques, maquette R3) —————
|
||||
|
||||
export const ANALYTICS_PERIODS = [3, 6, 12] as const;
|
||||
export type AnalyticsPeriod = (typeof ANALYTICS_PERIODS)[number];
|
||||
|
||||
export const AnalyticsSummarySchema = z.object({
|
||||
months: z.number().int(), // période appliquée (3, 6 ou 12 mois)
|
||||
monthCost: z.number(), // MAD, mois courant (pièces + main-d'œuvre)
|
||||
previousMonthCost: z.number(),
|
||||
closed12m: z.object({ total: z.number().int(), preventive: z.number().int() }),
|
||||
preventiveRate: z.number().nullable(), // grilles terminées / générées (12 mois)
|
||||
avgResolutionDays: z.number().nullable(), // dépannages terminés (12 mois)
|
||||
closed: z.object({ total: z.number().int(), preventive: z.number().int() }),
|
||||
preventiveRate: z.number().nullable(), // grilles terminées / générées (période)
|
||||
avgResolutionDays: z.number().nullable(), // dépannages terminés (période)
|
||||
failuresByComponent: z.array(
|
||||
z.object({ label: z.string(), count: z.number().int() }),
|
||||
),
|
||||
costsByMonth: z.array(z.object({ month: z.string(), total: z.number() })), // 6 derniers
|
||||
costsByMonth: z.array(z.object({ month: z.string(), total: z.number() })), // un point par mois de la période
|
||||
topAssets: z.array(
|
||||
z.object({
|
||||
reference: z.string(),
|
||||
|
||||
@@ -39,6 +39,7 @@ export const PartnerSchema = z.object({
|
||||
city: z.string().nullable(),
|
||||
isActive: z.boolean(),
|
||||
openOrders: z.number().int(), // BC non reçus/annulés (fournisseur)
|
||||
siteNames: z.array(z.string()), // sites gérés (client/syndic) — maquette « Rattachements »
|
||||
});
|
||||
export type PartnerDto = z.infer<typeof PartnerSchema>;
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ export const LocationSchema = z.object({
|
||||
guardianPhone: z.string().nullable(),
|
||||
latitude: z.number().nullable(),
|
||||
longitude: z.number().nullable(),
|
||||
partnerId: z.uuid().nullable(), // client/syndic du site (R3 — rattachements Tiers)
|
||||
partnerName: z.string().nullable(),
|
||||
assetCount: z.number().int(), // appareils rattachés (zones incluses pour un site)
|
||||
});
|
||||
export type LocationDto = z.infer<typeof LocationSchema>;
|
||||
@@ -59,6 +61,7 @@ export const LocationCreateSchema = z.object({
|
||||
guardianPhone: z.string().max(40).optional(),
|
||||
latitude: z.number().min(-90).max(90).optional(),
|
||||
longitude: z.number().min(-180).max(180).optional(),
|
||||
partnerId: z.uuid().nullable().optional(), // null = détacher le client/syndic
|
||||
});
|
||||
export type LocationCreate = z.infer<typeof LocationCreateSchema>;
|
||||
|
||||
|
||||
37
packages/shared/src/schemas/search.ts
Normal file
37
packages/shared/src/schemas/search.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { z } from 'zod';
|
||||
import { WORK_ORDER_STATUSES } from '../exploitation';
|
||||
|
||||
/** Recherche globale (topbar ⌘K) — chaque famille de résultats n'apparaît
|
||||
* que si l'appelant a la permission `view` du domaine ; les OT respectent
|
||||
* en plus l'invariant « voir autre ». */
|
||||
|
||||
export const SEARCH_MIN_CHARS = 2;
|
||||
export const SEARCH_MAX_PER_KIND = 5;
|
||||
|
||||
export const SearchResponseSchema = z.object({
|
||||
workOrders: z.array(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
reference: z.string(),
|
||||
title: z.string(),
|
||||
status: z.enum(WORK_ORDER_STATUSES),
|
||||
}),
|
||||
),
|
||||
assets: z.array(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
reference: z.string(),
|
||||
brand: z.string(),
|
||||
model: z.string().nullable(),
|
||||
siteName: z.string(),
|
||||
}),
|
||||
),
|
||||
sites: z.array(
|
||||
z.object({
|
||||
id: z.uuid(),
|
||||
name: z.string(),
|
||||
city: z.string().nullable(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
export type SearchResponse = z.infer<typeof SearchResponseSchema>;
|
||||
Reference in New Issue
Block a user