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) => (
+
+ openDetail(row)} />
+ {row.status === 'ACTIVE' && (
+ openNewVersion(row.variant)} />
+ )}
+
+ );
+
+ 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')}
+
+
+ )}
+
+
+ {/* Dialog new version */}
+ setNewVersionDialog(false)} style={{ width: '560px', maxWidth: '95vw' }} modal>
+ {__('Variante:', 'gepafin')} {newVersionPayload._variant}
+
+ {__('Versione (semver xx.yy.zz)', 'gepafin')}
+ setNewVersionPayload({ ...newVersionPayload, version: e.target.value })} placeholder="1.1.0" style={{ width: '100%' }} />
+
+
+ setNewVersionPayload({ ...newVersionPayload, activate_now: e.checked })} />
+ {__('Attiva subito (ARCHIVE precedente ACTIVE)', 'gepafin')}
+
+
+ {__('Il layout_config viene pre-popolato da quello ACTIVE corrente. Puoi modificarlo dopo la creazione.', 'gepafin')}
+
+
+ setNewVersionDialog(false)} />
+
+
+
+
+ );
+};
+
+
+// ========== Tab 2: Policy ==========
+
+const PolicyTab = ({ toast }) => {
+ const [policy, setPolicy] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ const load = () => {
+ setLoading(true);
+ Ar1Service.adminGetPolicy(
+ (resp) => { setPolicy(resp); setLoading(false); },
+ (err) => {
+ setLoading(false);
+ toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail });
+ }
+ );
+ };
+ useEffect(() => { load(); /* eslint-disable-next-line */ }, []);
+
+ const save = () => {
+ if (!policy) return;
+ const payload = {
+ validity_days: policy.validity_days,
+ popup_dismiss_hours: policy.popup_dismiss_hours,
+ popup_force_on_expired: policy.popup_force_on_expired,
+ auto_archive_on_company_document: policy.auto_archive_on_company_document,
+ company_document_category_id: policy.company_document_category_id,
+ allow_bulk_recompilation_request: policy.allow_bulk_recompilation_request,
+ };
+ Ar1Service.adminUpdatePolicy(payload,
+ () => { toast.current?.show({ severity: 'success', summary: 'OK', detail: 'Policy aggiornata' }); load(); },
+ (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail })
+ );
+ };
+
+ if (loading || !policy) return {__('Caricamento...', 'gepafin')}
;
+
+ return (
+
+
+
+
+ {__('Validità dichiarazione (giorni)', 'gepafin')}
+
+ setPolicy({ ...policy, validity_days: e.value })} min={30} max={1825} style={{ width: '100%' }} />
+ {__('Range: 30-1825 giorni (5 anni max)', 'gepafin')}
+
+
+
+ {__('Ore di dismiss popup', 'gepafin')}
+
+ setPolicy({ ...policy, popup_dismiss_hours: e.value })} min={1} max={168} style={{ width: '100%' }} />
+ {__('Range: 1-168 ore (1 settimana max)', 'gepafin')}
+
+
+
+ {__('Categoria documento (auto-archive)', 'gepafin')}
+
+ setPolicy({ ...policy, company_document_category_id: e.value })} min={1} style={{ width: '100%' }} />
+ {__('4 = ANTIRICICLAGGIO (default Gepafin)', 'gepafin')}
+
+
+
+ setPolicy({ ...policy, popup_force_on_expired: e.value })} />
+ {__('Popup bloccante se EXPIRED', 'gepafin')}
+
+
+ setPolicy({ ...policy, auto_archive_on_company_document: e.value })} />
+ {__('Auto-archive in company_document', 'gepafin')}
+
+
+ setPolicy({ ...policy, allow_bulk_recompilation_request: e.value })} />
+ {__('Abilita invio massivo PEC', 'gepafin')}
+
+
+
+
+
+
+
+ );
+};
+
+
+// ========== Tab 3: Regole Reminder PEC ==========
+
+const PecScheduleTab = ({ toast }) => {
+ const [rules, setRules] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [editDialog, setEditDialog] = useState(false);
+ const [editingRule, setEditingRule] = useState(null);
+
+ const load = () => {
+ setLoading(true);
+ Ar1Service.adminListPecSchedule(
+ (resp) => { setRules(resp?.items || []); setLoading(false); },
+ (err) => {
+ setLoading(false);
+ toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail });
+ }
+ );
+ };
+ useEffect(() => { load(); /* eslint-disable-next-line */ }, []);
+
+ const openNew = () => {
+ setEditingRule({ kind: '', offset_days: 30, is_recurring: false, recurring_interval_days: null, enabled: true, description: '' });
+ setEditDialog(true);
+ };
+ const openEdit = (r) => {
+ setEditingRule({ ...r });
+ setEditDialog(true);
+ };
+
+ const save = () => {
+ if (!editingRule) return;
+ const cb = {
+ onOk: () => { toast.current?.show({ severity: 'success', summary: 'OK', detail: 'Regola salvata' }); setEditDialog(false); load(); },
+ onErr: (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }),
+ };
+ if (editingRule.id) {
+ Ar1Service.adminUpdatePecRule(editingRule.id, {
+ offset_days: editingRule.offset_days, is_recurring: editingRule.is_recurring,
+ recurring_interval_days: editingRule.recurring_interval_days, enabled: editingRule.enabled,
+ description: editingRule.description,
+ }, cb.onOk, cb.onErr);
+ } else {
+ Ar1Service.adminCreatePecRule(editingRule, cb.onOk, cb.onErr);
+ }
+ };
+
+ const del = (rule) => {
+ confirmDialog({
+ message: __(`Eliminare la regola "${rule.kind}"?`, 'gepafin'),
+ header: __('Conferma', 'gepafin'), icon: 'pi pi-exclamation-triangle',
+ acceptClassName: 'p-button-danger',
+ accept: () => {
+ Ar1Service.adminDeletePecRule(rule.id,
+ () => { toast.current?.show({ severity: 'success', summary: 'OK', detail: 'Regola eliminata' }); load(); },
+ (err) => toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail })
+ );
+ }
+ });
+ };
+
+ const enabledTpl = (r) => ;
+ const recurringTpl = (r) => r.is_recurring ? `ogni ${r.recurring_interval_days}gg` : '—';
+ const actionsTpl = (r) => (
+
+ openEdit(r)} />
+ del(r)} />
+
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setEditDialog(false)} style={{ width: '520px', maxWidth: '95vw' }} modal>
+ {editingRule && (
+
+
+ {__('Kind (identificatore, unique, es: AR1_REMINDER_30D)', 'gepafin')}
+ setEditingRule({ ...editingRule, kind: e.target.value })} disabled={!!editingRule.id} style={{ width: '100%' }} />
+
+
+ {__('Offset giorni (positivo = prima scadenza, negativo = dopo)', 'gepafin')}
+ setEditingRule({ ...editingRule, offset_days: e.value })} style={{ width: '100%' }} />
+
+
+ setEditingRule({ ...editingRule, is_recurring: e.checked })} />
+ {__('Ricorrente', 'gepafin')}
+
+ {editingRule.is_recurring && (
+
+ {__('Intervallo ripetizione (giorni)', 'gepafin')}
+ setEditingRule({ ...editingRule, recurring_interval_days: e.value })} min={1} style={{ width: '100%' }} />
+
+ )}
+
+ {__('Descrizione', 'gepafin')}
+ setEditingRule({ ...editingRule, description: e.target.value })} style={{ width: '100%' }} />
+
+
+ setEditingRule({ ...editingRule, enabled: e.value })} />
+ {__('Abilitata', 'gepafin')}
+
+
+ setEditDialog(false)} />
+
+
+
+ )}
+
+
+ );
+};
+
+
+// ========== Tab 4: Invio Massivo PEC ==========
+
+const BulkPecTab = ({ toast }) => {
+ const [filters, setFilters] = useState({
+ only_expired: false,
+ only_missing: false,
+ company_ids: [],
+ expired_before: null,
+ });
+ const [dryRunResult, setDryRunResult] = useState(null);
+ const [submitting, setSubmitting] = useState(false);
+
+ const runDryRun = () => {
+ setSubmitting(true);
+ const payload = {
+ only_expired: filters.only_expired,
+ only_missing: filters.only_missing,
+ company_ids: filters.company_ids.length ? filters.company_ids.map(Number) : null,
+ expired_before: filters.expired_before ? filters.expired_before.toISOString() : null,
+ dry_run: true,
+ };
+ Ar1Service.adminBulkRecompilation(payload,
+ (resp) => { setSubmitting(false); setDryRunResult(resp); },
+ (err) => { setSubmitting(false); toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }); }
+ );
+ };
+
+ const runLive = () => {
+ confirmDialog({
+ message: __(`Confermi l'invio di ${dryRunResult?.matched_count || 0} PEC? L'operazione non può essere annullata.`, 'gepafin'),
+ header: __('Conferma invio massivo', 'gepafin'), icon: 'pi pi-exclamation-triangle',
+ accept: () => {
+ setSubmitting(true);
+ const payload = {
+ only_expired: filters.only_expired,
+ only_missing: filters.only_missing,
+ company_ids: filters.company_ids.length ? filters.company_ids.map(Number) : null,
+ expired_before: filters.expired_before ? filters.expired_before.toISOString() : null,
+ dry_run: false,
+ };
+ Ar1Service.adminBulkRecompilation(payload,
+ (resp) => {
+ setSubmitting(false);
+ toast.current?.show({ severity: 'success', summary: 'Inviato', detail: `Marcati ${resp.marked_count} form per PEC` });
+ setDryRunResult(null);
+ },
+ (err) => { setSubmitting(false); toast.current?.show({ severity: 'error', summary: 'Errore', detail: err?.detail }); }
+ );
+ }
+ });
+ };
+
+ return (
+
+
+ {__('Invia una PEC di sollecito ricompilazione a un insieme filtrato di aziende. Il micro-servizio marca i form; il BE poller invia la PEC tenant-aware (PEC Massiva o Mailgun) entro 5 minuti.', 'gepafin')}
+
+
+
+
+ setFilters({ ...filters, only_expired: e.checked })} />
+ {__('Solo aziende con AR1 EXPIRED', 'gepafin')}
+
+
+ setFilters({ ...filters, only_missing: e.checked })} />
+ {__('Solo aziende senza AR1 (richiede company_ids)', 'gepafin')}
+
+
+ {__('Company IDs (opzionale, lista)', 'gepafin')}
+ setFilters({ ...filters, company_ids: e.value })} placeholder={__('es. 1, 2, 15', 'gepafin')} />
+
+
+ {__('Scaduto prima di (solo con EXPIRED)', 'gepafin')}
+ setFilters({ ...filters, expired_before: e.value })} dateFormat="dd/mm/yy" showIcon style={{ width: '100%' }} />
+
+
+
+
+
+ {dryRunResult && dryRunResult.matched_count > 0 && (
+
+ )}
+
+
+ {dryRunResult && (
+
+
{__('Risultato dry-run', 'gepafin')}
+
{__('Aziende matchate:', 'gepafin')} {dryRunResult.matched_count}
+
{__('Già in coda PEC:', 'gepafin')} {dryRunResult.already_pending_count || 0}
+ {dryRunResult.sample_company_ids && dryRunResult.sample_company_ids.length > 0 && (
+
{__('Sample company IDs (max 20):', 'gepafin')} {dryRunResult.sample_company_ids.join(', ')}
+ )}
+ {dryRunResult.note &&
{dryRunResult.note}
}
+
+ )}
+
+ );
+};
+
+
+export default Ar1AdminConfig;
diff --git a/src/modules/ar1/service/ar1Service.js b/src/modules/ar1/service/ar1Service.js
index 4a0129e..75c34c4 100644
--- a/src/modules/ar1/service/ar1Service.js
+++ b/src/modules/ar1/service/ar1Service.js
@@ -153,6 +153,110 @@ const Ar1Service = {
.catch(e => handleError(e, onError));
},
+ // ---------- ADMIN: Templates ----------
+ listTemplates(onSuccess, onError, queryStr = '') {
+ fetch(`${BASE_URL}/admin/ar1-templates${queryStr}`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ getTemplateDetail(templateId, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${templateId}`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ updateTemplateLayout(templateId, layoutConfig, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${templateId}/layout-config`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify({ layout_config: layoutConfig })
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ createNewTemplateVersion(variant, payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${variant}/new-version`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ // ---------- ADMIN: Policy ----------
+ getPolicy(onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-policy`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ updatePolicy(payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-policy`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ // ---------- ADMIN: PEC Schedule Config (CRUD) ----------
+ listPecSchedule(onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ createPecRule(payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ updatePecRule(ruleId, payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config/${ruleId}`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
+ deletePecRule(ruleId, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config/${ruleId}`, {
+ method: 'DELETE', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => {
+ if (r.status === 204) {
+ if (onSuccess) onSuccess({});
+ } else {
+ handleResponse(r, onSuccess, onError);
+ }
+ })
+ .catch(e => handleError(e, onError));
+ },
+
+ // ---------- ADMIN: Bulk PEC ----------
+ bulkRequestRecompilation(payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-forms/bulk-request-recompilation`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+ },
+
// ---------- Archive manuale (di solito automatico) ----------
archiveToCompanyDocument(formId, onSuccess, onError) {
fetch(`${BASE_URL}/api/ar1-forms/${formId}/archive-to-company-document`, {
@@ -164,3 +268,102 @@ const Ar1Service = {
};
export default Ar1Service;
+
+// ========== ADMIN METHODS (aggiunti fase admin) ==========
+
+Ar1Service.adminListTemplates = function (onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminGetTemplate = function (templateId, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${templateId}`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminUpdateLayoutConfig = function (templateId, layoutConfig, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${templateId}/layout-config`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify({ layout_config: layoutConfig })
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminNewVersion = function (variant, payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-templates/${variant}/new-version`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminGetPolicy = function (onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-policy`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminUpdatePolicy = function (payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-policy`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminListPecSchedule = function (onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config`, {
+ method: 'GET', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminCreatePecRule = function (payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminUpdatePecRule = function (ruleId, payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config/${ruleId}`, {
+ method: 'PUT', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminDeletePecRule = function (ruleId, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-pec-schedule-config/${ruleId}`, {
+ method: 'DELETE', mode: 'cors', headers: buildHeaders()
+ })
+ .then(r => {
+ if (r.status === 204) { if (onSuccess) onSuccess({}); }
+ else handleResponse(r, onSuccess, onError);
+ })
+ .catch(e => handleError(e, onError));
+};
+
+Ar1Service.adminBulkRecompilation = function (payload, onSuccess, onError) {
+ fetch(`${BASE_URL}/admin/ar1-forms/bulk-request-recompilation`, {
+ method: 'POST', mode: 'cors', headers: buildHeaders(),
+ body: JSON.stringify(payload)
+ })
+ .then(r => handleResponse(r, onSuccess, onError))
+ .catch(e => handleError(e, onError));
+};
diff --git a/src/routes.js b/src/routes.js
index 65985e2..e011e6b 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -24,6 +24,7 @@ import IstruttoriaPratica from './modules/rendicontazione/pages/IstruttoriaPrati
import Ar1Home from './modules/ar1/pages/Ar1Home';
import Ar1Wizard from './modules/ar1/pages/Ar1Wizard';
import Ar1Signature from './modules/ar1/pages/Ar1Signature';
+import Ar1AdminConfig from './modules/ar1/pages/Ar1AdminConfig';
import BandoFlowEdit from './pages/BandoFlowEdit';
import Imieibandi from './pages/Imieibandi';
import BandoApplication from './pages/BandoApplication';
@@ -187,6 +188,13 @@ const routes = ({ role, chosenCompanyId }) => {
{'ROLE_PRE_INSTRUCTOR' === role ? : null}
{'ROLE_INSTRUCTOR_MANAGER' === role ? : null}
}/>
+
+ {'ROLE_SUPER_ADMIN' === role ? : }
+ {'ROLE_BENEFICIARY' === role ? : null}
+ {'ROLE_CONFIDI' === role ? : null}
+ {'ROLE_PRE_INSTRUCTOR' === role ? : null}
+ {'ROLE_INSTRUCTOR_MANAGER' === role ? : null}
+ }/>
{'ROLE_BENEFICIARY' === role ? : null}
{'ROLE_SUPER_ADMIN' === role ? : null}