Просмотр исходного кода

add more options to delete content (#54)

Bernt Christian Egeland 6 месяцев назад
Родитель
Сommit
0fc5fbcc6f

+ 12 - 0
messages/de/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Alle Angebote und deren Positionen",
     "contentInventory": "Inventar",
     "contentInventoryDescription": "Alle Inventarteile und deren Bilder",
+    "contentInspections": "Inspektionen",
+    "contentInspectionsDescription": "Alle Fahrzeuginspektionen und deren Ergebnisse",
+    "contentTechnicians": "Techniker",
+    "contentTechniciansDescription": "Alle Technikerprofile. Verknüpfte Datensätze werden getrennt.",
+    "contentInspectionTemplates": "Inspektionsvorlagen",
+    "contentInspectionTemplatesDescription": "Alle Inspektionsvorlagen und Abschnitte. Inspektionen zuerst löschen.",
+    "contentNotifications": "Benachrichtigungen",
+    "contentNotificationsDescription": "Alle Systembenachrichtigungen",
+    "contentSmsMessages": "SMS-Nachrichten",
+    "contentSmsMessagesDescription": "Alle gesendeten und empfangenen SMS-Nachrichten",
+    "contentCustomFields": "Benutzerdefinierte Felder",
+    "contentCustomFieldsDescription": "Alle benutzerdefinierten Felddefinitionen und Werte",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Geben Sie <bold>delete my data</bold> zur Bestatigung ein",
     "deleteSelected": "{count} ausgewahlte loschen",

+ 12 - 0
messages/en/settings.json

@@ -154,6 +154,18 @@
     "contentQuotesDescription": "All quotes and their line items",
     "contentInventory": "Inventory",
     "contentInventoryDescription": "All inventory parts and their images",
+    "contentInspections": "Inspections",
+    "contentInspectionsDescription": "All vehicle inspections and their results",
+    "contentTechnicians": "Technicians",
+    "contentTechniciansDescription": "All technician profiles. Linked records will be unlinked.",
+    "contentInspectionTemplates": "Inspection Templates",
+    "contentInspectionTemplatesDescription": "All inspection templates and their sections. Delete inspections first.",
+    "contentNotifications": "Notifications",
+    "contentNotificationsDescription": "All system notifications",
+    "contentSmsMessages": "SMS Messages",
+    "contentSmsMessagesDescription": "All sent and received SMS messages",
+    "contentCustomFields": "Custom Fields",
+    "contentCustomFieldsDescription": "All custom field definitions and their values",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Type <bold>delete my data</bold> to confirm",
     "deleteSelected": "Delete {count} selected",

+ 12 - 0
messages/es/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Todas las cotizaciones y sus partidas",
     "contentInventory": "Inventario",
     "contentInventoryDescription": "Todas las piezas de inventario y sus imagenes",
+    "contentInspections": "Inspecciones",
+    "contentInspectionsDescription": "Todas las inspecciones de vehículos y sus resultados",
+    "contentTechnicians": "Técnicos",
+    "contentTechniciansDescription": "Todos los perfiles de técnicos. Los registros vinculados se desvincularán.",
+    "contentInspectionTemplates": "Plantillas de inspección",
+    "contentInspectionTemplatesDescription": "Todas las plantillas de inspección y sus secciones. Elimine primero las inspecciones.",
+    "contentNotifications": "Notificaciones",
+    "contentNotificationsDescription": "Todas las notificaciones del sistema",
+    "contentSmsMessages": "Mensajes SMS",
+    "contentSmsMessagesDescription": "Todos los mensajes SMS enviados y recibidos",
+    "contentCustomFields": "Campos personalizados",
+    "contentCustomFieldsDescription": "Todas las definiciones de campos personalizados y sus valores",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Escriba <bold>delete my data</bold> para confirmar",
     "deleteSelected": "Eliminar {count} seleccionados",

+ 12 - 0
messages/fr/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Tous les devis et leurs lignes",
     "contentInventory": "Inventaire",
     "contentInventoryDescription": "Toutes les pieces d'inventaire et leurs images",
+    "contentInspections": "Inspections",
+    "contentInspectionsDescription": "Toutes les inspections de véhicules et leurs résultats",
+    "contentTechnicians": "Techniciens",
+    "contentTechniciansDescription": "Tous les profils de techniciens. Les enregistrements liés seront dissociés.",
+    "contentInspectionTemplates": "Modèles d'inspection",
+    "contentInspectionTemplatesDescription": "Tous les modèles d'inspection et leurs sections. Supprimez d'abord les inspections.",
+    "contentNotifications": "Notifications",
+    "contentNotificationsDescription": "Toutes les notifications système",
+    "contentSmsMessages": "Messages SMS",
+    "contentSmsMessagesDescription": "Tous les messages SMS envoyés et reçus",
+    "contentCustomFields": "Champs personnalisés",
+    "contentCustomFieldsDescription": "Toutes les définitions de champs personnalisés et leurs valeurs",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Tapez <bold>delete my data</bold> pour confirmer",
     "deleteSelected": "Supprimer {count} selectionnes",

+ 12 - 0
messages/it/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Tutti i preventivi e le relative voci",
     "contentInventory": "Inventario",
     "contentInventoryDescription": "Tutti i ricambi in inventario e le relative immagini",
+    "contentInspections": "Ispezioni",
+    "contentInspectionsDescription": "Tutte le ispezioni dei veicoli e i relativi risultati",
+    "contentTechnicians": "Tecnici",
+    "contentTechniciansDescription": "Tutti i profili dei tecnici. I record collegati verranno scollegati.",
+    "contentInspectionTemplates": "Modelli di ispezione",
+    "contentInspectionTemplatesDescription": "Tutti i modelli di ispezione e le relative sezioni. Eliminare prima le ispezioni.",
+    "contentNotifications": "Notifiche",
+    "contentNotificationsDescription": "Tutte le notifiche di sistema",
+    "contentSmsMessages": "Messaggi SMS",
+    "contentSmsMessagesDescription": "Tutti i messaggi SMS inviati e ricevuti",
+    "contentCustomFields": "Campi personalizzati",
+    "contentCustomFieldsDescription": "Tutte le definizioni dei campi personalizzati e i relativi valori",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Digita <bold>delete my data</bold> per confermare",
     "deleteSelected": "Elimina {count} selezionati",

+ 12 - 0
messages/nb/settings.json

@@ -154,6 +154,18 @@
     "contentQuotesDescription": "Alle tilbud og deres linjer",
     "contentInventory": "Inventar",
     "contentInventoryDescription": "Alle inventardeler og deres bilder",
+    "contentInspections": "Inspeksjoner",
+    "contentInspectionsDescription": "Alle kjøretøyinspeksjoner og resultater",
+    "contentTechnicians": "Teknikere",
+    "contentTechniciansDescription": "Alle teknikerprofiler. Tilknyttede poster vil bli frakoblet.",
+    "contentInspectionTemplates": "Inspeksjonsmaler",
+    "contentInspectionTemplatesDescription": "Alle inspeksjonsmaler og seksjoner. Slett inspeksjoner først.",
+    "contentNotifications": "Varsler",
+    "contentNotificationsDescription": "Alle systemvarsler",
+    "contentSmsMessages": "SMS-meldinger",
+    "contentSmsMessagesDescription": "Alle sendte og mottatte SMS-meldinger",
+    "contentCustomFields": "Egendefinerte felt",
+    "contentCustomFieldsDescription": "Alle egendefinerte feltdefinisjoner og verdier",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Skriv <bold>delete my data</bold> for a bekrefte",
     "deleteSelected": "Slett {count} valgte",

+ 12 - 0
messages/nl/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Alle offertes en hun regels",
     "contentInventory": "Inventaris",
     "contentInventoryDescription": "Alle inventarisonderdelen en hun afbeeldingen",
+    "contentInspections": "Inspecties",
+    "contentInspectionsDescription": "Alle voertuiginspecties en hun resultaten",
+    "contentTechnicians": "Technici",
+    "contentTechniciansDescription": "Alle techniciprofielen. Gekoppelde records worden ontkoppeld.",
+    "contentInspectionTemplates": "Inspectiesjablonen",
+    "contentInspectionTemplatesDescription": "Alle inspectiesjablonen en secties. Verwijder eerst de inspecties.",
+    "contentNotifications": "Meldingen",
+    "contentNotificationsDescription": "Alle systeemmeldingen",
+    "contentSmsMessages": "SMS-berichten",
+    "contentSmsMessagesDescription": "Alle verzonden en ontvangen SMS-berichten",
+    "contentCustomFields": "Aangepaste velden",
+    "contentCustomFieldsDescription": "Alle aangepaste velddefinities en hun waarden",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Typ <bold>delete my data</bold> om te bevestigen",
     "deleteSelected": "{count} geselecteerde verwijderen",

+ 12 - 0
messages/pl/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Wszystkie wyceny i ich pozycje",
     "contentInventory": "Inwentarz",
     "contentInventoryDescription": "Wszystkie czesci inwentarza i ich zdjecia",
+    "contentInspections": "Inspekcje",
+    "contentInspectionsDescription": "Wszystkie inspekcje pojazdów i ich wyniki",
+    "contentTechnicians": "Technicy",
+    "contentTechniciansDescription": "Wszystkie profile techników. Powiązane rekordy zostaną odłączone.",
+    "contentInspectionTemplates": "Szablony inspekcji",
+    "contentInspectionTemplatesDescription": "Wszystkie szablony inspekcji i ich sekcje. Najpierw usuń inspekcje.",
+    "contentNotifications": "Powiadomienia",
+    "contentNotificationsDescription": "Wszystkie powiadomienia systemowe",
+    "contentSmsMessages": "Wiadomości SMS",
+    "contentSmsMessagesDescription": "Wszystkie wysłane i odebrane wiadomości SMS",
+    "contentCustomFields": "Pola niestandardowe",
+    "contentCustomFieldsDescription": "Wszystkie definicje pól niestandardowych i ich wartości",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Wpisz <bold>delete my data</bold> aby potwierdzic",
     "deleteSelected": "Usun {count} wybranych",

+ 12 - 0
messages/pt-BR/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Todos os orcamentos e seus itens",
     "contentInventory": "Inventario",
     "contentInventoryDescription": "Todas as pecas do inventario e suas imagens",
+    "contentInspections": "Inspeções",
+    "contentInspectionsDescription": "Todas as inspeções de veículos e seus resultados",
+    "contentTechnicians": "Técnicos",
+    "contentTechniciansDescription": "Todos os perfis de técnicos. Registros vinculados serão desvinculados.",
+    "contentInspectionTemplates": "Modelos de inspeção",
+    "contentInspectionTemplatesDescription": "Todos os modelos de inspeção e suas seções. Exclua as inspeções primeiro.",
+    "contentNotifications": "Notificações",
+    "contentNotificationsDescription": "Todas as notificações do sistema",
+    "contentSmsMessages": "Mensagens SMS",
+    "contentSmsMessagesDescription": "Todas as mensagens SMS enviadas e recebidas",
+    "contentCustomFields": "Campos personalizados",
+    "contentCustomFieldsDescription": "Todas as definições de campos personalizados e seus valores",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Digite <bold>delete my data</bold> para confirmar",
     "deleteSelected": "Excluir {count} selecionados",

+ 12 - 0
messages/tr/settings.json

@@ -208,6 +208,18 @@
     "contentQuotesDescription": "Tüm teklifler ve kalemleri",
     "contentInventory": "Envanter",
     "contentInventoryDescription": "Tüm envanter parçaları ve görselleri",
+    "contentInspections": "Denetimler",
+    "contentInspectionsDescription": "Tüm araç denetimleri ve sonuçları",
+    "contentTechnicians": "Teknisyenler",
+    "contentTechniciansDescription": "Tüm teknisyen profilleri. Bağlı kayıtların bağlantısı kaldırılacak.",
+    "contentInspectionTemplates": "Denetim Şablonları",
+    "contentInspectionTemplatesDescription": "Tüm denetim şablonları ve bölümleri. Önce denetimleri silin.",
+    "contentNotifications": "Bildirimler",
+    "contentNotificationsDescription": "Tüm sistem bildirimleri",
+    "contentSmsMessages": "SMS Mesajları",
+    "contentSmsMessagesDescription": "Tüm gönderilen ve alınan SMS mesajları",
+    "contentCustomFields": "Özel Alanlar",
+    "contentCustomFieldsDescription": "Tüm özel alan tanımları ve değerleri",
     "deleteContentConfirmPhrase": "delete my data",
     "deleteContentConfirmPrompt": "Onaylamak için <bold>delete my data</bold> yazın",
     "deleteSelected": "{count} seçili sil",

+ 60 - 4
src/app/(authenticated)/settings/data/data-settings.tsx

@@ -20,6 +20,12 @@ interface ContentCounts {
   customers: number
   quotes: number
   inventory: number
+  inspections: number
+  technicians: number
+  inspectionTemplates: number
+  notifications: number
+  smsMessages: number
+  customFields: number
 }
 
 interface ExportOptions {
@@ -209,6 +215,12 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
     customers: false,
     quotes: false,
     inventory: false,
+    inspections: false,
+    technicians: false,
+    inspectionTemplates: false,
+    notifications: false,
+    smsMessages: false,
+    customFields: false,
   })
 
   const selectedContentCount = Object.values(contentSelections).filter(Boolean).length
