refactor: reorganize autocodes into modules with updated UI, new translations, and backend migrations.

This commit is contained in:
2025-12-06 02:16:16 +01:00
parent 623f7b3b56
commit 6d1aef3a42
8 changed files with 4948 additions and 161 deletions

View File

@@ -47,3 +47,5 @@ File riassuntivo dello stato di sviluppo di Zentral.
- [2025-12-06 01:48:00 - Traduzione Modulo Acquisti](./devlog/2025-12-06-014800_translate_purchases.md) - **Completato**
- [2025-12-06 01:35:00 - Fix Traduzione Tab Applicazioni](./devlog/2025-12-06-013500_fix_apps_tab_translation.md) - **Completato**
- Corretta chiave di traduzione errata per la tab "Gestione Applicazioni" e migliorata la gestione dell'aggiornamento etichette tab.
- [2025-12-06 Auto Codes Reorganization](./devlog/2025-12-06-021000_autocodes_reorg.md) - **Completato**
- Riorganizzazione UI Auto Codes, allineamento stile a Custom Fields, miglioramento traduzioni e categorizzazione.

View File

@@ -0,0 +1,30 @@
# Riorganizzazione Auto Codes
## Obiettivo
Riorganizzare la sezione "Auto Codes" per allinearla graficamente e strutturalmente alla sezione "Custom Fields", migliorando le traduzioni e la categorizzazione.
## Stato Attuale
- La pagina `AutoCodesAdminPage.tsx` funziona ma ha nomi di moduli hardcoded in `types/autoCode.ts`.
- La struttura grafica è simile ma può essere migliorata per essere identica a `CustomFieldsAdminPage`.
- Mancano alcune traduzioni e la categorizzazione potrebbe non essere aggiornata con gli ultimi moduli.
## Piano di Lavoro
1. **Analisi e Preparazione**
- [x] Identificare le differenze stilistiche tra `AutoCodesAdminPage` e `CustomFieldsAdminPage`.
- [x] Identificare le stringhe non tradotte (es. nomi moduli).
2. **Refactoring Frontend**
- [x] Aggiornare `AutoCodesAdminPage.tsx` per usare lo stesso layout di `CustomFieldsAdminPage`.
- [x] Sostituire i nomi hardcoded dei moduli con chiavi di traduzione.
- [x] Aggiornare `types/autoCode.ts` per rimuovere `appNames` hardcoded o mapparlo su chiavi i18n.
3. **Aggiornamento Traduzioni**
- [x] Aggiungere le chiavi mancanti in `public/locales/it/translation.json`.
- [x] Aggiungere le chiavi mancanti in `public/locales/en/translation.json`.
4. **Verifica**
- [x] Verificare che la pagina si carichi correttamente.
- [x] Verificare che le traduzioni funzionino.
- [x] Verificare che la categorizzazione sia corretta.
- [x] Aggiornare `AutoCodeDto` nel frontend per usare `moduleCode`.
- [x] Creare migrazione per aggiornare `ModuleCode` nel database per le entità esistenti.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Zentral.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class UpdateAutoCodeModules : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE AutoCodes SET ModuleCode = 'warehouse' WHERE EntityCode IN ('warehouse_article', 'warehouse_location', 'inventory_count', 'stock_movement', 'stock_valuation')");
migrationBuilder.Sql("UPDATE AutoCodes SET ModuleCode = 'purchases' WHERE EntityCode IN ('supplier', 'purchase_order')");
migrationBuilder.Sql("UPDATE AutoCodes SET ModuleCode = 'sales' WHERE EntityCode IN ('sales_order')");
migrationBuilder.Sql("UPDATE AutoCodes SET ModuleCode = 'production' WHERE EntityCode IN ('production_order', 'bill_of_materials', 'work_center', 'production_cycle')");
migrationBuilder.Sql("UPDATE AutoCodes SET ModuleCode = 'core' WHERE EntityCode IN ('cliente')");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

View File

