@hed-hog/operations 0.0.318 → 0.0.321
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/controllers/operations-collaborator-costs.controller.d.ts +144 -0
- package/dist/controllers/operations-collaborator-costs.controller.d.ts.map +1 -0
- package/dist/controllers/operations-collaborator-costs.controller.js +162 -0
- package/dist/controllers/operations-collaborator-costs.controller.js.map +1 -0
- package/dist/controllers/operations-collaborators.controller.d.ts +14 -0
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +11 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +9 -9
- package/dist/controllers/operations-projects.controller.d.ts +31 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +23 -0
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-reports.controller.d.ts +199 -0
- package/dist/controllers/operations-reports.controller.d.ts.map +1 -0
- package/dist/controllers/operations-reports.controller.js +53 -0
- package/dist/controllers/operations-reports.controller.js.map +1 -0
- package/dist/controllers/operations-tasks.controller.d.ts +41 -2
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +17 -5
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/dto/create-collaborator-cost.dto.d.ts +16 -0
- package/dist/dto/create-collaborator-cost.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-cost.dto.js +88 -0
- package/dist/dto/create-collaborator-cost.dto.js.map +1 -0
- package/dist/dto/create-collaborator.dto.d.ts +0 -1
- package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
- package/dist/dto/create-collaborator.dto.js +0 -6
- package/dist/dto/create-collaborator.dto.js.map +1 -1
- package/dist/dto/create-cost-type.dto.d.ts +13 -0
- package/dist/dto/create-cost-type.dto.d.ts.map +1 -0
- package/dist/dto/create-cost-type.dto.js +87 -0
- package/dist/dto/create-cost-type.dto.js.map +1 -0
- package/dist/dto/list-approvals.dto.d.ts +2 -0
- package/dist/dto/list-approvals.dto.d.ts.map +1 -1
- package/dist/dto/list-approvals.dto.js +10 -0
- package/dist/dto/list-approvals.dto.js.map +1 -1
- package/dist/dto/list-collaborator-costs.dto.d.ts +5 -0
- package/dist/dto/list-collaborator-costs.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-costs.dto.js +23 -0
- package/dist/dto/list-collaborator-costs.dto.js.map +1 -0
- package/dist/dto/list-cost-types.dto.d.ts +6 -0
- package/dist/dto/list-cost-types.dto.d.ts.map +1 -0
- package/dist/dto/list-cost-types.dto.js +35 -0
- package/dist/dto/list-cost-types.dto.js.map +1 -0
- package/dist/dto/list-my-projects.dto.d.ts +5 -0
- package/dist/dto/list-my-projects.dto.d.ts.map +1 -0
- package/dist/dto/list-my-projects.dto.js +23 -0
- package/dist/dto/list-my-projects.dto.js.map +1 -0
- package/dist/dto/list-my-tasks.dto.d.ts +6 -0
- package/dist/dto/list-my-tasks.dto.d.ts.map +1 -0
- package/dist/dto/list-my-tasks.dto.js +33 -0
- package/dist/dto/list-my-tasks.dto.js.map +1 -0
- package/dist/dto/list-projects.dto.d.ts +1 -0
- package/dist/dto/list-projects.dto.d.ts.map +1 -1
- package/dist/dto/list-projects.dto.js +7 -0
- package/dist/dto/list-projects.dto.js.map +1 -1
- package/dist/dto/list-reports.dto.d.ts +16 -0
- package/dist/dto/list-reports.dto.d.ts.map +1 -0
- package/dist/dto/list-reports.dto.js +75 -0
- package/dist/dto/list-reports.dto.js.map +1 -0
- package/dist/dto/list-tasks.dto.d.ts +2 -0
- package/dist/dto/list-tasks.dto.d.ts.map +1 -1
- package/dist/dto/list-tasks.dto.js +12 -0
- package/dist/dto/list-tasks.dto.js.map +1 -1
- package/dist/dto/list-timesheets.dto.d.ts +2 -0
- package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
- package/dist/dto/list-timesheets.dto.js +10 -0
- package/dist/dto/list-timesheets.dto.js.map +1 -1
- package/dist/dto/update-collaborator-cost.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-cost.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-cost.dto.js +9 -0
- package/dist/dto/update-collaborator-cost.dto.js.map +1 -0
- package/dist/dto/update-task.dto.d.ts +1 -0
- package/dist/dto/update-task.dto.d.ts.map +1 -1
- package/dist/dto/update-task.dto.js +6 -0
- package/dist/dto/update-task.dto.js.map +1 -1
- package/dist/operations.module.d.ts.map +1 -1
- package/dist/operations.module.js +4 -0
- package/dist/operations.module.js.map +1 -1
- package/dist/operations.service.d.ts +457 -3
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1445 -208
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +31 -7
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +112 -7
- package/hedhog/data/operations_cost_type.yaml +166 -0
- package/hedhog/data/route.yaml +185 -0
- package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +884 -0
- package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +80 -1
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +219 -94
- package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +21 -32
- package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +178 -89
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +1185 -0
- package/hedhog/frontend/app/_components/operations-calendar-view.tsx.ejs +306 -0
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +943 -782
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +223 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +162 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +227 -1
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +11 -3
- package/hedhog/frontend/app/approvals/page.tsx.ejs +191 -46
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +133 -25
- package/hedhog/frontend/app/my-projects/[id]/page.tsx.ejs +11 -0
- package/hedhog/frontend/app/my-projects/page.tsx.ejs +440 -0
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +1304 -0
- package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +771 -0
- package/hedhog/frontend/app/reports/projects/page.tsx.ejs +809 -0
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +322 -58
- package/hedhog/frontend/messages/en.json +234 -25
- package/hedhog/frontend/messages/pt.json +234 -25
- package/hedhog/table/operations_collaborator.yaml +0 -4
- package/hedhog/table/operations_collaborator_compensation_history.yaml +28 -0
- package/hedhog/table/operations_collaborator_cost.yaml +56 -0
- package/hedhog/table/operations_cost_type.yaml +38 -0
- package/package.json +7 -7
- package/src/controllers/operations-collaborator-costs.controller.ts +147 -0
- package/src/controllers/operations-collaborators.controller.ts +19 -8
- package/src/controllers/operations-projects.controller.ts +19 -8
- package/src/controllers/operations-reports.controller.ts +32 -0
- package/src/controllers/operations-tasks.controller.ts +32 -12
- package/src/dto/create-collaborator-cost.dto.ts +78 -0
- package/src/dto/create-collaborator.dto.ts +9 -14
- package/src/dto/create-cost-type.dto.ts +62 -0
- package/src/dto/list-approvals.dto.ts +8 -0
- package/src/dto/list-collaborator-costs.dto.ts +8 -0
- package/src/dto/list-cost-types.dto.ts +19 -0
- package/src/dto/list-my-projects.dto.ts +8 -0
- package/src/dto/list-my-tasks.dto.ts +17 -0
- package/src/dto/list-projects.dto.ts +7 -1
- package/src/dto/list-reports.dto.ts +51 -0
- package/src/dto/list-tasks.dto.ts +11 -1
- package/src/dto/list-timesheets.dto.ts +8 -0
- package/src/dto/update-collaborator-cost.dto.ts +4 -0
- package/src/dto/update-task.dto.ts +6 -0
- package/src/operations.module.ts +4 -0
- package/src/operations.service.spec.ts +45 -7
- package/src/operations.service.ts +1988 -221
|
@@ -88,12 +88,6 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
88
88
|
) ??
|
|
89
89
|
contract.documents.find((document) => document.isCurrent) ??
|
|
90
90
|
null;
|
|
91
|
-
|
|
92
|
-
const previewSource = currentDocument?.fileContentBase64
|
|
93
|
-
? `data:${currentDocument.mimeType};base64,${currentDocument.fileContentBase64}`
|
|
94
|
-
: buildStoredFileUrl(currentDocument?.fileId);
|
|
95
|
-
const canPreview =
|
|
96
|
-
Boolean(previewSource) && currentDocument?.mimeType?.includes('pdf');
|
|
97
91
|
const contractTitle =
|
|
98
92
|
contract.name || contract.code || commonT('labels.notAvailable');
|
|
99
93
|
|
|
@@ -106,6 +100,21 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
106
100
|
return formT.has(key) ? formT(key) : formatEnumLabel(value);
|
|
107
101
|
};
|
|
108
102
|
|
|
103
|
+
const getHistoryNoteLabel = (note?: string | null) => {
|
|
104
|
+
if (!note) {
|
|
105
|
+
return commonT('labels.noNotes');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const normalizedNote = note.trim();
|
|
109
|
+
const historyNoteMap: Record<string, string> = {
|
|
110
|
+
'Manual contract created from registry.': t('historyNotes.manualCreated'),
|
|
111
|
+
'Contract registry data updated.': t('historyNotes.registryUpdated'),
|
|
112
|
+
'Contract documents removed.': t('historyNotes.documentsRemoved'),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return historyNoteMap[normalizedNote] ?? normalizedNote;
|
|
116
|
+
};
|
|
117
|
+
|
|
109
118
|
return (
|
|
110
119
|
<Page>
|
|
111
120
|
<OperationsHeader
|
|
@@ -117,11 +126,7 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
117
126
|
{currentDocument?.fileId || currentDocument?.fileContentBase64 ? (
|
|
118
127
|
currentDocument.fileId ? (
|
|
119
128
|
<Button variant="outline" size="sm" asChild>
|
|
120
|
-
<a
|
|
121
|
-
href={buildStoredFileUrl(currentDocument.fileId) || '#'}
|
|
122
|
-
target="_blank"
|
|
123
|
-
rel="noreferrer"
|
|
124
|
-
>
|
|
129
|
+
<a href={buildStoredFileUrl(currentDocument.fileId) || '#'} download>
|
|
125
130
|
<Download className="size-4" />
|
|
126
131
|
{t('actions.downloadPdf')}
|
|
127
132
|
</a>
|
|
@@ -155,29 +160,14 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
155
160
|
}
|
|
156
161
|
/>
|
|
157
162
|
|
|
158
|
-
<Tabs defaultValue="
|
|
163
|
+
<Tabs defaultValue="overview">
|
|
159
164
|
<TabsList className="flex-wrap">
|
|
160
|
-
<TabsTrigger value="preview">{t('tabs.preview')}</TabsTrigger>
|
|
161
165
|
<TabsTrigger value="overview">{t('tabs.overview')}</TabsTrigger>
|
|
166
|
+
<TabsTrigger value="parties">{t('tabs.parties')}</TabsTrigger>
|
|
162
167
|
<TabsTrigger value="documents">{t('tabs.documents')}</TabsTrigger>
|
|
168
|
+
<TabsTrigger value="history">{t('tabs.history')}</TabsTrigger>
|
|
163
169
|
</TabsList>
|
|
164
170
|
|
|
165
|
-
<TabsContent value="preview">
|
|
166
|
-
<SectionCard title={t('sections.preview')}>
|
|
167
|
-
{canPreview && previewSource ? (
|
|
168
|
-
<iframe
|
|
169
|
-
title={t('sections.preview')}
|
|
170
|
-
src={previewSource}
|
|
171
|
-
className="h-180 w-full rounded-lg border"
|
|
172
|
-
/>
|
|
173
|
-
) : (
|
|
174
|
-
<div className="rounded-lg border border-dashed px-4 py-12 text-center text-sm text-muted-foreground">
|
|
175
|
-
{t('states.previewUnavailable')}
|
|
176
|
-
</div>
|
|
177
|
-
)}
|
|
178
|
-
</SectionCard>
|
|
179
|
-
</TabsContent>
|
|
180
|
-
|
|
181
171
|
<TabsContent value="overview">
|
|
182
172
|
<SectionCard title={t('sections.overview')}>
|
|
183
173
|
<dl className="grid gap-3 text-sm md:grid-cols-4">
|
|
@@ -286,8 +276,7 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
286
276
|
<Button variant="outline" size="sm" asChild>
|
|
287
277
|
<a
|
|
288
278
|
href={buildStoredFileUrl(document.fileId) || '#'}
|
|
289
|
-
|
|
290
|
-
rel="noreferrer"
|
|
279
|
+
download
|
|
291
280
|
>
|
|
292
281
|
<Download className="size-4" />
|
|
293
282
|
{t('actions.download')}
|
|
@@ -336,7 +325,7 @@ export function ContractDetailsScreen({ contractId }: { contractId: number }) {
|
|
|
336
325
|
<div className="text-muted-foreground">
|
|
337
326
|
{formatDateTime(item.createdAt)}
|
|
338
327
|
</div>
|
|
339
|
-
<div>{item.note
|
|
328
|
+
<div>{getHistoryNoteLabel(item.note)}</div>
|
|
340
329
|
</div>
|
|
341
330
|
))}
|
|
342
331
|
</div>
|
|
@@ -29,6 +29,13 @@ type PartyState = {
|
|
|
29
29
|
isPrimary: boolean;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
type ContractUploadDocument = {
|
|
33
|
+
fileId?: number | null;
|
|
34
|
+
fileName: string;
|
|
35
|
+
mimeType: string;
|
|
36
|
+
fileContentBase64?: string | null;
|
|
37
|
+
};
|
|
38
|
+
|
|
32
39
|
export type ContractFormState = {
|
|
33
40
|
code: string;
|
|
34
41
|
name: string;
|
|
@@ -58,6 +65,8 @@ export type ContractFormState = {
|
|
|
58
65
|
mimeType: string;
|
|
59
66
|
fileContentBase64?: string | null;
|
|
60
67
|
} | null;
|
|
68
|
+
additionalUploadedDocuments: ContractUploadDocument[];
|
|
69
|
+
deletedDocumentIds: number[];
|
|
61
70
|
};
|
|
62
71
|
|
|
63
72
|
function emptyParty(): PartyState {
|
|
@@ -97,6 +106,8 @@ function buildEmptyForm(): ContractFormState {
|
|
|
97
106
|
contentHtml: '',
|
|
98
107
|
parties: [emptyParty()],
|
|
99
108
|
pdfDocument: null,
|
|
109
|
+
additionalUploadedDocuments: [],
|
|
110
|
+
deletedDocumentIds: [],
|
|
100
111
|
};
|
|
101
112
|
}
|
|
102
113
|
|
|
@@ -148,6 +159,8 @@ function toFormState(contract: OperationsContractDetails): ContractFormState {
|
|
|
148
159
|
}))
|
|
149
160
|
: [emptyParty()],
|
|
150
161
|
pdfDocument: null,
|
|
162
|
+
additionalUploadedDocuments: [],
|
|
163
|
+
deletedDocumentIds: [],
|
|
151
164
|
};
|
|
152
165
|
}
|
|
153
166
|
|
|
@@ -276,26 +289,39 @@ export function ContractFormScreen({
|
|
|
276
289
|
);
|
|
277
290
|
}, [contract]);
|
|
278
291
|
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
292
|
+
const downloadableDocuments = useMemo(
|
|
293
|
+
() => [
|
|
294
|
+
...(form.pdfDocument
|
|
295
|
+
? [{ ...form.pdfDocument, id: 'draft-primary', isPending: true }]
|
|
296
|
+
: []),
|
|
297
|
+
...form.additionalUploadedDocuments.map((document, index) => ({
|
|
298
|
+
...document,
|
|
299
|
+
id: `draft-attachment-${index}`,
|
|
300
|
+
isPending: true,
|
|
301
|
+
})),
|
|
302
|
+
...(contract?.documents
|
|
303
|
+
.filter((document) => !form.deletedDocumentIds.includes(document.id))
|
|
304
|
+
.filter(
|
|
305
|
+
(document) =>
|
|
306
|
+
!(
|
|
307
|
+
form.pdfDocument?.fileId &&
|
|
308
|
+
document.fileId === form.pdfDocument.fileId &&
|
|
309
|
+
document.fileName === form.pdfDocument.fileName
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
.map((document) => ({
|
|
313
|
+
...document,
|
|
314
|
+
id: document.id,
|
|
315
|
+
isPending: false,
|
|
316
|
+
})) ?? []),
|
|
317
|
+
],
|
|
318
|
+
[
|
|
319
|
+
contract?.documents,
|
|
320
|
+
form.additionalUploadedDocuments,
|
|
321
|
+
form.deletedDocumentIds,
|
|
322
|
+
form.pdfDocument,
|
|
323
|
+
]
|
|
324
|
+
);
|
|
299
325
|
|
|
300
326
|
const handleSubmit = async () => {
|
|
301
327
|
if (!form.name.trim()) {
|
|
@@ -344,6 +370,8 @@ export function ContractFormScreen({
|
|
|
344
370
|
isPrimary: party.isPrimary,
|
|
345
371
|
})),
|
|
346
372
|
replaceUploadedPdfDocument: form.pdfDocument,
|
|
373
|
+
additionalUploadedDocuments: form.additionalUploadedDocuments,
|
|
374
|
+
deletedDocumentIds: form.deletedDocumentIds,
|
|
347
375
|
};
|
|
348
376
|
|
|
349
377
|
try {
|
|
@@ -380,6 +408,74 @@ export function ContractFormScreen({
|
|
|
380
408
|
}
|
|
381
409
|
};
|
|
382
410
|
|
|
411
|
+
const handleFilesSelected = async (files: FileList | null) => {
|
|
412
|
+
if (!files?.length) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const uploadedDocuments = await Promise.all(
|
|
417
|
+
Array.from(files).map(async (file) => ({
|
|
418
|
+
fileName: file.name,
|
|
419
|
+
mimeType:
|
|
420
|
+
file.type ||
|
|
421
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
422
|
+
fileContentBase64: await fileToBase64(file),
|
|
423
|
+
}))
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
setForm((current) => {
|
|
427
|
+
const hasPrimaryDocument = Boolean(current.pdfDocument || currentDocument);
|
|
428
|
+
const nextPrimaryDocument = hasPrimaryDocument
|
|
429
|
+
? current.pdfDocument
|
|
430
|
+
: uploadedDocuments[0] ?? null;
|
|
431
|
+
const extraDocuments = hasPrimaryDocument
|
|
432
|
+
? uploadedDocuments
|
|
433
|
+
: uploadedDocuments.slice(1);
|
|
434
|
+
|
|
435
|
+
return {
|
|
436
|
+
...current,
|
|
437
|
+
pdfDocument: nextPrimaryDocument,
|
|
438
|
+
additionalUploadedDocuments: [
|
|
439
|
+
...current.additionalUploadedDocuments,
|
|
440
|
+
...extraDocuments,
|
|
441
|
+
],
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const handleRemoveDocument = (documentId: string | number) => {
|
|
447
|
+
setForm((current) => {
|
|
448
|
+
if (documentId === 'draft-primary') {
|
|
449
|
+
return {
|
|
450
|
+
...current,
|
|
451
|
+
pdfDocument: null,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (String(documentId).startsWith('draft-attachment-')) {
|
|
456
|
+
const index = Number(String(documentId).replace('draft-attachment-', ''));
|
|
457
|
+
return {
|
|
458
|
+
...current,
|
|
459
|
+
additionalUploadedDocuments: current.additionalUploadedDocuments.filter(
|
|
460
|
+
(_item, itemIndex) => itemIndex !== index
|
|
461
|
+
),
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const numericId = Number(documentId);
|
|
466
|
+
if (!Number.isInteger(numericId) || numericId <= 0) {
|
|
467
|
+
return current;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
...current,
|
|
472
|
+
deletedDocumentIds: current.deletedDocumentIds.includes(numericId)
|
|
473
|
+
? current.deletedDocumentIds
|
|
474
|
+
: [...current.deletedDocumentIds, numericId],
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
|
|
383
479
|
const noAccessState = (
|
|
384
480
|
<EmptyState
|
|
385
481
|
icon={<FileText className="size-12" />}
|
|
@@ -480,82 +576,75 @@ export function ContractFormScreen({
|
|
|
480
576
|
<input
|
|
481
577
|
type="file"
|
|
482
578
|
className="hidden"
|
|
579
|
+
multiple
|
|
483
580
|
accept=".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
484
581
|
onChange={async (event) => {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const fileContentBase64 = await fileToBase64(file);
|
|
491
|
-
setForm((current) => ({
|
|
492
|
-
...current,
|
|
493
|
-
pdfDocument: {
|
|
494
|
-
fileName: file.name,
|
|
495
|
-
mimeType:
|
|
496
|
-
file.type ||
|
|
497
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
498
|
-
fileContentBase64,
|
|
499
|
-
},
|
|
500
|
-
}));
|
|
582
|
+
await handleFilesSelected(event.target.files);
|
|
583
|
+
event.target.value = '';
|
|
501
584
|
}}
|
|
502
585
|
/>
|
|
503
586
|
</label>
|
|
504
|
-
</div>
|
|
505
|
-
</div>
|
|
506
|
-
</SectionCard>
|
|
507
587
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
588
|
+
{downloadableDocuments.length ? (
|
|
589
|
+
<div className="space-y-2">
|
|
590
|
+
{downloadableDocuments.map((document) => {
|
|
591
|
+
const storedUrl =
|
|
592
|
+
'fileId' in document
|
|
593
|
+
? buildStoredFileUrl(document.fileId)
|
|
594
|
+
: null;
|
|
595
|
+
|
|
596
|
+
return (
|
|
597
|
+
<div
|
|
598
|
+
key={String(document.id)}
|
|
599
|
+
className="flex flex-col gap-3 rounded-lg border px-4 py-3 sm:flex-row sm:items-center sm:justify-between"
|
|
600
|
+
>
|
|
601
|
+
<div className="min-w-0">
|
|
602
|
+
<div className="truncate font-medium">
|
|
603
|
+
{document.fileName}
|
|
604
|
+
</div>
|
|
605
|
+
<div className="text-xs text-muted-foreground">
|
|
606
|
+
{document.mimeType}
|
|
607
|
+
</div>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div className="flex flex-wrap gap-2">
|
|
611
|
+
{storedUrl ? (
|
|
612
|
+
<Button type="button" variant="outline" asChild>
|
|
613
|
+
<a href={storedUrl} download>
|
|
614
|
+
<Download className="size-4" />
|
|
615
|
+
{detailT('actions.download')}
|
|
616
|
+
</a>
|
|
617
|
+
</Button>
|
|
618
|
+
) : document.fileContentBase64 ? (
|
|
619
|
+
<Button
|
|
620
|
+
type="button"
|
|
621
|
+
variant="outline"
|
|
622
|
+
onClick={() =>
|
|
623
|
+
downloadBase64File(
|
|
624
|
+
document.fileName,
|
|
625
|
+
document.mimeType,
|
|
626
|
+
document.fileContentBase64 || ''
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
>
|
|
630
|
+
<Download className="size-4" />
|
|
631
|
+
{detailT('actions.download')}
|
|
632
|
+
</Button>
|
|
633
|
+
) : null}
|
|
634
|
+
<Button
|
|
635
|
+
type="button"
|
|
636
|
+
variant="outline"
|
|
637
|
+
onClick={() => handleRemoveDocument(document.id)}
|
|
638
|
+
>
|
|
639
|
+
{commonT('actions.delete')}
|
|
640
|
+
</Button>
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
);
|
|
644
|
+
})}
|
|
645
|
+
</div>
|
|
646
|
+
) : null}
|
|
527
647
|
</div>
|
|
528
|
-
)}
|
|
529
|
-
|
|
530
|
-
<div className="mt-4 flex flex-wrap gap-2">
|
|
531
|
-
{currentDocument?.fileId ? (
|
|
532
|
-
<Button type="button" variant="outline" asChild>
|
|
533
|
-
<a
|
|
534
|
-
href={buildStoredFileUrl(currentDocument.fileId) || '#'}
|
|
535
|
-
target="_blank"
|
|
536
|
-
rel="noreferrer"
|
|
537
|
-
>
|
|
538
|
-
<Download className="size-4" />
|
|
539
|
-
{detailT('actions.downloadPdf')}
|
|
540
|
-
</a>
|
|
541
|
-
</Button>
|
|
542
|
-
) : null}
|
|
543
|
-
{currentDocument?.fileContentBase64 ? (
|
|
544
|
-
<Button
|
|
545
|
-
type="button"
|
|
546
|
-
variant="outline"
|
|
547
|
-
onClick={() =>
|
|
548
|
-
downloadBase64File(
|
|
549
|
-
currentDocument.fileName,
|
|
550
|
-
currentDocument.mimeType,
|
|
551
|
-
currentDocument.fileContentBase64 || ''
|
|
552
|
-
)
|
|
553
|
-
}
|
|
554
|
-
>
|
|
555
|
-
<Download className="size-4" />
|
|
556
|
-
{detailT('actions.downloadPdf')}
|
|
557
|
-
</Button>
|
|
558
|
-
) : null}
|
|
559
648
|
</div>
|
|
560
649
|
</SectionCard>
|
|
561
650
|
|