@@ -223,7 +235,11 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
         toast.success(t('account.contentDeleted'))
         setContentDialogOpen(false)
         setContentConfirmText('')
-        setContentSelections({ vehicles: false, customers: false, quotes: false, inventory: false })
+        setContentSelections({
+          vehicles: false, customers: false, quotes: false, inventory: false,
+          inspections: false, technicians: false, inspectionTemplates: false,
+          notifications: false, smsMessages: false, customFields: false,
+        })
         router.refresh()
       } else {
         toast.error(result.error || t('account.failedDeleteContent'))
@@ -523,7 +539,11 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
               className="border-destructive/30 text-destructive hover:bg-destructive/10 hover:text-destructive"
               onClick={() => {
                 setContentConfirmText('')
-                setContentSelections({ vehicles: false, customers: false, quotes: false, inventory: false })
+                setContentSelections({
+          vehicles: false, customers: false, quotes: false, inventory: false,
+          inspections: false, technicians: false, inspectionTemplates: false,
+          notifications: false, smsMessages: false, customFields: false,
+        })
                 setContentDialogOpen(true)
               }}
             >
@@ -536,7 +556,7 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
 
       {/* Delete Content Confirmation Dialog */}
       <Dialog open={contentDialogOpen} onOpenChange={setContentDialogOpen}>
-        <DialogContent className="sm:max-w-md">
+        <DialogContent className="sm:max-w-[640px]">
           <DialogHeader>
             <DialogTitle className="text-destructive">{t('account.deleteContentDialogTitle')}</DialogTitle>
             <DialogDescription>
@@ -544,7 +564,7 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
             </DialogDescription>
           </DialogHeader>
           <div className="space-y-4 py-2">
-            <div className="space-y-3">
+            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
               {([
                 {
                   key: 'vehicles' as const,
@@ -570,6 +590,42 @@ export function DataSettings({ contentCounts }: { contentCounts: ContentCounts }
                   count: contentCounts.inventory,
                   description: t('account.contentInventoryDescription'),
                 },
+                {
+                  key: 'inspections' as const,
+                  label: t('account.contentInspections'),
+                  count: contentCounts.inspections,
+                  description: t('account.contentInspectionsDescription'),
+                },
+                {
+                  key: 'technicians' as const,
+                  label: t('account.contentTechnicians'),
+                  count: contentCounts.technicians,
+                  description: t('account.contentTechniciansDescription'),
+                },
+                {
+                  key: 'inspectionTemplates' as const,
+                  label: t('account.contentInspectionTemplates'),
+                  count: contentCounts.inspectionTemplates,
+                  description: t('account.contentInspectionTemplatesDescription'),
+                },
+                {
+                  key: 'notifications' as const,
+                  label: t('account.contentNotifications'),
+                  count: contentCounts.notifications,
+                  description: t('account.contentNotificationsDescription'),
+                },
+                {
+                  key: 'smsMessages' as const,
+                  label: t('account.contentSmsMessages'),
+                  count: contentCounts.smsMessages,
+                  description: t('account.contentSmsMessagesDescription'),
+                },
+                {
+                  key: 'customFields' as const,
+                  label: t('account.contentCustomFields'),
+                  count: contentCounts.customFields,
+                  description: t('account.contentCustomFieldsDescription'),
+                },
               ]).map((item) => (
                 <label
                   key={item.key}

+ 5 - 1
src/app/(authenticated)/settings/data/page.tsx

@@ -5,7 +5,11 @@ export default async function DataSettingsPage() {
   const countsResult = await getContentCounts();
   const contentCounts = countsResult.success && countsResult.data
     ? countsResult.data
-    : { vehicles: 0, customers: 0, quotes: 0, inventory: 0 };
+    : {
+        vehicles: 0, customers: 0, quotes: 0, inventory: 0,
+        inspections: 0, technicians: 0, inspectionTemplates: 0,
+        notifications: 0, smsMessages: 0, customFields: 0,
+      };
 
   return <DataSettings contentCounts={contentCounts} />;
 }

+ 76 - 4
src/features/settings/Actions/deleteContent.ts

@@ -12,6 +12,12 @@ const deleteContentSchema = z.object({
   customers: z.boolean().default(false),
   quotes: z.boolean().default(false),
   inventory: z.boolean().default(false),
+  inspections: z.boolean().default(false),
+  technicians: z.boolean().default(false),
+  inspectionTemplates: z.boolean().default(false),
+  notifications: z.boolean().default(false),
+  smsMessages: z.boolean().default(false),
+  customFields: z.boolean().default(false),
 });
 
 export async function deleteContent(input: unknown) {
@@ -22,7 +28,7 @@ export async function deleteContent(input: unknown) {
 
     const selections = deleteContentSchema.parse(input);
 
-    if (!selections.vehicles && !selections.customers && !selections.quotes && !selections.inventory) {
+    if (!Object.values(selections).some(Boolean)) {
       throw new Error("No content types selected");
     }
 
@@ -71,6 +77,32 @@ export async function deleteContent(input: unknown) {
       deleted.push("customers");
     }
 
+    // --- Inspections ---
+    // Cascades: InspectionItem, InspectionQuoteRequest
+    if (selections.inspections) {
+      await db.inspection.deleteMany({ where: { organizationId } });
+      deleted.push("inspections");
+    }
+
+    // --- Technicians ---
+    // ServiceRecords/Inspections with this technician get technicianId set to null (SetNull).
+    if (selections.technicians) {
+      await db.technician.deleteMany({ where: { organizationId } });
+      deleted.push("technicians");
+    }
+
+    // --- Inspection Templates ---
+    // Cascades: InspectionTemplateSection -> InspectionTemplateItem
+    // Inspections reference templates (no cascade), so delete remaining inspections first.
+    if (selections.inspectionTemplates) {
+      if (!selections.inspections) {
+        await db.inspection.deleteMany({ where: { organizationId } });
+        deleted.push("inspections");
+      }
+      await db.inspectionTemplate.deleteMany({ where: { organizationId } });
+      deleted.push("inspectionTemplates");
+    }
+
     // --- Inventory ---
     if (selections.inventory) {
       const parts = await db.inventoryPart.findMany({
@@ -85,6 +117,25 @@ export async function deleteContent(input: unknown) {
       deleted.push("inventory");
     }
 
+    // --- Notifications ---
+    if (selections.notifications) {
+      await db.notification.deleteMany({ where: { organizationId } });
+      deleted.push("notifications");
+    }
+
+    // --- SMS Messages ---
+    if (selections.smsMessages) {
+      await db.smsMessage.deleteMany({ where: { organizationId } });
+      deleted.push("smsMessages");
+    }
+
+    // --- Custom Fields ---
+    // Cascades: CustomFieldValue
+    if (selections.customFields) {
+      await db.customFieldDefinition.deleteMany({ where: { organizationId } });
+      deleted.push("customFields");
+    }
+
     // Clean up files from disk (best effort)
     for (const filePath of filesToClean) {
       try {
@@ -99,6 +150,9 @@ export async function deleteContent(input: unknown) {
     revalidatePath("/customers");
     revalidatePath("/quotes");
     revalidatePath("/inventory");
+    revalidatePath("/inspections");
+    revalidatePath("/technicians");
+    revalidatePath("/settings");
     revalidatePath("/settings/account");
 
     return { deleted };
@@ -115,16 +169,34 @@ export async function deleteContent(input: unknown) {
 export async function getContentCounts() {
   return withAuth(async ({ organizationId }) => {
     if (!organizationId) {
-      return { vehicles: 0, customers: 0, quotes: 0, inventory: 0 };
+      return {
+        vehicles: 0, customers: 0, quotes: 0, inventory: 0,
+        inspections: 0, technicians: 0, inspectionTemplates: 0,
+        notifications: 0, smsMessages: 0, customFields: 0,
+      };
     }
 
-    const [vehicles, customers, quotes, inventory] = await Promise.all([
+    const [
+      vehicles, customers, quotes, inventory,
+      inspections, technicians, inspectionTemplates,
+      notifications, smsMessages, customFields,
+    ] = await Promise.all([
       db.vehicle.count({ where: { organizationId } }),
       db.customer.count({ where: { organizationId } }),
       db.quote.count({ where: { organizationId } }),
       db.inventoryPart.count({ where: { organizationId } }),
+      db.inspection.count({ where: { organizationId } }),
+      db.technician.count({ where: { organizationId } }),
+      db.inspectionTemplate.count({ where: { organizationId } }),
+      db.notification.count({ where: { organizationId } }),
+      db.smsMessage.count({ where: { organizationId } }),
+      db.customFieldDefinition.count({ where: { organizationId } }),
     ]);
 
-    return { vehicles, customers, quotes, inventory };
+    return {
+      vehicles, customers, quotes, inventory,
+      inspections, technicians, inspectionTemplates,
+      notifications, smsMessages, customFields,
+    };
   });
 }