feat(rendicontazione): integrazione upload reale + preview PDF + verbale nei flussi
IstruttoriaPratica.js: - previewDialog esteso con entityType/entityId (non piu solo filename) - openPreview/closePreview/doDownload rimpiazzano openPreview/downloadStub stub - Dialog placeholder 'anteprima simulata' rimosso, sostituito con <FilePreviewDialog/> - Bottoni anteprima/scarica in fatture/ULA/documenti usano gli endpoint reali (disabled se !storage_path) - Nuovi bottoni 'Anteprima verbale' (HTML tab) e 'Scarica verbale PDF' nella toolbar per status in UNDER_REVIEW/AWAITING_AMENDMENT/APPROVED/REJECTED - downloadVerbale/openVerbaleHtml helpers PraticaRendicontazioneEdit.js: - previewDialog state + openPreview/closePreview - updateInvoiceFile/updateUlaFile/updateDocFile: aggiornano lo stato locale dopo upload/delete senza full reload pagina - ensureDocRecord: auto-crea RemissionDocument (via upsertDocument con filename=null) prima dell'upload cosi FileUploadCell ha un entityId valido - Colonne 'Allegato' nelle DataTable fatture/ULA ora renderizzano <FileUploadCell/> con onPreview/onChange wired - Sezione documenti: FileUploadCell per record esistenti, bottone 'Carica' per record non ancora creati - Modal fattura: rimosso campo 'Nome file PDF (simulato)', infobox post-save guida al caricamento dalla tabella - Modal dipendente: rimosso campo 'Nome file allegato (simulato)', infobox analogo - <FilePreviewDialog/> montato in chiusura Test JSX: @babel/parser OK su entrambi i file. Webpack ricompila hot-reload.
This commit is contained in:
@@ -16,6 +16,7 @@ import { Checkbox } from 'primereact/checkbox';
|
||||
import { ConfirmPopup, confirmPopup } from 'primereact/confirmpopup';
|
||||
|
||||
import RendicontazioneService from '../service/rendicontazioneService';
|
||||
import FilePreviewDialog from '../components/FilePreviewDialog';
|
||||
|
||||
const CONTRACT_TYPES = {
|
||||
T_IND: 'Tempo indeterminato', T_DET: 'Tempo determinato',
|
||||
@@ -67,7 +68,7 @@ const IstruttoriaPratica = () => {
|
||||
const [bundle, setBundle] = useState(null);
|
||||
|
||||
// dialoghi
|
||||
const [previewDialog, setPreviewDialog] = useState({ visible: false, filename: null, title: null });
|
||||
const [previewDialog, setPreviewDialog] = useState({ visible: false, entityType: null, entityId: null, filename: null, title: null });
|
||||
const [docNoteDialog, setDocNoteDialog] = useState({ visible: false, doc: null, status: null });
|
||||
// tabelle: expanded rows + buffer modifiche inline
|
||||
const [expandedInv, setExpandedInv] = useState({});
|
||||
@@ -123,10 +124,22 @@ const IstruttoriaPratica = () => {
|
||||
detail: typeof err?.detail === 'object' ? JSON.stringify(err.detail) : err?.detail });
|
||||
};
|
||||
|
||||
const openPreview = (filename, title) => setPreviewDialog({ visible: true, filename, title });
|
||||
const downloadStub = (filename) => {
|
||||
toast.current?.show({ severity: 'info', summary: __('Sandbox', 'gepafin'),
|
||||
detail: __(`Download di ${filename} — in produzione scarica il file reale dallo storage.`, 'gepafin') });
|
||||
const openPreview = (entityType, entityId, title, filename) => setPreviewDialog({ visible: true, entityType, entityId, title, filename });
|
||||
const closePreview = () => setPreviewDialog({ visible: false, entityType: null, entityId: null, filename: null, title: null });
|
||||
const downloadVerbale = () => {
|
||||
RendicontazioneService.downloadVerbale(practiceId,
|
||||
(err) => toast.current?.show({ severity: 'error', summary: __('Errore', 'gepafin'), detail: err?.detail || __('Verbale non disponibile', 'gepafin') })
|
||||
);
|
||||
};
|
||||
const openVerbaleHtml = () => {
|
||||
RendicontazioneService.openVerbaleHtml(practiceId).catch(() =>
|
||||
toast.current?.show({ severity: 'error', summary: __('Errore', 'gepafin'), detail: __('Verbale non disponibile', 'gepafin') })
|
||||
);
|
||||
};
|
||||
const doDownload = (entityType, entityId) => {
|
||||
RendicontazioneService.downloadEntityFile(entityType, entityId,
|
||||
(err) => toast.current?.show({ severity: 'error', summary: __('Errore', 'gepafin'), detail: err?.detail || __('Download non riuscito', 'gepafin') })
|
||||
);
|
||||
};
|
||||
|
||||
// Quick verify (thumbs up/down) senza rettifica
|
||||
@@ -403,6 +416,16 @@ const IstruttoriaPratica = () => {
|
||||
disabled={openAmendments.length > 0}
|
||||
onClick={() => setAmendDialog({ visible: true, text: '', deadline: null })} />
|
||||
</>)}
|
||||
|
||||
{/* Verbale: sempre visibile all'istruttore per preview e scarico */}
|
||||
{['UNDER_REVIEW', 'AWAITING_AMENDMENT', 'APPROVED', 'REJECTED'].includes(practice.status) && (<>
|
||||
<Button type="button" icon="pi pi-eye" iconPos="right" outlined
|
||||
label={__('Anteprima verbale', 'gepafin')}
|
||||
onClick={openVerbaleHtml} />
|
||||
<Button type="button" icon="pi pi-file-pdf" iconPos="right" severity="info"
|
||||
label={__('Scarica verbale PDF', 'gepafin')}
|
||||
onClick={downloadVerbale} />
|
||||
</>)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -658,10 +681,12 @@ const IstruttoriaPratica = () => {
|
||||
body={(r) => (
|
||||
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
||||
<Button icon="pi pi-eye" rounded outlined size="small" severity="info"
|
||||
onClick={() => openPreview(r.pdf_filename || `fattura_${r.invoice_number}.pdf`, `Fattura ${r.invoice_number}`)}
|
||||
disabled={!r.storage_path}
|
||||
onClick={() => openPreview('invoice', r.id, `Fattura ${r.invoice_number}`, r.pdf_filename)}
|
||||
tooltip={__('Anteprima', 'gepafin')} tooltipOptions={{ position: 'top' }} />
|
||||
<Button icon="pi pi-download" rounded outlined size="small" severity="info"
|
||||
onClick={() => downloadStub(r.pdf_filename || `fattura_${r.invoice_number}.pdf`)}
|
||||
disabled={!r.storage_path}
|
||||
onClick={() => doDownload('invoice', r.id)}
|
||||
tooltip={__('Scarica', 'gepafin')} tooltipOptions={{ position: 'top' }} />
|
||||
<Button icon="pi pi-check" rounded outlined size="small"
|
||||
severity={r.verification_status === 'AMMESSA' ? 'success' : 'secondary'}
|
||||
@@ -814,12 +839,12 @@ const IstruttoriaPratica = () => {
|
||||
body={(r) => (
|
||||
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
||||
<Button icon="pi pi-eye" rounded outlined size="small" severity="info"
|
||||
disabled={!r.supporting_doc_filename}
|
||||
onClick={() => openPreview(r.supporting_doc_filename, `${r.full_name}`)}
|
||||
disabled={!r.storage_path}
|
||||
onClick={() => openPreview('ula', r.id, `${r.full_name}`, r.supporting_doc_filename)}
|
||||
tooltip={__('Anteprima allegato', 'gepafin')} tooltipOptions={{ position: 'top' }} />
|
||||
<Button icon="pi pi-download" rounded outlined size="small" severity="info"
|
||||
disabled={!r.supporting_doc_filename}
|
||||
onClick={() => downloadStub(r.supporting_doc_filename)}
|
||||
disabled={!r.storage_path}
|
||||
onClick={() => doDownload('ula', r.id)}
|
||||
tooltip={__('Scarica allegato', 'gepafin')} tooltipOptions={{ position: 'top' }} />
|
||||
<Button icon="pi pi-check" rounded outlined size="small"
|
||||
severity={r.verification_status === 'AMMESSA' ? 'success' : 'secondary'}
|
||||
@@ -877,13 +902,13 @@ const IstruttoriaPratica = () => {
|
||||
</div>
|
||||
<div className="appPageSection__iconActions">
|
||||
<Button icon="pi pi-eye" rounded outlined severity="info"
|
||||
disabled={!doc.filename}
|
||||
onClick={() => openPreview(doc.filename, `${dr.label} — ${doc.filename || ''}`)}
|
||||
disabled={!doc.storage_path}
|
||||
onClick={() => openPreview('document', doc.id, `${dr.label}`, doc.filename)}
|
||||
tooltip={__('Anteprima', 'gepafin')} tooltipOptions={{ position: 'top' }}
|
||||
aria-label={__('Anteprima', 'gepafin')} />
|
||||
<Button icon="pi pi-download" rounded outlined severity="info"
|
||||
disabled={!doc.filename}
|
||||
onClick={() => downloadStub(doc.filename)}
|
||||
disabled={!doc.storage_path}
|
||||
onClick={() => doDownload('document', doc.id)}
|
||||
tooltip={__('Scarica', 'gepafin')} tooltipOptions={{ position: 'top' }}
|
||||
aria-label={__('Scarica', 'gepafin')} />
|
||||
<Button icon="pi pi-clock" rounded outlined severity="warning"
|
||||
@@ -963,22 +988,15 @@ const IstruttoriaPratica = () => {
|
||||
|
||||
{/* ============ DIALOGS ============ */}
|
||||
|
||||
{/* Preview PDF */}
|
||||
<Dialog visible={previewDialog.visible} style={{ width: '640px' }}
|
||||
header={previewDialog.title || __('Anteprima', 'gepafin')} modal
|
||||
onHide={() => setPreviewDialog({ visible: false, filename: null, title: null })}>
|
||||
<div style={{ padding: '2rem', textAlign: 'center', background: 'var(--surface-100)', borderRadius: '6px' }}>
|
||||
<i className="pi pi-file-pdf" style={{ fontSize: '4rem', color: 'var(--red-500)' }} />
|
||||
<h3 style={{ margin: '1rem 0 0.5rem 0' }}>{previewDialog.filename || '—'}</h3>
|
||||
<p className="text-color-secondary" style={{ marginTop: 0 }}>
|
||||
{__('Anteprima PDF — in sandbox il file reale non è caricato. In produzione qui viene visualizzato il PDF originale della fattura/documento.', 'gepafin')}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '1rem' }}>
|
||||
<Button label={__('Chiudi', 'gepafin')} outlined
|
||||
onClick={() => setPreviewDialog({ visible: false, filename: null, title: null })} />
|
||||
</div>
|
||||
</Dialog>
|
||||
{/* Preview PDF reale — iframe con blob autenticato */}
|
||||
<FilePreviewDialog
|
||||
visible={previewDialog.visible}
|
||||
onHide={closePreview}
|
||||
entityType={previewDialog.entityType}
|
||||
entityId={previewDialog.entityId}
|
||||
title={previewDialog.title}
|
||||
filename={previewDialog.filename}
|
||||
/>
|
||||
|
||||
{/* Rettifica fattura */}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import { Column } from 'primereact/column';
|
||||
|
||||
// api
|
||||
import RendicontazioneService from '../service/rendicontazioneService';
|
||||
import FileUploadCell from '../components/FileUploadCell';
|
||||
import FilePreviewDialog from '../components/FilePreviewDialog';
|
||||
|
||||
// ---------- costanti ----------
|
||||
const IVA_REGIME_LABELS = {
|
||||
@@ -81,6 +83,57 @@ const PraticaRendicontazioneEdit = () => {
|
||||
const [empDialog, setEmpDialog] = useState({ visible: false, data: null });
|
||||
// modal risposta soccorso istruttorio
|
||||
const [amendDialog, setAmendDialog] = useState({ visible: false, amendment: null, responseText: '' });
|
||||
// preview file
|
||||
const [previewDialog, setPreviewDialog] = useState({ visible: false, entityType: null, entityId: null, filename: null, title: null });
|
||||
const openPreview = (entityType, entityId, title, filename) => setPreviewDialog({ visible: true, entityType, entityId, title, filename });
|
||||
const closePreview = () => setPreviewDialog({ visible: false, entityType: null, entityId: null, filename: null, title: null });
|
||||
// update locale riga dopo upload/delete
|
||||
const updateInvoiceFile = (invoiceId, fileMeta) => {
|
||||
setPractice(p => p ? { ...p, invoices: p.invoices.map(i => i.id === invoiceId ? {
|
||||
...i,
|
||||
pdf_filename: fileMeta ? fileMeta.filename_original : null,
|
||||
storage_path: fileMeta ? fileMeta.storage_path : null,
|
||||
size_bytes: fileMeta ? fileMeta.size_bytes : null,
|
||||
} : i) } : p);
|
||||
};
|
||||
const updateUlaFile = (empId, fileMeta) => {
|
||||
setPractice(p => p ? { ...p, ula_employees: p.ula_employees.map(e => e.id === empId ? {
|
||||
...e,
|
||||
supporting_doc_filename: fileMeta ? fileMeta.filename_original : null,
|
||||
storage_path: fileMeta ? fileMeta.storage_path : null,
|
||||
size_bytes: fileMeta ? fileMeta.size_bytes : null,
|
||||
} : e) } : p);
|
||||
};
|
||||
const updateDocFile = (docCode, docId, fileMeta) => {
|
||||
setPractice(p => {
|
||||
if (!p) return p;
|
||||
const exists = p.documents.find(d => d.doc_code === docCode);
|
||||
const newDocs = exists
|
||||
? p.documents.map(d => d.doc_code === docCode ? {
|
||||
...d,
|
||||
filename: fileMeta ? fileMeta.filename_original : null,
|
||||
storage_path: fileMeta ? fileMeta.storage_path : null,
|
||||
size_bytes: fileMeta ? fileMeta.size_bytes : null,
|
||||
} : d)
|
||||
: p.documents;
|
||||
return { ...p, documents: newDocs };
|
||||
});
|
||||
};
|
||||
// ensure doc record exists (returns id via callback)
|
||||
const ensureDocRecord = (docCode, onReady) => {
|
||||
const existing = practice?.documents?.find(d => d.doc_code === docCode);
|
||||
if (existing && existing.id) { onReady(existing.id); return; }
|
||||
RendicontazioneService.upsertDocument(practiceId, docCode, { filename: null },
|
||||
(resp) => {
|
||||
const newDoc = resp?.data;
|
||||
if (newDoc && newDoc.id) {
|
||||
setPractice(p => p ? { ...p, documents: [...p.documents.filter(d => d.doc_code !== docCode), newDoc] } : p);
|
||||
onReady(newDoc.id);
|
||||
}
|
||||
},
|
||||
(err) => toast.current?.show({ severity: 'error', summary: __('Errore preparazione documento', 'gepafin'), detail: err?.detail })
|
||||
);
|
||||
};
|
||||
|
||||
// ---------- load ----------
|
||||
const load = useCallback(() => {
|
||||
@@ -438,6 +491,17 @@ const PraticaRendicontazioneEdit = () => {
|
||||
body={(r) => <span title={r.description}>{r.description.slice(0, 40)}{r.description.length > 40 ? '…' : ''}</span>} />
|
||||
<Column field="taxable" header={__('Imponibile', 'gepafin')} body={(r) => euro(r.taxable)} />
|
||||
<Column field="total" header={__('Totale', 'gepafin')} body={(r) => euro(r.total)} />
|
||||
<Column header={__('Allegato PDF', 'gepafin')} style={{ minWidth: '280px' }}
|
||||
body={(r) => (
|
||||
<FileUploadCell
|
||||
entityType="invoice" entityId={r.id}
|
||||
filename={r.pdf_filename} sizeBytes={r.size_bytes}
|
||||
readOnly={readOnly}
|
||||
onPreview={() => openPreview('invoice', r.id, `Fattura ${r.invoice_number}`, r.pdf_filename)}
|
||||
onChange={(meta) => updateInvoiceFile(r.id, meta)}
|
||||
toastRef={toast}
|
||||
/>
|
||||
)} />
|
||||
{!readOnly && (
|
||||
<Column header="" body={(r) => (
|
||||
<Button icon="pi pi-trash" severity="danger" outlined size="small"
|
||||
@@ -478,8 +542,17 @@ const PraticaRendicontazioneEdit = () => {
|
||||
<Column field="fte_pct" header="FTE" body={(r) => Number(r.fte_pct).toFixed(2)} />
|
||||
<Column header={__('Periodo', 'gepafin')}
|
||||
body={(r) => `${formatDate(r.period_start_date)} → ${formatDate(r.period_end_date)}`} />
|
||||
<Column field="supporting_doc_filename" header={__('Allegato', 'gepafin')}
|
||||
body={(r) => r.supporting_doc_filename ? <span><i className="pi pi-file" /> {r.supporting_doc_filename}</span> : <span className="text-color-secondary">—</span>} />
|
||||
<Column header={__('Allegato', 'gepafin')} style={{ minWidth: '280px' }}
|
||||
body={(r) => (
|
||||
<FileUploadCell
|
||||
entityType="ula" entityId={r.id}
|
||||
filename={r.supporting_doc_filename} sizeBytes={r.size_bytes}
|
||||
readOnly={readOnly}
|
||||
onPreview={() => openPreview('ula', r.id, r.full_name, r.supporting_doc_filename)}
|
||||
onChange={(meta) => updateUlaFile(r.id, meta)}
|
||||
toastRef={toast}
|
||||
/>
|
||||
)} />
|
||||
{!readOnly && (
|
||||
<Column header="" body={(r) => (
|
||||
<Button icon="pi pi-trash" severity="danger" outlined size="small"
|
||||
@@ -507,37 +580,35 @@ const PraticaRendicontazioneEdit = () => {
|
||||
<div className="fieldsRepeater">
|
||||
{docsRequired.map((dr) => {
|
||||
const existing = practice.documents.find(d => d.doc_code === dr.code);
|
||||
const hasFile = !!(existing && existing.filename);
|
||||
return (
|
||||
<div key={dr.code} className="fieldsRepeater__panel"
|
||||
style={{ border: '1px solid var(--surface-border)', borderRadius: '6px', padding: '0.75rem 1rem',
|
||||
display: 'flex', alignItems: 'center', gap: '1rem', flexWrap: 'wrap' }}>
|
||||
<i className={existing?.filename ? 'pi pi-check-circle' : 'pi pi-circle'}
|
||||
style={{ color: existing?.filename ? 'var(--green-500)' : 'var(--text-color-secondary)', fontSize: '1.25rem' }} />
|
||||
<i className={hasFile ? 'pi pi-check-circle' : 'pi pi-circle'}
|
||||
style={{ color: hasFile ? 'var(--green-500)' : 'var(--text-color-secondary)', fontSize: '1.25rem' }} />
|
||||
<div style={{ flex: 1, minWidth: '200px' }}>
|
||||
<strong>{dr.label}</strong>
|
||||
<div><small className="text-color-secondary"><code>{dr.code}</code></small></div>
|
||||
</div>
|
||||
<div style={{ flex: 2, minWidth: '220px' }}>
|
||||
{existing?.filename
|
||||
? <span><i className="pi pi-file" /> {existing.filename}</span>
|
||||
: <span className="text-color-secondary">{__('Nessun file', 'gepafin')}</span>}
|
||||
<div style={{ flex: 2, minWidth: '260px' }}>
|
||||
{existing && existing.id ? (
|
||||
<FileUploadCell
|
||||
entityType="document" entityId={existing.id}
|
||||
filename={existing.filename} sizeBytes={existing.size_bytes}
|
||||
readOnly={readOnly}
|
||||
onPreview={() => openPreview('document', existing.id, dr.label, existing.filename)}
|
||||
onChange={(meta) => updateDocFile(dr.code, existing.id, meta)}
|
||||
toastRef={toast}
|
||||
/>
|
||||
) : !readOnly ? (
|
||||
<Button type="button" icon="pi pi-upload" size="small" outlined
|
||||
label={__('Carica', 'gepafin')}
|
||||
onClick={() => ensureDocRecord(dr.code, () => {/* reload not needed, setPractice already updated */})} />
|
||||
) : (
|
||||
<span className="text-color-secondary">{__('Nessun file', 'gepafin')}</span>
|
||||
)}
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
||||
<Button type="button" icon="pi pi-upload" size="small"
|
||||
label={existing?.filename ? __('Sostituisci', 'gepafin') : __('Carica', 'gepafin')}
|
||||
outlined={!!existing?.filename}
|
||||
onClick={() => {
|
||||
const fname = prompt(__('Nome del file (simulato)', 'gepafin'),
|
||||
existing?.filename || `${dr.code}.pdf`);
|
||||
if (fname) upsertDocument(dr.code, fname);
|
||||
}} />
|
||||
{existing?.filename && (
|
||||
<Button type="button" icon="pi pi-trash" severity="danger" outlined size="small"
|
||||
onClick={() => clearDocument(dr.code)} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -615,10 +686,9 @@ const PraticaRendicontazioneEdit = () => {
|
||||
onValueChange={(e) => setInvDialog(d => ({ ...d, data: { ...d.data, total: e.value } }))} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="appForm__field">
|
||||
<label>{__('Nome file PDF (simulato)', 'gepafin')}</label>
|
||||
<InputText value={invDialog.data.pdf_filename}
|
||||
onChange={(e) => setInvDialog(d => ({ ...d, data: { ...d.data, pdf_filename: e.target.value } }))} />
|
||||
<div className="appForm__field" style={{ color: 'var(--text-color-secondary)', fontSize: '0.9em', padding: '0.5rem 0.75rem', background: 'var(--surface-50)', borderRadius: '4px' }}>
|
||||
<i className="pi pi-info-circle" style={{ marginRight: '0.4rem' }} />
|
||||
{__('Dopo aver salvato la fattura potrai caricare il PDF dalla tabella.', 'gepafin')}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.75rem', marginTop: '1rem' }}>
|
||||
<Button type="button" outlined label={__('Annulla', 'gepafin')} onClick={() => setInvDialog({ visible: false, data: null })} />
|
||||
@@ -706,11 +776,10 @@ const PraticaRendicontazioneEdit = () => {
|
||||
options={(ulaSection.supporting_doc_types || []).map(t => typeof t === 'string' ? { value: t, label: t } : { value: t.code, label: t.label })}
|
||||
onChange={(e) => setEmpDialog(d => ({ ...d, data: { ...d.data, supporting_doc_type: e.value } }))} />
|
||||
</div>
|
||||
<div className="appForm__field">
|
||||
<label>{__('Nome file allegato (simulato)', 'gepafin')}</label>
|
||||
<InputText value={empDialog.data.supporting_doc_filename}
|
||||
onChange={(e) => setEmpDialog(d => ({ ...d, data: { ...d.data, supporting_doc_filename: e.target.value } }))} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="appForm__field" style={{ color: 'var(--text-color-secondary)', fontSize: '0.9em', padding: '0.5rem 0.75rem', background: 'var(--surface-50)', borderRadius: '4px' }}>
|
||||
<i className="pi pi-info-circle" style={{ marginRight: '0.4rem' }} />
|
||||
{__("Dopo aver salvato il dipendente potrai caricare il LUL o il documento di supporto dalla tabella.", 'gepafin')}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.75rem', marginTop: '1rem' }}>
|
||||
<Button type="button" outlined label={__('Annulla', 'gepafin')} onClick={() => setEmpDialog({ visible: false, data: null })} />
|
||||
@@ -719,6 +788,14 @@ const PraticaRendicontazioneEdit = () => {
|
||||
</form>
|
||||
)}
|
||||
</Dialog>
|
||||
<FilePreviewDialog
|
||||
visible={previewDialog.visible}
|
||||
onHide={closePreview}
|
||||
entityType={previewDialog.entityType}
|
||||
entityId={previewDialog.entityId}
|
||||
title={previewDialog.title}
|
||||
filename={previewDialog.filename}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user