@spaceinvoices/react-ui 0.4.8 → 0.4.10
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 +69 -6
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +124 -285
- package/src/components/advance-invoices/list/list-table.tsx +10 -3
- package/src/components/advance-invoices/list/locales/de.ts +2 -0
- package/src/components/advance-invoices/list/locales/en.ts +1 -0
- package/src/components/advance-invoices/list/locales/es.ts +1 -0
- package/src/components/advance-invoices/list/locales/fr.ts +1 -0
- package/src/components/advance-invoices/list/locales/hr.ts +1 -0
- package/src/components/advance-invoices/list/locales/it.ts +1 -0
- package/src/components/advance-invoices/list/locales/nl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pt.ts +1 -0
- package/src/components/advance-invoices/list/locales/sl.ts +1 -0
- package/src/components/advance-invoices/list/use-advance-invoice-download.ts +1 -12
- package/src/components/credit-notes/create/create-credit-note-form.tsx +116 -238
- package/src/components/credit-notes/list/list-table.tsx +6 -3
- package/src/components/credit-notes/list/use-credit-note-download.ts +1 -12
- package/src/components/customers/customer-autocomplete.tsx +64 -11
- package/src/components/customers/customer-list-table/customer-list-table.tsx +3 -2
- package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +9 -1
- package/src/components/dashboard/collection-rate-card/locales/bg.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/cs.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/et.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/fi.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/is.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/nb.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/sk.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/sv.ts +3 -0
- package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +10 -2
- package/src/components/dashboard/invoice-status-chart/locales/bg.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/cs.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/de.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/es.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/et.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/fi.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/is.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/it.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/nb.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/sk.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/sv.ts +10 -0
- package/src/components/dashboard/payment-methods-chart/locales/bg.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/cs.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/et.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/fi.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/is.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/nb.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/sk.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/sv.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +9 -1
- package/src/components/dashboard/payment-trend-chart/locales/bg.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/cs.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/de.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/es.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/et.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/fi.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/is.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/it.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/nb.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/sk.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/sv.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +15 -8
- package/src/components/dashboard/revenue-trend-chart/locales/bg.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/cs.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/de.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/es.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/et.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/fi.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/is.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/it.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/nb.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sk.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sv.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +15 -8
- package/src/components/dashboard/tax-collected-card/locales.ts +110 -0
- package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +8 -2
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +4 -4
- package/src/components/dashboard/top-customers-chart/locales/bg.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/cs.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/de.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/es.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/et.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/fi.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/fr.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/hr.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/is.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/it.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/nb.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/nl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/pl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/pt.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/sk.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/sl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/sv.ts +7 -0
- package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +23 -12
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +33 -20
- package/src/components/delivery-notes/list/list-table.tsx +22 -13
- package/src/components/delivery-notes/list/locales/de.ts +2 -0
- package/src/components/delivery-notes/list/locales/en.ts +1 -0
- package/src/components/delivery-notes/list/locales/es.ts +1 -0
- package/src/components/delivery-notes/list/locales/fr.ts +1 -0
- package/src/components/delivery-notes/list/locales/hr.ts +1 -0
- package/src/components/delivery-notes/list/locales/it.ts +1 -0
- package/src/components/delivery-notes/list/locales/nl.ts +1 -0
- package/src/components/delivery-notes/list/locales/pl.ts +1 -0
- package/src/components/delivery-notes/list/locales/pt.ts +1 -0
- package/src/components/delivery-notes/list/locales/sl.ts +1 -0
- package/src/components/delivery-notes/list/use-delivery-note-download.ts +1 -12
- package/src/components/documents/create/document-add-item-form.tsx +28 -16
- package/src/components/documents/create/document-add-item-tax-rate-field.tsx +12 -2
- package/src/components/documents/create/document-items-section.tsx +70 -39
- package/src/components/documents/create/document-recipient-section.tsx +10 -1
- package/src/components/documents/create/live-preview.tsx +113 -15
- package/src/components/documents/create/prepare-document-submission.ts +35 -16
- package/src/components/documents/create/use-document-customer-form.ts +14 -3
- package/src/components/documents/documents.hooks.ts +7 -2
- package/src/components/documents/shared/document-preview-display.tsx +136 -67
- package/src/components/documents/shared/scaled-document-preview.tsx +45 -5
- package/src/components/documents/view/document-actions-bar.tsx +284 -182
- package/src/components/documents/view/document-activities-list.tsx +3 -0
- package/src/components/documents/view/document-payments-list.tsx +3 -0
- package/src/components/documents/view/locales/de.ts +8 -0
- package/src/components/documents/view/locales/es.ts +8 -0
- package/src/components/documents/view/locales/fr.ts +8 -0
- package/src/components/documents/view/locales/hr.ts +8 -0
- package/src/components/documents/view/locales/it.ts +8 -0
- package/src/components/documents/view/locales/nl.ts +8 -0
- package/src/components/documents/view/locales/pl.ts +8 -0
- package/src/components/documents/view/locales/pt.ts +8 -0
- package/src/components/documents/view/locales/sl.ts +8 -0
- package/src/components/documents/view/use-document-download.ts +14 -25
- package/src/components/entities/create-entity-form.tsx +101 -16
- package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +3 -3
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +78 -124
- package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +8 -1
- package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +14 -2
- package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +7 -2
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +56 -130
- package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +8 -1
- package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +1 -0
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
- package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +20 -3
- package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +38 -12
- package/src/components/entities/settings/eslog-settings-form.tsx +13 -1
- package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +3 -22
- package/src/components/entities/shared/fiscalization-step-flow.ts +77 -0
- package/src/components/entities/shared/fiscalization-step-tabs.tsx +71 -0
- package/src/components/estimates/create/create-estimate-form.tsx +34 -21
- package/src/components/estimates/list/list-table.tsx +23 -14
- 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 +1 -0
- package/src/components/estimates/list/locales/fr.ts +1 -0
- package/src/components/estimates/list/locales/hr.ts +1 -0
- package/src/components/estimates/list/locales/it.ts +1 -0
- package/src/components/estimates/list/locales/nl.ts +1 -0
- package/src/components/estimates/list/locales/pl.ts +1 -0
- package/src/components/estimates/list/locales/pt.ts +1 -0
- package/src/components/estimates/list/locales/sl.ts +1 -0
- package/src/components/estimates/list/use-estimate-download.ts +1 -12
- package/src/components/export/document-export-form.tsx +33 -7
- package/src/components/export/sales-per-item-export-form.tsx +23 -7
- package/src/components/invoices/create/create-invoice-form.tsx +295 -329
- package/src/components/invoices/create/prepare-invoice-submission.ts +0 -8
- package/src/components/invoices/list/list-table.tsx +7 -4
- package/src/components/invoices/list/use-invoice-download.ts +1 -11
- package/src/components/invoices/send-email-dialog/locales/de.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/es.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/fr.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/hr.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/it.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/nl.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/pl.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/pt.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/sl.ts +2 -0
- package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +77 -8
- package/src/components/invoices/view/eslog-info-display.tsx +17 -1
- package/src/components/invoices/view/fiscalization-status-card.tsx +7 -3
- package/src/components/items/item-combobox.tsx +26 -6
- package/src/components/items/item-list-table/item-list-table.tsx +5 -2
- package/src/components/payments/list/list-table.tsx +14 -4
- package/src/components/recurring-invoices/list/list-table.tsx +7 -4
- package/src/components/request-logs/locales.ts +412 -0
- package/src/components/request-logs/request-log-detail.tsx +37 -21
- package/src/components/request-logs/request-log-list-table.tsx +57 -11
- package/src/components/table/data-table.tsx +5 -2
- package/src/components/table/date-cell.tsx +3 -1
- package/src/components/table/filter-bar.tsx +14 -2
- package/src/components/table/hooks/use-table-query.ts +1 -1
- package/src/components/table/locales.ts +1116 -0
- package/src/components/table/search-input.tsx +12 -3
- package/src/components/table/selection-toolbar.tsx +23 -6
- package/src/components/table/table-empty-state.tsx +43 -3
- package/src/components/table/table-no-results.tsx +3 -3
- package/src/components/table/table-pagination.tsx +4 -3
- package/src/components/table/types.ts +1 -0
- package/src/components/tax-reports/index.ts +1 -0
- package/src/components/tax-reports/kir-export-form.tsx +46 -8
- package/src/components/tax-reports/slovenia-tax-profile-step.tsx +191 -0
- package/src/components/tax-reports/slovenia-yearly-export-form.tsx +509 -0
- package/src/components/tax-reports/slovenia-yearly-review-step.tsx +253 -0
- package/src/components/tax-reports/slovenia-yearly-summary.tsx +19 -0
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -2
- package/src/components/ui/sticky-form-footer.tsx +7 -1
- package/src/components/webhook-logs/index.ts +6 -0
- package/src/components/webhook-logs/locales.ts +392 -0
- package/src/components/webhook-logs/webhook-delivery-detail.tsx +255 -0
- package/src/components/webhook-logs/webhook-delivery-list-table.tsx +278 -0
- package/src/components/wl-subscription/index.ts +1 -0
- package/src/components/wl-subscription/locked-feature.tsx +1 -0
- package/src/components/wl-subscription/paywall.tsx +193 -0
- package/src/components/wl-subscription/upgrade-modal.tsx +93 -29
- package/src/generate-schemas.ts +10 -5
- package/src/generated/schemas/customer.ts +2 -0
- package/src/generated/schemas/entity.ts +34 -0
- package/src/generated/schemas/me.ts +20 -1
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +40 -34
- package/src/generated/schemas/rendercreditnotepreview_body.ts +42 -36
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +23 -13
- package/src/generated/schemas/renderestimatepreview_body.ts +23 -13
- package/src/generated/schemas/renderinvoicepreview_body.ts +40 -34
- package/src/generated/schemas/sendemail_body.ts +44 -0
- package/src/generated/schemas/startpdfexport_body.ts +91 -1
- package/src/generated/schemas/webhook.ts +10 -0
- package/src/hooks/use-duplicate-document.ts +51 -13
- package/src/hooks/use-eslog-validation.ts +59 -0
- package/src/hooks/use-premise-selection.ts +186 -0
- package/src/lib/browser-cookies.ts +4 -4
- package/src/lib/date-fns-locale.ts +48 -0
- package/src/lib/fiscalization-options.ts +81 -0
- package/src/lib/locale.ts +38 -0
- package/src/lib/template-variables.tsx +1 -1
- package/src/lib/translation.ts +14 -3
- package/src/providers/entities-context.tsx +1 -0
- package/src/providers/entities-provider.tsx +102 -3
- package/src/providers/form-footer-context.tsx +37 -4
- package/src/providers/sdk-provider.tsx +7 -2
- package/src/providers/white-label-provider.tsx +4 -1
- package/src/providers/wl-subscription-provider.tsx +90 -3
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { getClientHeaders } from "@spaceinvoices/js-sdk";
|
|
4
|
+
import { formatDistanceToNow } from "date-fns";
|
|
5
|
+
import { useCallback, useMemo } from "react";
|
|
6
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/ui/components/ui/sheet";
|
|
7
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
|
|
8
|
+
import { AUTH_COOKIES } from "@/ui/lib/auth";
|
|
9
|
+
import { getCookie } from "@/ui/lib/browser-cookies";
|
|
10
|
+
import { getDateFnsLocale } from "@/ui/lib/date-fns-locale";
|
|
11
|
+
import { createTranslation } from "@/ui/lib/translation";
|
|
12
|
+
import { cn } from "@/ui/lib/utils";
|
|
13
|
+
import { DataTable } from "../table/data-table";
|
|
14
|
+
import { withTableTranslations } from "../table/locales";
|
|
15
|
+
import type { Column, FilterConfig, ListTableProps, TableQueryParams, TableQueryResponse } from "../table/types";
|
|
16
|
+
import translations from "./locales";
|
|
17
|
+
import { WebhookDeliveryDetail } from "./webhook-delivery-detail";
|
|
18
|
+
|
|
19
|
+
export interface WebhookDeliveryResponse {
|
|
20
|
+
id: string;
|
|
21
|
+
webhook_id: string;
|
|
22
|
+
entity_id: string;
|
|
23
|
+
event_type: string;
|
|
24
|
+
status: string;
|
|
25
|
+
request_body: Record<string, unknown>;
|
|
26
|
+
response_status: number | null;
|
|
27
|
+
response_body: string | null;
|
|
28
|
+
error_message: string | null;
|
|
29
|
+
attempt: number;
|
|
30
|
+
max_attempts: number;
|
|
31
|
+
next_retry_at: string | null;
|
|
32
|
+
duration_ms: number | null;
|
|
33
|
+
created_at: string;
|
|
34
|
+
completed_at: string | null;
|
|
35
|
+
webhook_url: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const getApiBaseUrl = () => {
|
|
39
|
+
if (typeof import.meta !== "undefined" && import.meta.env) {
|
|
40
|
+
return import.meta.env.VITE_API_URL || import.meta.env.BUN_PUBLIC_API_URL || "";
|
|
41
|
+
}
|
|
42
|
+
return "";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WEBHOOK_LOGS_CACHE_KEY = "webhook-logs";
|
|
46
|
+
const mergedTranslations = withTableTranslations(translations);
|
|
47
|
+
|
|
48
|
+
const STATUS_COLORS: Record<string, string> = {
|
|
49
|
+
success: "bg-green-500",
|
|
50
|
+
failed: "bg-red-500",
|
|
51
|
+
pending: "bg-yellow-500",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const STATUS_BADGE_COLORS: Record<string, string> = {
|
|
55
|
+
success: "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300",
|
|
56
|
+
failed: "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300",
|
|
57
|
+
pending: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function StatusDot({ status }: { status: string }) {
|
|
61
|
+
return <span className={cn("inline-block h-2.5 w-2.5 rounded-full", STATUS_COLORS[status] || "bg-gray-400")} />;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function StatusBadge({ status, t }: { status: string; t: (key: string) => string }) {
|
|
65
|
+
return (
|
|
66
|
+
<span
|
|
67
|
+
className={cn(
|
|
68
|
+
"inline-flex items-center rounded px-1.5 py-0.5 font-medium text-xs capitalize",
|
|
69
|
+
STATUS_BADGE_COLORS[status] || "bg-gray-100 text-gray-700",
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
{t(status)}
|
|
73
|
+
</span>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type WebhookDeliveryListTableProps = ListTableProps<WebhookDeliveryResponse> & {
|
|
78
|
+
environment?: "live" | "sandbox";
|
|
79
|
+
showEntityColumn?: boolean;
|
|
80
|
+
selectedDelivery?: WebhookDeliveryResponse | null;
|
|
81
|
+
onSelectDelivery?: (delivery: WebhookDeliveryResponse | null) => void;
|
|
82
|
+
/** Translation function */
|
|
83
|
+
t?: (key: string) => string;
|
|
84
|
+
/** Locale used for relative date formatting */
|
|
85
|
+
locale?: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export function WebhookDeliveryListTable({
|
|
89
|
+
queryParams,
|
|
90
|
+
onChangeParams,
|
|
91
|
+
entityId,
|
|
92
|
+
environment,
|
|
93
|
+
showEntityColumn = false,
|
|
94
|
+
selectedDelivery,
|
|
95
|
+
onSelectDelivery,
|
|
96
|
+
t = (key) => key,
|
|
97
|
+
locale,
|
|
98
|
+
}: WebhookDeliveryListTableProps) {
|
|
99
|
+
const translate = createTranslation({ t, locale, translations: mergedTranslations });
|
|
100
|
+
|
|
101
|
+
const handleFetch = useCallback(
|
|
102
|
+
async (params: TableQueryParams): Promise<TableQueryResponse<WebhookDeliveryResponse>> => {
|
|
103
|
+
const token = getCookie(AUTH_COOKIES.TOKEN);
|
|
104
|
+
if (!token) throw new Error("Not authenticated");
|
|
105
|
+
|
|
106
|
+
const queryParamsUrl = new URLSearchParams();
|
|
107
|
+
|
|
108
|
+
if (entityId) {
|
|
109
|
+
queryParamsUrl.set("entity_id", entityId);
|
|
110
|
+
} else {
|
|
111
|
+
queryParamsUrl.set("environment", environment || "live");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (params.limit) queryParamsUrl.set("limit", String(params.limit));
|
|
115
|
+
if (params.next_cursor) queryParamsUrl.set("next_cursor", params.next_cursor);
|
|
116
|
+
if (params.prev_cursor) queryParamsUrl.set("prev_cursor", params.prev_cursor);
|
|
117
|
+
|
|
118
|
+
// Search maps to event_type filter
|
|
119
|
+
if (params.search) queryParamsUrl.set("event_type", params.search);
|
|
120
|
+
|
|
121
|
+
// Status filter (reuse http_status field for webhook status)
|
|
122
|
+
if (params.filter_http_status) queryParamsUrl.set("status", params.filter_http_status);
|
|
123
|
+
|
|
124
|
+
// Date filters
|
|
125
|
+
if (params.filter_date_from) queryParamsUrl.set("date_from", params.filter_date_from);
|
|
126
|
+
if (params.filter_date_to) queryParamsUrl.set("date_to", params.filter_date_to);
|
|
127
|
+
|
|
128
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
129
|
+
const response = await fetch(`${apiBaseUrl}/webhook-logs?${queryParamsUrl.toString()}`, {
|
|
130
|
+
headers: {
|
|
131
|
+
Authorization: `Bearer ${token}`,
|
|
132
|
+
...getClientHeaders("ui"),
|
|
133
|
+
"Content-Type": "application/json",
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
throw new Error(`Failed to fetch webhook logs: ${response.status}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (await response.json()) as TableQueryResponse<WebhookDeliveryResponse>;
|
|
142
|
+
},
|
|
143
|
+
[entityId, environment],
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const columns: Column<WebhookDeliveryResponse>[] = useMemo(() => {
|
|
147
|
+
const dateLocale = getDateFnsLocale(locale);
|
|
148
|
+
const cols: Column<WebhookDeliveryResponse>[] = [
|
|
149
|
+
{
|
|
150
|
+
id: "status_dot",
|
|
151
|
+
header: "",
|
|
152
|
+
className: "w-8",
|
|
153
|
+
cell: (d) => <StatusDot status={d.status} />,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "status",
|
|
157
|
+
header: translate("Status"),
|
|
158
|
+
className: "w-24",
|
|
159
|
+
cell: (d) => <StatusBadge status={d.status} t={translate} />,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "event_type",
|
|
163
|
+
header: translate("Event"),
|
|
164
|
+
cell: (d) => <span className="truncate font-mono text-sm">{d.event_type}</span>,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "webhook_url",
|
|
168
|
+
header: translate("URL"),
|
|
169
|
+
className: "hidden md:table-cell",
|
|
170
|
+
cell: (d) => (
|
|
171
|
+
<span className="truncate text-muted-foreground text-sm" title={d.webhook_url ?? undefined}>
|
|
172
|
+
{d.webhook_url ? truncateUrl(d.webhook_url) : "—"}
|
|
173
|
+
</span>
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
if (showEntityColumn) {
|
|
179
|
+
cols.push({
|
|
180
|
+
id: "entity_id",
|
|
181
|
+
header: translate("Entity"),
|
|
182
|
+
className: "hidden lg:table-cell",
|
|
183
|
+
cell: (d) => (
|
|
184
|
+
<Tooltip>
|
|
185
|
+
<TooltipTrigger asChild>
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
className="cursor-pointer rounded bg-muted px-1.5 py-0.5 font-mono text-muted-foreground text-xs hover:bg-muted/80"
|
|
189
|
+
onClick={(e) => {
|
|
190
|
+
e.stopPropagation();
|
|
191
|
+
navigator.clipboard.writeText(d.entity_id);
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
{d.entity_id}
|
|
195
|
+
</button>
|
|
196
|
+
</TooltipTrigger>
|
|
197
|
+
<TooltipContent>{translate("Copy")}</TooltipContent>
|
|
198
|
+
</Tooltip>
|
|
199
|
+
),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
cols.push(
|
|
204
|
+
{
|
|
205
|
+
id: "duration_ms",
|
|
206
|
+
header: translate("Duration"),
|
|
207
|
+
className: "hidden sm:table-cell w-20",
|
|
208
|
+
align: "right",
|
|
209
|
+
cell: (d) => (
|
|
210
|
+
<span className="text-muted-foreground text-xs">{d.duration_ms != null ? `${d.duration_ms}ms` : "—"}</span>
|
|
211
|
+
),
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "created_at",
|
|
215
|
+
header: translate("Time"),
|
|
216
|
+
align: "right",
|
|
217
|
+
cell: (d) => (
|
|
218
|
+
<span className="text-muted-foreground text-xs">
|
|
219
|
+
{formatDistanceToNow(new Date(d.created_at), { addSuffix: true, locale: dateLocale })}
|
|
220
|
+
</span>
|
|
221
|
+
),
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
return cols;
|
|
226
|
+
}, [locale, showEntityColumn, translate]);
|
|
227
|
+
|
|
228
|
+
const filterConfig: FilterConfig = {
|
|
229
|
+
dateFields: [{ id: "created_at", label: translate("Date") }],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const cacheKey = entityId
|
|
233
|
+
? `${WEBHOOK_LOGS_CACHE_KEY}-${entityId}`
|
|
234
|
+
: `${WEBHOOK_LOGS_CACHE_KEY}-account-${environment || "live"}`;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<>
|
|
238
|
+
<DataTable
|
|
239
|
+
columns={columns}
|
|
240
|
+
cacheKey={cacheKey}
|
|
241
|
+
resourceName="webhook delivery"
|
|
242
|
+
onFetch={handleFetch}
|
|
243
|
+
queryParams={queryParams}
|
|
244
|
+
onChangeParams={onChangeParams}
|
|
245
|
+
entityId={entityId}
|
|
246
|
+
filterConfig={filterConfig}
|
|
247
|
+
onRowClick={(d) => onSelectDelivery?.(d)}
|
|
248
|
+
t={translate}
|
|
249
|
+
/>
|
|
250
|
+
|
|
251
|
+
<Sheet open={!!selectedDelivery} onOpenChange={(open) => !open && onSelectDelivery?.(null)}>
|
|
252
|
+
<SheetContent className="w-full overflow-y-auto sm:max-w-2xl">
|
|
253
|
+
<SheetHeader>
|
|
254
|
+
<SheetTitle className="flex items-center gap-2">
|
|
255
|
+
{selectedDelivery && (
|
|
256
|
+
<>
|
|
257
|
+
<StatusBadge status={selectedDelivery.status} t={translate} />
|
|
258
|
+
<span className="font-mono text-sm">{selectedDelivery.event_type}</span>
|
|
259
|
+
</>
|
|
260
|
+
)}
|
|
261
|
+
</SheetTitle>
|
|
262
|
+
</SheetHeader>
|
|
263
|
+
{selectedDelivery && <WebhookDeliveryDetail delivery={selectedDelivery} t={translate} locale={locale} />}
|
|
264
|
+
</SheetContent>
|
|
265
|
+
</Sheet>
|
|
266
|
+
</>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function truncateUrl(url: string, maxLength = 40): string {
|
|
271
|
+
try {
|
|
272
|
+
const parsed = new URL(url);
|
|
273
|
+
const display = parsed.hostname + parsed.pathname;
|
|
274
|
+
return display.length > maxLength ? `${display.slice(0, maxLength)}…` : display;
|
|
275
|
+
} catch {
|
|
276
|
+
return url.length > maxLength ? `${url.slice(0, maxLength)}…` : url;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -168,6 +168,7 @@ function getDefaultLockedMessage(feature: GatedFeature): string {
|
|
|
168
168
|
api_access: "API access requires an Advanced plan",
|
|
169
169
|
webhooks: "Webhooks require an Advanced plan",
|
|
170
170
|
priority_support: "Priority support requires an Advanced plan",
|
|
171
|
+
e_invoicing: "E-Invoicing requires a Starter or Advanced plan",
|
|
171
172
|
};
|
|
172
173
|
|
|
173
174
|
return messages[feature] || "This feature requires a plan upgrade";
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Check, Crown, Loader2, Sparkles, Zap } from "lucide-react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { useWLSubscription, type WhiteLabelPlan } from "../../providers/wl-subscription-provider";
|
|
5
|
+
import { Badge } from "../ui/badge";
|
|
6
|
+
import { Button } from "../ui/button";
|
|
7
|
+
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card";
|
|
8
|
+
import { Switch } from "../ui/switch";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Paywall component
|
|
12
|
+
*
|
|
13
|
+
* Shown when `needsPayment` is true — either after trial expiry
|
|
14
|
+
* or for paid-only configs with no active subscription.
|
|
15
|
+
* Displays plan cards with monthly/yearly toggle and checkout buttons.
|
|
16
|
+
*/
|
|
17
|
+
export function Paywall() {
|
|
18
|
+
const { isTrialExpired, availablePlans, createCheckout } = useWLSubscription();
|
|
19
|
+
const [isYearly, setIsYearly] = useState(false);
|
|
20
|
+
const [isRedirecting, setIsRedirecting] = useState<string | null>(null);
|
|
21
|
+
const [checkoutError, setCheckoutError] = useState<string | null>(null);
|
|
22
|
+
|
|
23
|
+
// Only show paid plans
|
|
24
|
+
const paidPlans = [...availablePlans].filter((p) => !p.is_free).sort((a, b) => a.display_order - b.display_order);
|
|
25
|
+
|
|
26
|
+
const handleCheckout = async (planSlug: string) => {
|
|
27
|
+
try {
|
|
28
|
+
setIsRedirecting(planSlug);
|
|
29
|
+
setCheckoutError(null);
|
|
30
|
+
const billingInterval = isYearly ? "yearly" : "monthly";
|
|
31
|
+
const checkoutUrl = await createCheckout(planSlug, billingInterval);
|
|
32
|
+
window.location.href = checkoutUrl;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
setCheckoutError(err instanceof Error ? err.message : "Failed to start checkout");
|
|
35
|
+
setIsRedirecting(null);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex min-h-[60vh] flex-col items-center justify-center px-4 py-12">
|
|
41
|
+
<div className="mx-auto max-w-3xl text-center">
|
|
42
|
+
<Sparkles className="mx-auto mb-4 h-10 w-10 text-primary" />
|
|
43
|
+
<h1 className="mb-2 font-bold text-3xl tracking-tight">
|
|
44
|
+
{isTrialExpired ? "Your trial has expired" : "Choose a plan to get started"}
|
|
45
|
+
</h1>
|
|
46
|
+
<p className="mb-8 text-muted-foreground">
|
|
47
|
+
{isTrialExpired
|
|
48
|
+
? "Subscribe to a plan to continue using all features."
|
|
49
|
+
: "Pick the plan that works best for your business."}
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
{/* Monthly/Yearly toggle */}
|
|
53
|
+
<div className="mb-8 flex items-center justify-center gap-3">
|
|
54
|
+
<span className={`text-sm ${!isYearly ? "font-medium" : "text-muted-foreground"}`}>Monthly</span>
|
|
55
|
+
<Switch checked={isYearly} onCheckedChange={setIsYearly} />
|
|
56
|
+
<span className={`text-sm ${isYearly ? "font-medium" : "text-muted-foreground"}`}>Yearly</span>
|
|
57
|
+
{isYearly && (
|
|
58
|
+
<Badge variant="secondary" className="ml-1">
|
|
59
|
+
Save 20%
|
|
60
|
+
</Badge>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{checkoutError && (
|
|
65
|
+
<p className="mb-4 rounded-md bg-destructive/10 px-3 py-2 text-destructive text-sm">{checkoutError}</p>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
69
|
+
{paidPlans.map((plan, index) => (
|
|
70
|
+
<PaywallPlanCard
|
|
71
|
+
key={plan.id}
|
|
72
|
+
plan={plan}
|
|
73
|
+
isYearly={isYearly}
|
|
74
|
+
isPopular={index === paidPlans.length - 1}
|
|
75
|
+
isLoading={isRedirecting === plan.slug}
|
|
76
|
+
isDisabled={isRedirecting !== null}
|
|
77
|
+
onSelect={() => handleCheckout(plan.slug)}
|
|
78
|
+
/>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{paidPlans.length === 0 && (
|
|
83
|
+
<p className="py-8 text-muted-foreground">No plans available. Contact support for more information.</p>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================
|
|
91
|
+
// PLAN CARD
|
|
92
|
+
// ============================================
|
|
93
|
+
|
|
94
|
+
type PaywallPlanCardProps = {
|
|
95
|
+
plan: WhiteLabelPlan;
|
|
96
|
+
isYearly: boolean;
|
|
97
|
+
isPopular: boolean;
|
|
98
|
+
isLoading: boolean;
|
|
99
|
+
isDisabled: boolean;
|
|
100
|
+
onSelect: () => void;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function PaywallPlanCard({ plan, isYearly, isPopular, isLoading, isDisabled, onSelect }: PaywallPlanCardProps) {
|
|
104
|
+
const monthlyPrice = plan.base_price_cents ? plan.base_price_cents / 100 : 0;
|
|
105
|
+
const yearlyTotal = Math.round(monthlyPrice * 12 * 0.8 * 100) / 100;
|
|
106
|
+
const yearlyMonthly = Math.round((yearlyTotal / 12) * 100) / 100;
|
|
107
|
+
|
|
108
|
+
const displayPrice = isYearly ? yearlyMonthly : monthlyPrice;
|
|
109
|
+
const documentsLimit = plan.limits?.documents_per_month ?? "Unlimited";
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Card className={`relative flex flex-col ${isPopular ? "border-primary ring-2 ring-primary" : ""}`}>
|
|
113
|
+
{isPopular && (
|
|
114
|
+
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
115
|
+
<Badge className="bg-primary text-primary-foreground">Most Popular</Badge>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
<CardHeader className="pb-2">
|
|
119
|
+
<div className="flex items-center gap-2">
|
|
120
|
+
<PaywallPlanIcon slug={plan.slug} />
|
|
121
|
+
<CardTitle className="text-xl">{plan.name}</CardTitle>
|
|
122
|
+
</div>
|
|
123
|
+
<CardDescription>{documentsLimit} documents/month</CardDescription>
|
|
124
|
+
</CardHeader>
|
|
125
|
+
<CardContent className="flex-1">
|
|
126
|
+
<div className="mb-4">
|
|
127
|
+
<span className="font-bold text-4xl">€{displayPrice.toFixed(0)}</span>
|
|
128
|
+
<span className="text-muted-foreground">/mo</span>
|
|
129
|
+
{isYearly && (
|
|
130
|
+
<p className="mt-1 text-muted-foreground text-sm">€{yearlyTotal.toFixed(0)} billed yearly</p>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<ul className="space-y-2">
|
|
135
|
+
{getPaywallFeatures(plan.slug).map((feature) => (
|
|
136
|
+
<li key={feature} className="flex items-center gap-2 text-sm">
|
|
137
|
+
<Check className="h-4 w-4 flex-shrink-0 text-green-500" />
|
|
138
|
+
{feature}
|
|
139
|
+
</li>
|
|
140
|
+
))}
|
|
141
|
+
</ul>
|
|
142
|
+
</CardContent>
|
|
143
|
+
<CardFooter>
|
|
144
|
+
<Button className="w-full" variant={isPopular ? "default" : "outline"} disabled={isDisabled} onClick={onSelect}>
|
|
145
|
+
{isLoading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
|
|
146
|
+
{isLoading ? "Redirecting..." : `Get ${plan.name}`}
|
|
147
|
+
</Button>
|
|
148
|
+
</CardFooter>
|
|
149
|
+
</Card>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ============================================
|
|
154
|
+
// HELPERS
|
|
155
|
+
// ============================================
|
|
156
|
+
|
|
157
|
+
function PaywallPlanIcon({ slug }: { slug: string }) {
|
|
158
|
+
switch (slug) {
|
|
159
|
+
case "basic":
|
|
160
|
+
return <Zap className="h-5 w-5 text-blue-500" />;
|
|
161
|
+
case "advanced":
|
|
162
|
+
return <Crown className="h-5 w-5 text-amber-500" />;
|
|
163
|
+
default:
|
|
164
|
+
return <Sparkles className="h-5 w-5 text-primary" />;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getPaywallFeatures(slug: string): string[] {
|
|
169
|
+
switch (slug) {
|
|
170
|
+
case "basic":
|
|
171
|
+
return [
|
|
172
|
+
"Invoices & estimates",
|
|
173
|
+
"Customer management",
|
|
174
|
+
"PDF export",
|
|
175
|
+
"FURS fiscalization",
|
|
176
|
+
"eSlog export",
|
|
177
|
+
"Recurring invoices",
|
|
178
|
+
"Email sending",
|
|
179
|
+
];
|
|
180
|
+
case "advanced":
|
|
181
|
+
return [
|
|
182
|
+
"Everything in Basic",
|
|
183
|
+
"All features unlocked",
|
|
184
|
+
"Custom templates",
|
|
185
|
+
"API access",
|
|
186
|
+
"Webhooks",
|
|
187
|
+
"Priority support",
|
|
188
|
+
"Overage billing (pay per doc)",
|
|
189
|
+
];
|
|
190
|
+
default:
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|