@@ -30,6 +30,7 @@
"preview": "Preview",
"none": "None",
"view": "View",
"copy": "Copy",
"required": "Required",
"add": "Add",
"active": "Active",
@@ -431,10 +432,20 @@
"patternHelper": "Pattern for code generation",
"previewLabel": "Preview:",
"resetSequence": "Reset Sequence",
"description": "Description",
"everyYear": "Every year",
"everyMonth": "Every month",
"generationActive": "Generation active",
"readOnly": "Code not editable"
"readOnly": "Code not editable",
"noConfigs": "No automatic codes configured for this app.",
"modules": {
"core": "Core System",
"warehouse": "Warehouse",
"purchases": "Purchases",
"sales": "Sales",
"production": "Production",
"quality": "Quality"
}
},
"customFields": {
"title": "Custom Fields Management",

View File

@@ -29,7 +29,8 @@
"notes": "Note",
"preview": "Anteprima",
"none": "Nessuno",
"view": "Dettaglio"
"view": "Dettaglio",
"copy": "Copia"
},
"menu": {
"dashboard": "Dashboard",
@@ -428,10 +429,20 @@
"patternHelper": "Pattern per generazione codice",
"previewLabel": "Anteprima:",
"resetSequence": "Reset Sequenza",
"description": "Descrizione",
"everyYear": "Ogni anno",
"everyMonth": "Ogni mese",
"generationActive": "Generazione attiva",
"readOnly": "Codice non modificabile"
"readOnly": "Codice non modificabile",
"noConfigs": "Nessun codice automatico configurato per questa applicazione.",
"modules": {
"core": "Sistema Base",
"warehouse": "Magazzino",
"purchases": "Acquisti",
"sales": "Vendite",
"production": "Produzione",
"quality": "Qualità"
}
},
"customFields": {
"title": "Gestione Campi Personalizzati",

View File

@@ -54,7 +54,7 @@ import type {
AutoCodeUpdateDto,
PlaceholderInfo,
} from "../types/autoCode";
import { groupByModule, appNames, appIcons } from "../types/autoCode";
import { groupByModule, appIcons } from "../types/autoCode";
export default function AutoCodesAdminPage() {
const queryClient = useQueryClient();
@@ -169,150 +169,163 @@ export default function AutoCodesAdminPage() {
</Box>
{/* Accordion per moduli */}
{Object.entries(groupedConfigs).map(([appCode, moduleConfigs]) => (
<Accordion
key={appCode}
expanded={expandedModule === appCode}
onChange={(_, isExpanded) =>
setExpandedModule(isExpanded ? appCode : false)
}
sx={{ mb: 1 }}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
{getAppIcon(appCode)}
<Typography variant="h6">
{appNames[appCode] || appCode}
</Typography>
<Chip
label={`${moduleConfigs.length} configurazioni`}
size="small"
variant="outlined"
/>
</Box>
</AccordionSummary>
<AccordionDetails>
<TableContainer component={Paper} variant="outlined">
<Table size="small">
<TableHead>
<TableRow>
<TableCell>{t("autoCodes.entity")}</TableCell>
<TableCell>{t("autoCodes.prefix")}</TableCell>
<TableCell>{t("autoCodes.pattern")}</TableCell>
<TableCell>{t("autoCodes.example")}</TableCell>
<TableCell>{t("autoCodes.sequence")}</TableCell>
<TableCell>{t("autoCodes.reset")}</TableCell>
<TableCell align="center">{t("autoCodes.status")}</TableCell>
<TableCell align="right">{t("common.actions")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{moduleConfigs.map((config) => (
<TableRow key={config.id} hover>
<TableCell>
<Box>
<Typography variant="body2" fontWeight="medium">
{config.entityName}
</Typography>
<Typography variant="caption" color="text.secondary">
{config.entityCode}
</Typography>
</Box>
</TableCell>
<TableCell>
<Chip
label={config.prefix || "-"}
size="small"
variant="outlined"
/>
</TableCell>
<TableCell>
<Typography
variant="body2"
sx={{ fontFamily: "monospace", fontSize: "0.85rem" }}
>
{config.pattern}
</Typography>
</TableCell>
<TableCell>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 0.5,
}}
>
<Typography
variant="body2"
sx={{
fontFamily: "monospace",
color: "primary.main",
fontWeight: "medium",
}}
>
{config.exampleCode}
</Typography>
<Tooltip title={t("autoCodes.previewTooltip")}>
<IconButton
{Object.keys(appIcons).map((appCode) => {
const moduleConfigs = groupedConfigs[appCode] || [];
return (
<Accordion
key={appCode}
expanded={expandedModule === appCode}
onChange={(_, isExpanded) =>
setExpandedModule(isExpanded ? appCode : false)
}
sx={{ mb: 1 }}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
{getAppIcon(appCode)}
<Typography variant="h6">
{t(`autoCodes.modules.${appCode}`) || appCode}
</Typography>
<Chip
label={`${moduleConfigs.length} configurazioni`}
size="small"
variant="outlined"
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{moduleConfigs.length === 0 ? (
<Typography
color="text.secondary"
align="center"
sx={{ py: 2 }}
>
{t("autoCodes.noConfigs")}
</Typography>
) : (
<TableContainer component={Paper} variant="outlined">
<Table size="small">
<TableHead>
<TableRow>
<TableCell>{t("autoCodes.entity")}</TableCell>
<TableCell>{t("autoCodes.prefix")}</TableCell>
<TableCell>{t("autoCodes.pattern")}</TableCell>
<TableCell>{t("autoCodes.example")}</TableCell>
<TableCell>{t("autoCodes.sequence")}</TableCell>
<TableCell>{t("autoCodes.reset")}</TableCell>
<TableCell align="center">{t("autoCodes.status")}</TableCell>
<TableCell align="right">{t("common.actions")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{moduleConfigs.map((config) => (
<TableRow key={config.id} hover>
<TableCell>
<Box>
<Typography variant="body2" fontWeight="medium">
{config.entityName}
</Typography>
<Typography variant="caption" color="text.secondary">
{config.entityCode}
</Typography>
</Box>
</TableCell>
<TableCell>
<Chip
label={config.prefix || "-"}
size="small"
onClick={() =>
previewMutation.mutate(config.entityCode)
}
disabled={!config.isEnabled}
variant="outlined"
/>
</TableCell>
<TableCell>
<Typography
variant="body2"
sx={{ fontFamily: "monospace", fontSize: "0.85rem" }}
>
<PreviewIcon fontSize="small" />
</IconButton>
</Tooltip>
</Box>
</TableCell>
<TableCell>
<Typography variant="body2">
{config.lastSequence}
</Typography>
</TableCell>
<TableCell>
{config.resetSequenceMonthly ? (
<Chip label={t("autoCodes.monthly")} size="small" color="info" />
) : config.resetSequenceYearly ? (
<Chip label={t("autoCodes.yearly")} size="small" color="warning" />
) : (
<Chip label={t("autoCodes.never")} size="small" variant="outlined" />
)}
</TableCell>
<TableCell align="center">
<Chip
label={config.isEnabled ? t("apps.admin.active") : t("apps.admin.inactive")}
size="small"
color={config.isEnabled ? "success" : "default"}
/>
</TableCell>
<TableCell align="right">
<Tooltip title={t("common.edit")}>
<IconButton
size="small"
onClick={() => setEditingConfig(config)}
>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title={t("autoCodes.resetTooltip")}>
<IconButton
size="small"
onClick={() => setConfirmReset(config.entityCode)}
disabled={!config.isEnabled}
>
<ResetIcon fontSize="small" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
))}
{config.pattern}
</Typography>
</TableCell>
<TableCell>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 0.5,
}}
>
<Typography
variant="body2"
sx={{
fontFamily: "monospace",
color: "primary.main",
fontWeight: "medium",
}}
>
{config.exampleCode}
</Typography>
<Tooltip title={t("autoCodes.previewTooltip")}>
<IconButton
size="small"
onClick={() =>
previewMutation.mutate(config.entityCode)
}
disabled={!config.isEnabled}
>
<PreviewIcon fontSize="small" />
</IconButton>
</Tooltip>
</Box>
</TableCell>
<TableCell>
<Typography variant="body2">
{config.lastSequence}
</Typography>
</TableCell>
<TableCell>
{config.resetSequenceMonthly ? (
<Chip label={t("autoCodes.monthly")} size="small" color="info" />
) : config.resetSequenceYearly ? (
<Chip label={t("autoCodes.yearly")} size="small" color="warning" />
) : (
<Chip label={t("autoCodes.never")} size="small" variant="outlined" />
)}
</TableCell>
<TableCell align="center">
<Chip
label={config.isEnabled ? t("apps.admin.active") : t("apps.admin.inactive")}
size="small"
color={config.isEnabled ? "success" : "default"}
/>
</TableCell>
<TableCell align="right">
<Tooltip title={t("common.edit")}>
<IconButton
size="small"
onClick={() => setEditingConfig(config)}
>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title={t("autoCodes.resetTooltip")}>
<IconButton
size="small"
onClick={() => setConfirmReset(config.entityCode)}
disabled={!config.isEnabled}
>
<ResetIcon fontSize="small" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</AccordionDetails>
</Accordion>
);
})}
{/* Dialog modifica configurazione */}
<EditConfigDialog
@@ -396,7 +409,7 @@ export default function AutoCodesAdminPage() {
>
{previewCode}
</Typography>
<Tooltip title="Copia">
<Tooltip title={t("common.copy")}>
<IconButton
size="small"
onClick={() => {
@@ -718,7 +731,7 @@ function EditConfigDialog({
<Grid size={{ xs: 12 }}>
<TextField
label="Descrizione"
label={t("autoCodes.description")}
value={formData.description ?? config.description ?? ""}
onChange={(e) =>
setFormData({

View File

@@ -15,7 +15,7 @@ export interface AutoCodeDto {
lastResetMonth: number | null;
isEnabled: boolean;
isReadOnly: boolean;
appCode: string | null;
moduleCode: string | null;
description: string | null;
sortOrder: number;
exampleCode: string;
@@ -60,7 +60,7 @@ export interface PlaceholderInfo {
*/
export function groupByModule(configs: AutoCodeDto[]): Record<string, AutoCodeDto[]> {
return configs.reduce((acc, config) => {
const module = config.appCode || "core";
const module = config.moduleCode || "core";
if (!acc[module]) {
acc[module] = [];
}
@@ -69,17 +69,7 @@ export function groupByModule(configs: AutoCodeDto[]): Record<string, AutoCodeDt
}, {} as Record<string, AutoCodeDto[]>);
}
/**
* Nomi visualizzati per i moduli
*/
export const appNames: Record<string, string> = {
core: "Sistema Base",
warehouse: "Magazzino",
purchases: "Acquisti",
sales: "Vendite",
production: "Produzione",
quality: "Qualità",
};
/**
* Icone per i moduli (nomi MUI icons)