refactor: reorganize autocodes into modules with updated UI, new translations, and backend migrations.
This commit is contained in:
@@ -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.
|
||||
|
||||
30
docs/development/devlog/2025-12-06-021000_autocodes_reorg.md
Normal file
30
docs/development/devlog/2025-12-06-021000_autocodes_reorg.md
Normal 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.
|
||||
4704
src/backend/Zentral.Infrastructure/Migrations/20251206011423_UpdateAutoCodeModules.Designer.cs
generated
Normal file
4704
src/backend/Zentral.Infrastructure/Migrations/20251206011423_UpdateAutoCodeModules.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user