@spaceinvoices/react-ui 0.4.3 → 0.4.5
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/cli/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/common/autocomplete.tsx +18 -2
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +61 -12
- package/src/components/advance-invoices/create/locales/de.ts +17 -0
- package/src/components/advance-invoices/create/locales/es.ts +17 -0
- package/src/components/advance-invoices/create/locales/fr.ts +17 -0
- package/src/components/advance-invoices/create/locales/hr.ts +16 -0
- package/src/components/advance-invoices/create/locales/it.ts +17 -0
- package/src/components/advance-invoices/create/locales/nl.ts +17 -0
- package/src/components/advance-invoices/create/locales/pl.ts +16 -0
- package/src/components/advance-invoices/create/locales/pt.ts +16 -0
- package/src/components/advance-invoices/create/locales/sl.ts +16 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +0 -1
- package/src/components/advance-invoices/list/list-row-actions.tsx +48 -1
- package/src/components/advance-invoices/list/list-table.tsx +100 -50
- package/src/components/advance-invoices/list/locales/de.ts +9 -0
- package/src/components/advance-invoices/list/locales/en.ts +7 -0
- package/src/components/advance-invoices/list/locales/es.ts +9 -0
- package/src/components/advance-invoices/list/locales/fr.ts +9 -0
- package/src/components/advance-invoices/list/locales/hr.ts +8 -0
- package/src/components/advance-invoices/list/locales/it.ts +9 -0
- package/src/components/advance-invoices/list/locales/nl.ts +9 -0
- package/src/components/advance-invoices/list/locales/pl.ts +8 -0
- package/src/components/advance-invoices/list/locales/pt.ts +9 -0
- package/src/components/advance-invoices/list/locales/sl.ts +8 -0
- package/src/components/credit-notes/create/create-credit-note-form.tsx +185 -11
- package/src/components/credit-notes/create/locales/de.ts +18 -0
- package/src/components/credit-notes/create/locales/es.ts +18 -0
- package/src/components/credit-notes/create/locales/fr.ts +18 -0
- package/src/components/credit-notes/create/locales/hr.ts +17 -0
- package/src/components/credit-notes/create/locales/it.ts +18 -0
- package/src/components/credit-notes/create/locales/nl.ts +18 -0
- package/src/components/credit-notes/create/locales/pl.ts +17 -0
- package/src/components/credit-notes/create/locales/pt.ts +17 -0
- package/src/components/credit-notes/create/locales/sl.ts +18 -1
- package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
- package/src/components/credit-notes/list/list-row-actions.tsx +44 -1
- package/src/components/credit-notes/list/list-table.tsx +93 -32
- package/src/components/credit-notes/list/locales/de.ts +7 -0
- package/src/components/credit-notes/list/locales/en.ts +6 -0
- package/src/components/credit-notes/list/locales/es.ts +7 -0
- package/src/components/credit-notes/list/locales/fr.ts +7 -0
- package/src/components/credit-notes/list/locales/hr.ts +7 -0
- package/src/components/credit-notes/list/locales/it.ts +7 -0
- package/src/components/credit-notes/list/locales/nl.ts +7 -0
- package/src/components/credit-notes/list/locales/pl.ts +7 -0
- package/src/components/credit-notes/list/locales/pt.ts +7 -0
- package/src/components/credit-notes/list/locales/sl.ts +7 -0
- package/src/components/customers/customer-list-table/customer-list-table.tsx +0 -3
- package/src/components/customers/customer-list-table/locales/de.ts +1 -0
- package/src/components/customers/customer-list-table/locales/es.ts +1 -0
- package/src/components/customers/customer-list-table/locales/fr.ts +1 -0
- package/src/components/customers/customer-list-table/locales/hr.ts +1 -0
- package/src/components/customers/customer-list-table/locales/it.ts +1 -0
- package/src/components/customers/customer-list-table/locales/nl.ts +1 -0
- package/src/components/customers/customer-list-table/locales/pl.ts +1 -0
- package/src/components/customers/customer-list-table/locales/pt.ts +1 -0
- package/src/components/customers/customer-list-table/locales/sl.ts +1 -0
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -9
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +77 -48
- package/src/components/dashboard/shared/use-revenue-data.ts +77 -9
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +67 -7
- package/src/components/delivery-notes/create/locales/de.ts +16 -0
- package/src/components/delivery-notes/create/locales/es.ts +16 -0
- package/src/components/delivery-notes/create/locales/fr.ts +16 -0
- package/src/components/delivery-notes/create/locales/hr.ts +16 -0
- package/src/components/delivery-notes/create/locales/it.ts +16 -0
- package/src/components/delivery-notes/create/locales/nl.ts +16 -0
- package/src/components/delivery-notes/create/locales/pl.ts +16 -0
- package/src/components/delivery-notes/create/locales/pt.ts +16 -0
- package/src/components/delivery-notes/create/locales/sl.ts +17 -1
- package/src/components/delivery-notes/list/list-row-actions.tsx +20 -1
- package/src/components/delivery-notes/list/list-table.tsx +50 -8
- package/src/components/delivery-notes/list/locales/de.ts +35 -0
- package/src/components/delivery-notes/list/locales/en.ts +33 -0
- package/src/components/delivery-notes/list/locales/es.ts +35 -0
- package/src/components/delivery-notes/list/locales/fr.ts +35 -0
- package/src/components/delivery-notes/list/locales/hr.ts +35 -0
- package/src/components/delivery-notes/list/locales/it.ts +35 -0
- package/src/components/delivery-notes/list/locales/nl.ts +35 -0
- package/src/components/delivery-notes/list/locales/pl.ts +35 -0
- package/src/components/delivery-notes/list/locales/pt.ts +35 -0
- package/src/components/delivery-notes/list/locales/sl.ts +35 -0
- package/src/components/documents/create/document-add-item-form.tsx +70 -0
- package/src/components/documents/create/document-details-section.tsx +163 -29
- package/src/components/documents/create/document-items-section.tsx +21 -4
- package/src/components/documents/create/linked-documents-info.tsx +82 -0
- package/src/components/documents/create/live-preview.tsx +25 -5
- package/src/components/documents/create/mark-as-paid-section.tsx +29 -20
- package/src/components/documents/create/prepare-document-submission.ts +19 -7
- package/src/components/documents/shared/document-preview-display.tsx +3 -4
- package/src/components/documents/types.ts +2 -2
- package/src/components/documents/view/document-actions-bar.tsx +7 -27
- package/src/components/documents/view/document-activities-list.tsx +65 -47
- package/src/components/documents/view/document-details-card.tsx +118 -76
- package/src/components/documents/view/document-payments-list.tsx +99 -65
- package/src/components/documents/view/document-relations-list.tsx +43 -28
- package/src/components/documents/view/document-sidebar.tsx +151 -0
- package/src/components/documents/view/index.ts +2 -0
- package/src/components/documents/view/locales/de.ts +4 -0
- package/src/components/documents/view/locales/es.ts +4 -0
- package/src/components/documents/view/locales/fr.ts +4 -0
- package/src/components/documents/view/locales/hr.ts +4 -0
- package/src/components/documents/view/locales/it.ts +4 -0
- package/src/components/documents/view/locales/nl.ts +4 -0
- package/src/components/documents/view/locales/pl.ts +4 -0
- package/src/components/documents/view/locales/pt.ts +4 -0
- package/src/components/documents/view/locales/sl.ts +5 -1
- package/src/components/documents/view/use-document-download.ts +7 -3
- package/src/components/entities/create-entity-form.tsx +166 -13
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +101 -1
- package/src/components/entities/entity-settings-form/input-with-preview.tsx +30 -2
- package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +6 -6
- package/src/components/entities/fina-settings-form/locales/de.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/en.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/es.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/fr.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/hr.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/it.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/nl.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/pl.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/pt.ts +2 -2
- package/src/components/entities/fina-settings-form/locales/sl.ts +2 -2
- package/src/components/entities/furs-settings-form/locales/en.ts +0 -1
- package/src/components/entities/settings/company-settings-form.tsx +173 -20
- package/src/components/entities/settings/pdf-template-selector/locales/de.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/es.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/fr.ts +4 -1
- package/src/components/entities/settings/pdf-template-selector/locales/hr.ts +4 -1
- package/src/components/entities/settings/pdf-template-selector/locales/it.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/nl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/pl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/pt.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +3 -1
- package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +1 -0
- package/src/components/estimates/create/create-estimate-form.tsx +93 -8
- package/src/components/estimates/create/locales/de.ts +16 -0
- package/src/components/estimates/create/locales/es.ts +16 -0
- package/src/components/estimates/create/locales/fr.ts +16 -0
- package/src/components/estimates/create/locales/hr.ts +16 -0
- package/src/components/estimates/create/locales/it.ts +16 -0
- package/src/components/estimates/create/locales/nl.ts +16 -0
- package/src/components/estimates/create/locales/pl.ts +16 -0
- package/src/components/estimates/create/locales/pt.ts +16 -0
- package/src/components/estimates/create/locales/sl.ts +17 -1
- package/src/components/estimates/list/list-table.tsx +14 -8
- package/src/components/estimates/list/locales/de.ts +2 -0
- package/src/components/estimates/list/locales/en.ts +1 -0
- package/src/components/estimates/list/locales/es.ts +2 -0
- package/src/components/estimates/list/locales/fr.ts +2 -0
- package/src/components/estimates/list/locales/hr.ts +2 -0
- package/src/components/estimates/list/locales/it.ts +2 -0
- package/src/components/estimates/list/locales/nl.ts +2 -0
- package/src/components/estimates/list/locales/pl.ts +2 -0
- package/src/components/estimates/list/locales/pt.ts +2 -0
- package/src/components/estimates/list/locales/sl.ts +2 -0
- package/src/components/export/document-export-form.tsx +55 -14
- package/src/components/export/index.ts +2 -0
- package/src/components/export/sales-per-item-export-form.tsx +223 -0
- package/src/components/invoices/create/create-invoice-form.tsx +106 -11
- package/src/components/invoices/create/locales/de.ts +26 -0
- package/src/components/invoices/create/locales/es.ts +26 -0
- package/src/components/invoices/create/locales/fr.ts +26 -0
- package/src/components/invoices/create/locales/hr.ts +25 -0
- package/src/components/invoices/create/locales/it.ts +26 -0
- package/src/components/invoices/create/locales/nl.ts +26 -0
- package/src/components/invoices/create/locales/pl.ts +25 -0
- package/src/components/invoices/create/locales/pt.ts +25 -0
- package/src/components/invoices/create/locales/sl.ts +26 -1
- package/src/components/invoices/invoices-furs.hooks.ts +4 -1
- package/src/components/invoices/list/list-table.tsx +77 -31
- package/src/components/invoices/list/locales/de.ts +5 -0
- package/src/components/invoices/list/locales/en.ts +4 -0
- package/src/components/invoices/list/locales/es.ts +5 -0
- package/src/components/invoices/list/locales/fr.ts +5 -0
- package/src/components/invoices/list/locales/hr.ts +5 -0
- package/src/components/invoices/list/locales/it.ts +5 -0
- package/src/components/invoices/list/locales/nl.ts +5 -0
- package/src/components/invoices/list/locales/pl.ts +5 -0
- package/src/components/invoices/list/locales/pt.ts +5 -0
- package/src/components/invoices/list/locales/sl.ts +5 -0
- package/src/components/invoices/view/fiscalization-status-card.tsx +44 -24
- package/src/components/items/item-combobox.tsx +5 -3
- package/src/components/items/item-list-table/item-list-header.tsx +4 -17
- package/src/components/items/item-list-table/item-list-table.tsx +3 -3
- package/src/components/items/item-list-table/locales/de.ts +1 -0
- package/src/components/items/item-list-table/locales/es.ts +1 -0
- package/src/components/items/item-list-table/locales/fr.ts +1 -0
- package/src/components/items/item-list-table/locales/hr.ts +1 -0
- package/src/components/items/item-list-table/locales/it.ts +1 -0
- package/src/components/items/item-list-table/locales/nl.ts +1 -0
- package/src/components/items/item-list-table/locales/pl.ts +1 -0
- package/src/components/items/item-list-table/locales/pt.ts +1 -0
- package/src/components/items/item-list-table/locales/sl.ts +1 -0
- package/src/components/payments/list/list-table.tsx +0 -4
- package/src/components/payments/list/locales/de.ts +1 -0
- package/src/components/payments/list/locales/es.ts +1 -0
- package/src/components/payments/list/locales/fr.ts +1 -0
- package/src/components/payments/list/locales/hr.ts +1 -0
- package/src/components/payments/list/locales/it.ts +1 -0
- package/src/components/payments/list/locales/nl.ts +1 -0
- package/src/components/payments/list/locales/pl.ts +1 -0
- package/src/components/payments/list/locales/pt.ts +1 -0
- package/src/components/payments/list/locales/sl.ts +1 -0
- package/src/components/recurring-invoices/list/list-table.tsx +0 -7
- package/src/components/recurring-invoices/list/locales/de.ts +1 -0
- package/src/components/recurring-invoices/list/locales/es.ts +1 -0
- package/src/components/recurring-invoices/list/locales/fr.ts +1 -0
- package/src/components/recurring-invoices/list/locales/hr.ts +1 -0
- package/src/components/recurring-invoices/list/locales/it.ts +1 -0
- package/src/components/recurring-invoices/list/locales/nl.ts +1 -0
- package/src/components/recurring-invoices/list/locales/pl.ts +1 -0
- package/src/components/recurring-invoices/list/locales/pt.ts +1 -0
- package/src/components/recurring-invoices/list/locales/sl.ts +1 -0
- package/src/components/request-logs/request-log-list-table.tsx +0 -3
- package/src/components/table/README.md +14 -121
- package/src/components/table/data-table.tsx +22 -37
- package/src/components/table/hooks/use-table-state.ts +3 -27
- package/src/components/table/index.ts +0 -2
- package/src/components/table/search-input.tsx +17 -0
- package/src/components/table/selection-toolbar.tsx +35 -1
- package/src/components/table/table-empty-state.tsx +6 -3
- package/src/components/table/table-no-results.tsx +10 -5
- package/src/components/table/types.ts +0 -5
- package/src/components/taxes/tax-list-table/locales/de.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/es.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/fr.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/hr.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/it.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/nl.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/pl.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/pt.ts +1 -0
- package/src/components/taxes/tax-list-table/locales/sl.ts +1 -0
- package/src/components/taxes/tax-list-table/tax-list-header.tsx +3 -14
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -3
- package/src/components/ui/popover.tsx +3 -1
- package/src/components/ui/tooltip.tsx +3 -1
- package/src/generated/schemas/advanceinvoice.ts +4 -2
- package/src/generated/schemas/creditnote.ts +2 -1
- package/src/generated/schemas/deliverynote.ts +2 -1
- package/src/generated/schemas/estimate.ts +4 -2
- package/src/generated/schemas/index.ts +1 -1
- package/src/generated/schemas/invoice.ts +2 -1
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -23
- package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -23
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +17 -23
- package/src/generated/schemas/renderestimatepreview_body.ts +17 -23
- package/src/generated/schemas/renderinvoicepreview_body.ts +19 -23
- package/src/hooks/use-duplicate-document.ts +56 -14
- package/src/hooks/use-vies-check.ts +3 -0
- package/src/lib/fiscalization.ts +12 -0
- package/src/lib/schemas/advance-invoice.ts +0 -1
- package/src/components/table/sortable-header.tsx +0 -56
|
@@ -44,7 +44,8 @@ const translations = { sl, de, it, fr, es, pt, nl, pl, hr } as const;
|
|
|
44
44
|
|
|
45
45
|
type Document = Invoice | Estimate | CreditNote | AdvanceInvoice;
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
/** Language options for PDF label translations (formatting always uses entity locale) */
|
|
48
|
+
const PDF_LANGUAGE_CODES = [
|
|
48
49
|
{ label: "English", code: "en-US" },
|
|
49
50
|
{ label: "German", code: "de-DE" },
|
|
50
51
|
{ label: "Slovenian", code: "sl-SI" },
|
|
@@ -100,22 +101,6 @@ interface DocumentActionsBarProps extends ComponentTranslationProps {
|
|
|
100
101
|
isVoiding?: boolean;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
function getApiLocale(uiLanguage: string): string {
|
|
104
|
-
const localeMap: Record<string, string> = {
|
|
105
|
-
en: "en-US",
|
|
106
|
-
de: "de-DE",
|
|
107
|
-
sl: "sl-SI",
|
|
108
|
-
it: "it-IT",
|
|
109
|
-
fr: "fr-FR",
|
|
110
|
-
es: "es-ES",
|
|
111
|
-
pt: "pt-PT",
|
|
112
|
-
nl: "nl-NL",
|
|
113
|
-
pl: "pl-PL",
|
|
114
|
-
hr: "hr-HR",
|
|
115
|
-
};
|
|
116
|
-
return localeMap[uiLanguage] || "en-US";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
104
|
export function DocumentActionsBar({
|
|
120
105
|
document,
|
|
121
106
|
documentType,
|
|
@@ -163,7 +148,7 @@ export function DocumentActionsBar({
|
|
|
163
148
|
const shareableId = (document as Invoice).shareable_id;
|
|
164
149
|
const shareUrl = shareableId ? `${window.location.origin}/public/invoices/${shareableId}` : null;
|
|
165
150
|
|
|
166
|
-
const handleDownloadPdf = (
|
|
151
|
+
const handleDownloadPdf = (language?: string) => downloadPdf(document, documentType, language);
|
|
167
152
|
const handleDownloadEslog = () => downloadEslog(document, documentType);
|
|
168
153
|
|
|
169
154
|
const handleCopyShareLink = async () => {
|
|
@@ -178,7 +163,6 @@ export function DocumentActionsBar({
|
|
|
178
163
|
}
|
|
179
164
|
};
|
|
180
165
|
|
|
181
|
-
const apiLocale = getApiLocale(currentLocale);
|
|
182
166
|
const isDraft = (document as any).is_draft === true;
|
|
183
167
|
|
|
184
168
|
return (
|
|
@@ -189,7 +173,7 @@ export function DocumentActionsBar({
|
|
|
189
173
|
variant="outline"
|
|
190
174
|
size="sm"
|
|
191
175
|
disabled={isDownloadingPdf}
|
|
192
|
-
onClick={() => handleDownloadPdf(
|
|
176
|
+
onClick={() => handleDownloadPdf()}
|
|
193
177
|
className="cursor-pointer rounded-r-none"
|
|
194
178
|
>
|
|
195
179
|
{isDownloadingPdf ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Download className="mr-2 h-4 w-4" />}
|
|
@@ -207,13 +191,9 @@ export function DocumentActionsBar({
|
|
|
207
191
|
</Button>
|
|
208
192
|
</DropdownMenuTrigger>
|
|
209
193
|
<DropdownMenuContent align="end">
|
|
210
|
-
{
|
|
211
|
-
<DropdownMenuItem
|
|
212
|
-
|
|
213
|
-
onClick={() => handleDownloadPdf(locale.code)}
|
|
214
|
-
className="cursor-pointer"
|
|
215
|
-
>
|
|
216
|
-
{t(locale.label)}
|
|
194
|
+
{PDF_LANGUAGE_CODES.map((lang) => (
|
|
195
|
+
<DropdownMenuItem key={lang.code} onClick={() => handleDownloadPdf(lang.code)} className="cursor-pointer">
|
|
196
|
+
{t(lang.label)}
|
|
217
197
|
</DropdownMenuItem>
|
|
218
198
|
))}
|
|
219
199
|
</DropdownMenuContent>
|
|
@@ -27,6 +27,7 @@ interface DocumentActivitiesListProps extends ComponentTranslationProps {
|
|
|
27
27
|
entityId: string;
|
|
28
28
|
currentUserId?: string;
|
|
29
29
|
locale?: string;
|
|
30
|
+
variant?: "card" | "inline";
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
function formatActivityDate(date: string, locale: string): string {
|
|
@@ -67,6 +68,7 @@ export function DocumentActivitiesList({
|
|
|
67
68
|
entityId,
|
|
68
69
|
currentUserId,
|
|
69
70
|
locale = "en",
|
|
71
|
+
variant = "card",
|
|
70
72
|
...i18nProps
|
|
71
73
|
}: DocumentActivitiesListProps) {
|
|
72
74
|
const t = createTranslation({ translations, locale, ...i18nProps });
|
|
@@ -107,60 +109,76 @@ export function DocumentActivitiesList({
|
|
|
107
109
|
const hasPrev = cursors.length > 0;
|
|
108
110
|
const hasNext = !!pagination?.has_more;
|
|
109
111
|
|
|
112
|
+
const paginationButtons = (hasPrev || hasNext) && (
|
|
113
|
+
<div className="flex items-center gap-1">
|
|
114
|
+
<Button
|
|
115
|
+
variant="ghost"
|
|
116
|
+
size="sm"
|
|
117
|
+
onClick={handlePrevPage}
|
|
118
|
+
disabled={!hasPrev}
|
|
119
|
+
className="h-8 w-8 cursor-pointer p-0"
|
|
120
|
+
>
|
|
121
|
+
<ChevronLeft className="h-4 w-4" />
|
|
122
|
+
</Button>
|
|
123
|
+
<Button
|
|
124
|
+
variant="ghost"
|
|
125
|
+
size="sm"
|
|
126
|
+
onClick={handleNextPage}
|
|
127
|
+
disabled={!hasNext}
|
|
128
|
+
className="h-8 w-8 cursor-pointer p-0"
|
|
129
|
+
>
|
|
130
|
+
<ChevronRight className="h-4 w-4" />
|
|
131
|
+
</Button>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const bodyContent = isLoading ? (
|
|
136
|
+
<div className="space-y-2">
|
|
137
|
+
<Skeleton className="h-10 w-full" />
|
|
138
|
+
<Skeleton className="h-10 w-full" />
|
|
139
|
+
<Skeleton className="h-10 w-full" />
|
|
140
|
+
</div>
|
|
141
|
+
) : activities.length === 0 ? (
|
|
142
|
+
<p className="py-4 text-center text-muted-foreground text-sm">{t("No activity")}</p>
|
|
143
|
+
) : (
|
|
144
|
+
<div className="space-y-2">
|
|
145
|
+
{activities.map((activity) => (
|
|
146
|
+
<div key={activity.id} className="flex items-start justify-between rounded-md border p-3">
|
|
147
|
+
<div className="flex flex-col gap-0.5">
|
|
148
|
+
<span className="font-medium text-sm">{getActionLabel(activity.action, t)}</span>
|
|
149
|
+
<span className="text-muted-foreground text-xs">
|
|
150
|
+
{t("by")} {getActorLabel(activity, t, currentUserId)}
|
|
151
|
+
</span>
|
|
152
|
+
</div>
|
|
153
|
+
<span className="text-muted-foreground text-xs">{formatActivityDate(activity.created_at, locale)}</span>
|
|
154
|
+
</div>
|
|
155
|
+
))}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (variant === "inline") {
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
<div className="mb-3 flex items-center justify-between">
|
|
163
|
+
<h3 className="font-medium text-sm">
|
|
164
|
+
{t("Activity")} {pagination && pagination.total > 0 && `(${pagination.total})`}
|
|
165
|
+
</h3>
|
|
166
|
+
{paginationButtons}
|
|
167
|
+
</div>
|
|
168
|
+
{bodyContent}
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
110
173
|
return (
|
|
111
174
|
<Card>
|
|
112
175
|
<CardHeader className="flex flex-row items-center justify-between pb-3">
|
|
113
176
|
<CardTitle className="text-lg">
|
|
114
177
|
{t("Activity")} {pagination && pagination.total > 0 && `(${pagination.total})`}
|
|
115
178
|
</CardTitle>
|
|
116
|
-
{
|
|
117
|
-
<div className="flex items-center gap-1">
|
|
118
|
-
<Button
|
|
119
|
-
variant="ghost"
|
|
120
|
-
size="sm"
|
|
121
|
-
onClick={handlePrevPage}
|
|
122
|
-
disabled={!hasPrev}
|
|
123
|
-
className="h-8 w-8 cursor-pointer p-0"
|
|
124
|
-
>
|
|
125
|
-
<ChevronLeft className="h-4 w-4" />
|
|
126
|
-
</Button>
|
|
127
|
-
<Button
|
|
128
|
-
variant="ghost"
|
|
129
|
-
size="sm"
|
|
130
|
-
onClick={handleNextPage}
|
|
131
|
-
disabled={!hasNext}
|
|
132
|
-
className="h-8 w-8 cursor-pointer p-0"
|
|
133
|
-
>
|
|
134
|
-
<ChevronRight className="h-4 w-4" />
|
|
135
|
-
</Button>
|
|
136
|
-
</div>
|
|
137
|
-
)}
|
|
179
|
+
{paginationButtons}
|
|
138
180
|
</CardHeader>
|
|
139
|
-
<CardContent>
|
|
140
|
-
{isLoading ? (
|
|
141
|
-
<div className="space-y-2">
|
|
142
|
-
<Skeleton className="h-10 w-full" />
|
|
143
|
-
<Skeleton className="h-10 w-full" />
|
|
144
|
-
<Skeleton className="h-10 w-full" />
|
|
145
|
-
</div>
|
|
146
|
-
) : activities.length === 0 ? (
|
|
147
|
-
<p className="py-4 text-center text-muted-foreground text-sm">{t("No activity")}</p>
|
|
148
|
-
) : (
|
|
149
|
-
<div className="space-y-2">
|
|
150
|
-
{activities.map((activity) => (
|
|
151
|
-
<div key={activity.id} className="flex items-start justify-between rounded-md border p-3">
|
|
152
|
-
<div className="flex flex-col gap-0.5">
|
|
153
|
-
<span className="font-medium text-sm">{getActionLabel(activity.action, t)}</span>
|
|
154
|
-
<span className="text-muted-foreground text-xs">
|
|
155
|
-
{t("by")} {getActorLabel(activity, t, currentUserId)}
|
|
156
|
-
</span>
|
|
157
|
-
</div>
|
|
158
|
-
<span className="text-muted-foreground text-xs">{formatActivityDate(activity.created_at, locale)}</span>
|
|
159
|
-
</div>
|
|
160
|
-
))}
|
|
161
|
-
</div>
|
|
162
|
-
)}
|
|
163
|
-
</CardContent>
|
|
181
|
+
<CardContent>{bodyContent}</CardContent>
|
|
164
182
|
</Card>
|
|
165
183
|
);
|
|
166
184
|
}
|
|
@@ -25,6 +25,7 @@ interface DocumentDetailsCardProps extends ComponentTranslationProps {
|
|
|
25
25
|
documentType: DocumentType;
|
|
26
26
|
/** Locale for date formatting */
|
|
27
27
|
locale?: string;
|
|
28
|
+
variant?: "card" | "inline";
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -79,10 +80,17 @@ function getPaymentStatus(
|
|
|
79
80
|
* - Totals breakdown
|
|
80
81
|
* - Payment status (for invoices/advance invoices)
|
|
81
82
|
*/
|
|
82
|
-
export function DocumentDetailsCard({
|
|
83
|
+
export function DocumentDetailsCard({
|
|
84
|
+
document,
|
|
85
|
+
documentType,
|
|
86
|
+
locale = "en",
|
|
87
|
+
variant = "card",
|
|
88
|
+
...i18nProps
|
|
89
|
+
}: DocumentDetailsCardProps) {
|
|
83
90
|
const t = createTranslation({ translations, locale, ...i18nProps });
|
|
84
91
|
|
|
85
92
|
const currencyCode = document.currency_code;
|
|
93
|
+
const sign = documentType === "credit_note" ? -1 : 1;
|
|
86
94
|
const fmt = (amount: number) => formatCurrency(amount, currencyCode, locale);
|
|
87
95
|
const fmtDate = (date: Date | string | null | undefined) => formatDate(date, locale);
|
|
88
96
|
|
|
@@ -95,8 +103,114 @@ export function DocumentDetailsCard({ document, documentType, locale = "en", ...
|
|
|
95
103
|
// Get customer name
|
|
96
104
|
const customerName = document.customer?.name || "-";
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
const bodyContent = (
|
|
107
|
+
<>
|
|
108
|
+
{/* Document info */}
|
|
109
|
+
<div className="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
|
|
110
|
+
<div className="text-muted-foreground">{t("Number")}</div>
|
|
111
|
+
<div className="text-right font-medium">{document.number}</div>
|
|
112
|
+
|
|
113
|
+
<div className="text-muted-foreground">{t("Date")}</div>
|
|
114
|
+
<div className="text-right">{fmtDate(document.date)}</div>
|
|
115
|
+
|
|
116
|
+
{isInvoiceOrAdvance && invoiceDoc.date_due && (
|
|
117
|
+
<>
|
|
118
|
+
<div className="text-muted-foreground">{t("Due date")}</div>
|
|
119
|
+
<div className="text-right">{fmtDate(invoiceDoc.date_due)}</div>
|
|
120
|
+
</>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{isInvoiceOrAdvance && (invoiceDoc as any).date_service && (
|
|
124
|
+
<>
|
|
125
|
+
<div className="text-muted-foreground">
|
|
126
|
+
{(invoiceDoc as any).date_service_to ? t("Service period") : t("Service date")}
|
|
127
|
+
</div>
|
|
128
|
+
<div className="text-right">
|
|
129
|
+
{(invoiceDoc as any).date_service_to
|
|
130
|
+
? `${fmtDate((invoiceDoc as any).date_service)} - ${fmtDate((invoiceDoc as any).date_service_to)}`
|
|
131
|
+
: fmtDate((invoiceDoc as any).date_service)}
|
|
132
|
+
</div>
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
|
|
136
|
+
{isEstimate && estimateDoc.date_valid_till && (
|
|
137
|
+
<>
|
|
138
|
+
<div className="text-muted-foreground">{t("Valid until")}</div>
|
|
139
|
+
<div className="text-right">{fmtDate(estimateDoc.date_valid_till)}</div>
|
|
140
|
+
</>
|
|
141
|
+
)}
|
|
142
|
+
|
|
143
|
+
<div className="text-muted-foreground">{t("Customer")}</div>
|
|
144
|
+
<div className="text-right">{customerName}</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<Separator />
|
|
148
|
+
|
|
149
|
+
{/* Totals */}
|
|
150
|
+
<div className="space-y-2 text-sm">
|
|
151
|
+
<div className="flex justify-between">
|
|
152
|
+
<span className="text-muted-foreground">{t("Subtotal")}</span>
|
|
153
|
+
<span>{fmt(document.total * sign)}</span>
|
|
154
|
+
</div>
|
|
155
|
+
{document.taxes && document.taxes.length > 0 ? (
|
|
156
|
+
document.taxes.map((tax: Document["taxes"][number]) => {
|
|
157
|
+
const taxAmount = tax.amount ?? 0;
|
|
158
|
+
return (
|
|
159
|
+
<div key={String(tax.rate)} className="flex justify-between">
|
|
160
|
+
<span className="text-muted-foreground">
|
|
161
|
+
{t("Tax")} {tax.rate ?? 0}%{" "}
|
|
162
|
+
{tax.base != null && (
|
|
163
|
+
<span className="text-xs">
|
|
164
|
+
{t("of")} {fmt(tax.base * sign)}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
</span>
|
|
168
|
+
<span>{fmt(taxAmount * sign)}</span>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
})
|
|
172
|
+
) : (
|
|
173
|
+
<div className="flex justify-between">
|
|
174
|
+
<span className="text-muted-foreground">{t("Tax")}</span>
|
|
175
|
+
<span>{fmt(0)}</span>
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
<div className="flex justify-between font-semibold">
|
|
179
|
+
<span>{t("Total")}</span>
|
|
180
|
+
<span>{fmt(document.total_with_tax * sign)}</span>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{/* Payment info for invoices/advance invoices */}
|
|
184
|
+
{isInvoiceOrAdvance && invoiceDoc.total_paid > 0 && (
|
|
185
|
+
<>
|
|
186
|
+
<Separator />
|
|
187
|
+
<div className="flex justify-between text-green-600">
|
|
188
|
+
<span>{t("Paid")}</span>
|
|
189
|
+
<span>-{fmt(invoiceDoc.total_paid)}</span>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="flex justify-between font-semibold">
|
|
192
|
+
<span>{t("Due")}</span>
|
|
193
|
+
<span>{fmt(invoiceDoc.total_due)}</span>
|
|
194
|
+
</div>
|
|
195
|
+
</>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</>
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (variant === "inline") {
|
|
202
|
+
return (
|
|
203
|
+
<div className="space-y-3">
|
|
204
|
+
<div className="flex items-center justify-between font-medium text-sm">
|
|
205
|
+
{t("Details")}
|
|
206
|
+
{isInvoiceOrAdvance && (
|
|
207
|
+
<Badge variant={getPaymentStatus(invoiceDoc, t).variant}>{getPaymentStatus(invoiceDoc, t).label}</Badge>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
<div className="space-y-4">{bodyContent}</div>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
100
214
|
|
|
101
215
|
return (
|
|
102
216
|
<Card>
|
|
@@ -108,79 +222,7 @@ export function DocumentDetailsCard({ document, documentType, locale = "en", ...
|
|
|
108
222
|
)}
|
|
109
223
|
</CardTitle>
|
|
110
224
|
</CardHeader>
|
|
111
|
-
<CardContent className="space-y-4">
|
|
112
|
-
{/* Document info */}
|
|
113
|
-
<div className="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
|
|
114
|
-
<div className="text-muted-foreground">{t("Number")}</div>
|
|
115
|
-
<div className="text-right font-medium">{document.number}</div>
|
|
116
|
-
|
|
117
|
-
<div className="text-muted-foreground">{t("Date")}</div>
|
|
118
|
-
<div className="text-right">{fmtDate(document.date)}</div>
|
|
119
|
-
|
|
120
|
-
{isInvoiceOrAdvance && (
|
|
121
|
-
<>
|
|
122
|
-
<div className="text-muted-foreground">{t("Due date")}</div>
|
|
123
|
-
<div className="text-right">{fmtDate(invoiceDoc.date_due)}</div>
|
|
124
|
-
</>
|
|
125
|
-
)}
|
|
126
|
-
|
|
127
|
-
{isInvoiceOrAdvance && (invoiceDoc as any).date_service && (
|
|
128
|
-
<>
|
|
129
|
-
<div className="text-muted-foreground">
|
|
130
|
-
{(invoiceDoc as any).date_service_to ? t("Service period") : t("Service date")}
|
|
131
|
-
</div>
|
|
132
|
-
<div className="text-right">
|
|
133
|
-
{(invoiceDoc as any).date_service_to
|
|
134
|
-
? `${fmtDate((invoiceDoc as any).date_service)} - ${fmtDate((invoiceDoc as any).date_service_to)}`
|
|
135
|
-
: fmtDate((invoiceDoc as any).date_service)}
|
|
136
|
-
</div>
|
|
137
|
-
</>
|
|
138
|
-
)}
|
|
139
|
-
|
|
140
|
-
{isEstimate && estimateDoc.date_valid_till && (
|
|
141
|
-
<>
|
|
142
|
-
<div className="text-muted-foreground">{t("Valid until")}</div>
|
|
143
|
-
<div className="text-right">{fmtDate(estimateDoc.date_valid_till)}</div>
|
|
144
|
-
</>
|
|
145
|
-
)}
|
|
146
|
-
|
|
147
|
-
<div className="text-muted-foreground">{t("Customer")}</div>
|
|
148
|
-
<div className="text-right">{customerName}</div>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
<Separator />
|
|
152
|
-
|
|
153
|
-
{/* Totals */}
|
|
154
|
-
<div className="space-y-2 text-sm">
|
|
155
|
-
<div className="flex justify-between">
|
|
156
|
-
<span className="text-muted-foreground">{t("Subtotal")}</span>
|
|
157
|
-
<span>{fmt(document.total)}</span>
|
|
158
|
-
</div>
|
|
159
|
-
<div className="flex justify-between">
|
|
160
|
-
<span className="text-muted-foreground">{t("Tax")}</span>
|
|
161
|
-
<span>{fmt(taxTotal)}</span>
|
|
162
|
-
</div>
|
|
163
|
-
<div className="flex justify-between font-semibold">
|
|
164
|
-
<span>{t("Total")}</span>
|
|
165
|
-
<span>{fmt(document.total_with_tax)}</span>
|
|
166
|
-
</div>
|
|
167
|
-
|
|
168
|
-
{/* Payment info for invoices/advance invoices */}
|
|
169
|
-
{isInvoiceOrAdvance && invoiceDoc.total_paid > 0 && (
|
|
170
|
-
<>
|
|
171
|
-
<Separator />
|
|
172
|
-
<div className="flex justify-between text-green-600">
|
|
173
|
-
<span>{t("Paid")}</span>
|
|
174
|
-
<span>-{fmt(invoiceDoc.total_paid)}</span>
|
|
175
|
-
</div>
|
|
176
|
-
<div className="flex justify-between font-semibold">
|
|
177
|
-
<span>{t("Due")}</span>
|
|
178
|
-
<span>{fmt(invoiceDoc.total_due)}</span>
|
|
179
|
-
</div>
|
|
180
|
-
</>
|
|
181
|
-
)}
|
|
182
|
-
</div>
|
|
183
|
-
</CardContent>
|
|
225
|
+
<CardContent className="space-y-4">{bodyContent}</CardContent>
|
|
184
226
|
</Card>
|
|
185
227
|
);
|
|
186
228
|
}
|
|
@@ -51,6 +51,7 @@ interface DocumentPaymentsListProps extends ComponentTranslationProps {
|
|
|
51
51
|
onDeleteSuccess?: () => void;
|
|
52
52
|
/** Callback on delete error */
|
|
53
53
|
onDeleteError?: (error: string) => void;
|
|
54
|
+
variant?: "card" | "inline";
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
@@ -94,6 +95,7 @@ export function DocumentPaymentsList({
|
|
|
94
95
|
onEditPayment,
|
|
95
96
|
onDeleteSuccess,
|
|
96
97
|
onDeleteError,
|
|
98
|
+
variant = "card",
|
|
97
99
|
...i18nProps
|
|
98
100
|
}: DocumentPaymentsListProps) {
|
|
99
101
|
const t = createTranslation({ translations, locale, ...i18nProps });
|
|
@@ -180,6 +182,101 @@ export function DocumentPaymentsList({
|
|
|
180
182
|
const fmt = (amount: number) => formatCurrency(amount, currencyCode, locale);
|
|
181
183
|
const fmtDate = (date: Date | string | null) => formatDate(date, locale);
|
|
182
184
|
|
|
185
|
+
const headerContent = (
|
|
186
|
+
<div
|
|
187
|
+
className={
|
|
188
|
+
variant === "inline" ? "flex items-center justify-between" : "flex flex-row items-center justify-between"
|
|
189
|
+
}
|
|
190
|
+
>
|
|
191
|
+
<h3
|
|
192
|
+
className={variant === "inline" ? "font-medium text-sm" : "font-semibold text-lg leading-none tracking-tight"}
|
|
193
|
+
>
|
|
194
|
+
{t("Payments")} {payments.length > 0 && `(${payments.length})`}
|
|
195
|
+
</h3>
|
|
196
|
+
<Button variant="outline" size="sm" onClick={onAddPayment} className="cursor-pointer">
|
|
197
|
+
<Plus className="mr-1 h-4 w-4" />
|
|
198
|
+
{t("Add payment")}
|
|
199
|
+
</Button>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const bodyContent = isLoading ? (
|
|
204
|
+
<div className="space-y-2">
|
|
205
|
+
<Skeleton className="h-10 w-full" />
|
|
206
|
+
<Skeleton className="h-10 w-full" />
|
|
207
|
+
</div>
|
|
208
|
+
) : payments.length === 0 ? (
|
|
209
|
+
<p className="py-4 text-center text-muted-foreground text-sm">{t("No payments")}</p>
|
|
210
|
+
) : (
|
|
211
|
+
<div className="space-y-2">
|
|
212
|
+
{payments.map((payment) => (
|
|
213
|
+
<div key={payment.id} className="flex items-center justify-between rounded-md border p-3">
|
|
214
|
+
<div className="flex items-center gap-4">
|
|
215
|
+
<span className="text-muted-foreground text-sm">{fmtDate(payment.date)}</span>
|
|
216
|
+
<span className="font-medium">{fmt(payment.amount)}</span>
|
|
217
|
+
<span className="text-muted-foreground text-sm">{getTypeLabel(payment.type)}</span>
|
|
218
|
+
</div>
|
|
219
|
+
<DropdownMenu>
|
|
220
|
+
<DropdownMenuTrigger asChild>
|
|
221
|
+
<Button variant="ghost" size="sm" className="h-8 w-8 cursor-pointer p-0">
|
|
222
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
223
|
+
</Button>
|
|
224
|
+
</DropdownMenuTrigger>
|
|
225
|
+
<DropdownMenuContent align="end">
|
|
226
|
+
<DropdownMenuItem onClick={() => onEditPayment?.(payment)} className="cursor-pointer">
|
|
227
|
+
<Pencil className="mr-2 h-4 w-4" />
|
|
228
|
+
{t("Edit")}
|
|
229
|
+
</DropdownMenuItem>
|
|
230
|
+
<DropdownMenuItem
|
|
231
|
+
onClick={() => setPaymentToDelete(payment)}
|
|
232
|
+
className="cursor-pointer text-destructive focus:text-destructive"
|
|
233
|
+
>
|
|
234
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
235
|
+
{t("Delete")}
|
|
236
|
+
</DropdownMenuItem>
|
|
237
|
+
</DropdownMenuContent>
|
|
238
|
+
</DropdownMenu>
|
|
239
|
+
</div>
|
|
240
|
+
))}
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const deleteDialog = (
|
|
245
|
+
<Dialog open={!!paymentToDelete} onOpenChange={() => setPaymentToDelete(null)}>
|
|
246
|
+
<DialogContent>
|
|
247
|
+
<DialogHeader>
|
|
248
|
+
<DialogTitle>{t("Delete payment")}</DialogTitle>
|
|
249
|
+
<DialogDescription>{t("Delete payment confirmation")}</DialogDescription>
|
|
250
|
+
</DialogHeader>
|
|
251
|
+
<DialogFooter>
|
|
252
|
+
<Button
|
|
253
|
+
variant="outline"
|
|
254
|
+
disabled={isDeleting}
|
|
255
|
+
onClick={() => setPaymentToDelete(null)}
|
|
256
|
+
className="cursor-pointer"
|
|
257
|
+
>
|
|
258
|
+
{t("Cancel")}
|
|
259
|
+
</Button>
|
|
260
|
+
<Button variant="destructive" onClick={handleDelete} disabled={isDeleting} className="cursor-pointer">
|
|
261
|
+
{t("Delete")}
|
|
262
|
+
</Button>
|
|
263
|
+
</DialogFooter>
|
|
264
|
+
</DialogContent>
|
|
265
|
+
</Dialog>
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (variant === "inline") {
|
|
269
|
+
return (
|
|
270
|
+
<>
|
|
271
|
+
<div>
|
|
272
|
+
{headerContent}
|
|
273
|
+
<div className="mt-3">{bodyContent}</div>
|
|
274
|
+
</div>
|
|
275
|
+
{deleteDialog}
|
|
276
|
+
</>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
183
280
|
return (
|
|
184
281
|
<>
|
|
185
282
|
<Card>
|
|
@@ -192,72 +289,9 @@ export function DocumentPaymentsList({
|
|
|
192
289
|
{t("Add payment")}
|
|
193
290
|
</Button>
|
|
194
291
|
</CardHeader>
|
|
195
|
-
<CardContent>
|
|
196
|
-
{isLoading ? (
|
|
197
|
-
<div className="space-y-2">
|
|
198
|
-
<Skeleton className="h-10 w-full" />
|
|
199
|
-
<Skeleton className="h-10 w-full" />
|
|
200
|
-
</div>
|
|
201
|
-
) : payments.length === 0 ? (
|
|
202
|
-
<p className="py-4 text-center text-muted-foreground text-sm">{t("No payments")}</p>
|
|
203
|
-
) : (
|
|
204
|
-
<div className="space-y-2">
|
|
205
|
-
{payments.map((payment) => (
|
|
206
|
-
<div key={payment.id} className="flex items-center justify-between rounded-md border p-3">
|
|
207
|
-
<div className="flex items-center gap-4">
|
|
208
|
-
<span className="text-muted-foreground text-sm">{fmtDate(payment.date)}</span>
|
|
209
|
-
<span className="font-medium">{fmt(payment.amount)}</span>
|
|
210
|
-
<span className="text-muted-foreground text-sm">{getTypeLabel(payment.type)}</span>
|
|
211
|
-
</div>
|
|
212
|
-
<DropdownMenu>
|
|
213
|
-
<DropdownMenuTrigger asChild>
|
|
214
|
-
<Button variant="ghost" size="sm" className="h-8 w-8 cursor-pointer p-0">
|
|
215
|
-
<MoreHorizontal className="h-4 w-4" />
|
|
216
|
-
</Button>
|
|
217
|
-
</DropdownMenuTrigger>
|
|
218
|
-
<DropdownMenuContent align="end">
|
|
219
|
-
<DropdownMenuItem onClick={() => onEditPayment?.(payment)} className="cursor-pointer">
|
|
220
|
-
<Pencil className="mr-2 h-4 w-4" />
|
|
221
|
-
{t("Edit")}
|
|
222
|
-
</DropdownMenuItem>
|
|
223
|
-
<DropdownMenuItem
|
|
224
|
-
onClick={() => setPaymentToDelete(payment)}
|
|
225
|
-
className="cursor-pointer text-destructive focus:text-destructive"
|
|
226
|
-
>
|
|
227
|
-
<Trash2 className="mr-2 h-4 w-4" />
|
|
228
|
-
{t("Delete")}
|
|
229
|
-
</DropdownMenuItem>
|
|
230
|
-
</DropdownMenuContent>
|
|
231
|
-
</DropdownMenu>
|
|
232
|
-
</div>
|
|
233
|
-
))}
|
|
234
|
-
</div>
|
|
235
|
-
)}
|
|
236
|
-
</CardContent>
|
|
292
|
+
<CardContent>{bodyContent}</CardContent>
|
|
237
293
|
</Card>
|
|
238
|
-
|
|
239
|
-
{/* Delete Confirmation Dialog */}
|
|
240
|
-
<Dialog open={!!paymentToDelete} onOpenChange={() => setPaymentToDelete(null)}>
|
|
241
|
-
<DialogContent>
|
|
242
|
-
<DialogHeader>
|
|
243
|
-
<DialogTitle>{t("Delete payment")}</DialogTitle>
|
|
244
|
-
<DialogDescription>{t("Delete payment confirmation")}</DialogDescription>
|
|
245
|
-
</DialogHeader>
|
|
246
|
-
<DialogFooter>
|
|
247
|
-
<Button
|
|
248
|
-
variant="outline"
|
|
249
|
-
disabled={isDeleting}
|
|
250
|
-
onClick={() => setPaymentToDelete(null)}
|
|
251
|
-
className="cursor-pointer"
|
|
252
|
-
>
|
|
253
|
-
{t("Cancel")}
|
|
254
|
-
</Button>
|
|
255
|
-
<Button variant="destructive" onClick={handleDelete} disabled={isDeleting} className="cursor-pointer">
|
|
256
|
-
{t("Delete")}
|
|
257
|
-
</Button>
|
|
258
|
-
</DialogFooter>
|
|
259
|
-
</DialogContent>
|
|
260
|
-
</Dialog>
|
|
294
|
+
{deleteDialog}
|
|
261
295
|
</>
|
|
262
296
|
);
|
|
263
297
|
}
|