diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index 9de0681..8f71f58 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -41,6 +41,13 @@ const AppSidebar = () => { id: 21, enable: intersection(permissions, ['MANAGE_TENDERS']).length }, + { + label: __('Configurazione AR1', 'gepafin'), + icon: 'pi pi-id-card', + href: '/ar1-admin', + id: 23, + enable: intersection(permissions, ['MANAGE_TENDERS']).length + }, { label: __('Dev: cambia utente', 'gepafin'), icon: 'pi pi-user-edit', diff --git a/src/modules/ar1/pages/Ar1AdminConfig.js b/src/modules/ar1/pages/Ar1AdminConfig.js new file mode 100644 index 0000000..df9b9ba --- /dev/null +++ b/src/modules/ar1/pages/Ar1AdminConfig.js @@ -0,0 +1,532 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { __ } from '@wordpress/i18n'; + +// primereact +import { Card } from 'primereact/card'; +import { Button } from 'primereact/button'; +import { Toast } from 'primereact/toast'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { TabView, TabPanel } from 'primereact/tabview'; +import { InputText } from 'primereact/inputtext'; +import { InputNumber } from 'primereact/inputnumber'; +import { InputSwitch } from 'primereact/inputswitch'; +import { Dialog } from 'primereact/dialog'; +import { Tag } from 'primereact/tag'; +import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog'; +import { Chips } from 'primereact/chips'; +import { Checkbox } from 'primereact/checkbox'; +import { Calendar } from 'primereact/calendar'; +import { Dropdown } from 'primereact/dropdown'; + +import Ar1Service from '../service/ar1Service'; + +/** + * Configurazione AR1 (superadmin). 4 sottosezioni in TabView: + * 1. Template — lista versioni con status (ACTIVE/ARCHIVED/DRAFT), edit L2 layout_config, nuova versione + * 2. Policy — singleton (validity, popup, auto-archive, bulk flag) + * 3. Regole Reminder PEC — CRUD ar1_pec_schedule_config + * 4. Invio Massivo PEC — filtri azienda + dry-run + submit live + * + * Percorso: /ar1-admin (permessi MANAGE_TENDERS) + */ +const Ar1AdminConfig = () => { + const toast = useRef(null); + const [activeTab, setActiveTab] = useState(0); + + return ( +
+ + + +

{__('Configurazione AR1 — Adeguata Verifica', 'gepafin')}

+

+ {__('Gestione template, policy, regole reminder PEC e invio massivo solleciti per la dichiarazione AR1 (D.Lgs. 231/2007).', 'gepafin')} +

+ + setActiveTab(e.index)}> + + + + + + + + + + + + + +
+ ); +}; + + +// ========== Tab 1: Template ========== + +const TemplatesTab = ({ toast }) => { + const [templates, setTemplates] = useState([]); + const [loading, setLoading] = useState(true); + const [detailOpen, setDetailOpen] = useState(false); + const [selectedTpl, setSelectedTpl] = useState(null); + const [layoutJson, setLayoutJson] = useState(''); + const [newVersionDialog, setNewVersionDialog] = useState(false); + const [newVersionPayload, setNewVersionPayload] = useState({ version: '', layout_config: {}, activate_now: true }); + + const load = () => { + setLoading(true); + Ar1Service.adminListTemplates( + (resp) => { + setTemplates(resp?.items || []); + setLoading(false); + }, + (err) => { + setLoading(false); + if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Impossibile caricare template' }); + } + ); + }; + useEffect(() => { load(); /* eslint-disable-next-line */ }, []); + + const openDetail = (row) => { + Ar1Service.adminGetTemplate(row.id, + (resp) => { + setSelectedTpl(resp); + setLayoutJson(JSON.stringify(resp.layout_config || {}, null, 2)); + setDetailOpen(true); + }, + (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }) + ); + }; + + const saveLayout = () => { + let parsed; + try { parsed = JSON.parse(layoutJson); } + catch (e) { + toast.current?.show({ severity: 'error', summary: 'JSON non valido', detail: e.message }); + return; + } + Ar1Service.adminUpdateLayoutConfig(selectedTpl.id, parsed, + () => { + toast.current?.show({ severity: 'success', summary: 'OK', detail: 'Layout aggiornato' }); + setDetailOpen(false); + load(); + }, + (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }) + ); + }; + + const openNewVersion = (variant) => { + const current = templates.find(t => t.variant === variant && t.status === 'ACTIVE'); + setNewVersionPayload({ + version: '', + layout_config: current?.layout_config || {}, + activate_now: true, + _variant: variant, + }); + setNewVersionDialog(true); + }; + + const submitNewVersion = () => { + Ar1Service.adminNewVersion(newVersionPayload._variant, { + version: newVersionPayload.version, + layout_config: newVersionPayload.layout_config, + activate_now: newVersionPayload.activate_now, + }, + () => { + toast.current?.show({ severity: 'success', summary: 'OK', detail: `Nuova versione ${newVersionPayload.version} creata` }); + setNewVersionDialog(false); + load(); + }, + (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }) + ); + }; + + const statusTpl = (row) => { + const map = { + ACTIVE: { severity: 'success', icon: 'pi pi-check-circle' }, + DRAFT: { severity: 'warning', icon: 'pi pi-pencil' }, + ARCHIVED: { severity: 'secondary', icon: 'pi pi-history' }, + }; + const cfg = map[row.status] || { severity: 'info', icon: 'pi pi-circle' }; + return ; + }; + + const actionsTpl = (row) => ( +
+
+ ); + + return ( + + + + + + + + + + + {/* Dialog detail */} + setDetailOpen(false)} style={{ width: '780px', maxWidth: '95vw' }} modal> + {selectedTpl && ( +
+

{__('Status:', 'gepafin')} {selectedTpl.status}

+

{__('N. Quadri:', 'gepafin')} {(selectedTpl.questions_snapshot?.quadri || []).length}

+ +

{__('Layout config (L2 — editabile)', 'gepafin')}

+

+ {__('Modifica il JSON del layout (brand, header, intro, privacy, field_labels_override). Le domande normative (L1) non sono editabili da qui.', 'gepafin')} +

+