@spaceinvoices/react-ui 0.3.0 → 0.4.0

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.
Files changed (50) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/registry.json +24 -225
  4. package/src/components/advance-invoices/advance-invoices.hooks.ts +2 -2
  5. package/src/components/documents/documents.hooks.ts +5 -48
  6. package/src/components/documents/shared/document-preview-display.tsx +12 -1
  7. package/src/components/documents/view/document-actions-bar.tsx +20 -12
  8. package/src/components/documents/view/document-activities-list.tsx +166 -0
  9. package/src/components/documents/view/document-details-card.tsx +6 -6
  10. package/src/components/documents/view/index.ts +1 -0
  11. package/src/components/documents/view/locales/de.ts +32 -0
  12. package/src/components/documents/view/locales/es.ts +32 -0
  13. package/src/components/documents/view/locales/fr.ts +32 -0
  14. package/src/components/documents/view/locales/hr.ts +32 -0
  15. package/src/components/documents/view/locales/it.ts +32 -0
  16. package/src/components/documents/view/locales/nl.ts +32 -0
  17. package/src/components/documents/view/locales/pl.ts +32 -0
  18. package/src/components/documents/view/locales/pt.ts +32 -0
  19. package/src/components/documents/view/locales/sl.ts +32 -0
  20. package/src/components/entities/fina-settings-form/locales/de.ts +3 -0
  21. package/src/components/entities/fina-settings-form/locales/en.ts +3 -0
  22. package/src/components/entities/fina-settings-form/locales/es.ts +3 -0
  23. package/src/components/entities/fina-settings-form/locales/fr.ts +3 -0
  24. package/src/components/entities/fina-settings-form/locales/hr.ts +3 -0
  25. package/src/components/entities/fina-settings-form/locales/it.ts +3 -0
  26. package/src/components/entities/fina-settings-form/locales/nl.ts +3 -0
  27. package/src/components/entities/fina-settings-form/locales/pl.ts +3 -0
  28. package/src/components/entities/fina-settings-form/locales/pt.ts +3 -0
  29. package/src/components/entities/fina-settings-form/locales/sl.ts +3 -0
  30. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +15 -7
  31. package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +1 -1
  32. package/src/components/entities/furs-settings-form/locales/de.ts +2 -0
  33. package/src/components/entities/furs-settings-form/locales/en.ts +12 -0
  34. package/src/components/entities/furs-settings-form/locales/es.ts +2 -0
  35. package/src/components/entities/furs-settings-form/locales/fr.ts +2 -0
  36. package/src/components/entities/furs-settings-form/locales/hr.ts +2 -0
  37. package/src/components/entities/furs-settings-form/locales/it.ts +2 -0
  38. package/src/components/entities/furs-settings-form/locales/nl.ts +2 -0
  39. package/src/components/entities/furs-settings-form/locales/pl.ts +2 -0
  40. package/src/components/entities/furs-settings-form/locales/pt.ts +2 -0
  41. package/src/components/entities/furs-settings-form/locales/sl.ts +14 -0
  42. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +121 -1
  43. package/src/components/invoices/index.ts +1 -1
  44. package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +2 -2
  45. package/src/components/invoices/view/fiscalization-status-card.tsx +121 -0
  46. package/src/generated/schemas/creditnote.ts +3 -0
  47. package/src/hooks/use-duplicate-document.ts +19 -11
  48. package/src/providers/entities-provider.tsx +21 -0
  49. package/src/components/invoices/view/fina-info-display.tsx +0 -196
  50. package/src/components/invoices/view/furs-info-display.tsx +0 -213
package/cli/dist/index.js CHANGED
@@ -870,7 +870,7 @@ async function list(options = {}) {
870
870
 
871
871
  // cli/src/index.ts
872
872
  var program = new Command();
873
- program.name("spaceinvoices-ui").description("CLI for adding Space Invoices React UI components to your project").version("0.3.0");
873
+ program.name("spaceinvoices-ui").description("CLI for adding Space Invoices React UI components to your project").version("0.4.0");
874
874
  program.option("--local <path>", "Use local registry from specified path (for development)");
875
875
  program.command("init").description("Initialize Space Invoices UI in your project").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Overwrite existing configuration").option("--cwd <path>", "Working directory (defaults to current directory)").action(async (options) => {
876
876
  const globalOpts = program.opts();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spaceinvoices/react-ui",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "description": "Space Invoices UI components - copy-paste distribution with CLI support",
package/registry.json CHANGED
@@ -518,248 +518,47 @@
518
518
  "providers": ["sdk-provider"],
519
519
  "utils": ["translation"]
520
520
  },
521
- "dashboard/collection-rate-card": {
522
- "name": "Collection Rate Card",
521
+ "invoices/fiscalization-status-card": {
522
+ "name": "Fiscalization Status Card",
523
523
  "category": "feature",
524
524
  "files": [
525
- "components/dashboard/collection-rate-card/collection-rate-card.tsx",
526
- "components/dashboard/collection-rate-card/use-collection-rate.ts",
527
- "components/dashboard/collection-rate-card/locales/sl.ts",
528
- "components/dashboard/collection-rate-card/index.ts"
525
+ "components/invoices/view/fiscalization-status-card.tsx"
529
526
  ],
530
- "dependencies": ["ui/card", "dashboard/loading-card"],
527
+ "dependencies": ["ui/card", "ui/badge", "ui/button"],
531
528
  "providers": ["sdk-provider"],
532
529
  "utils": ["translation"]
533
530
  },
534
- "dashboard/tax-collected-card": {
535
- "name": "Tax Collected Card",
536
- "category": "feature",
537
- "files": [
538
- "components/dashboard/tax-collected-card/tax-collected-card.tsx",
539
- "components/dashboard/tax-collected-card/use-tax-collected.ts",
540
- "components/dashboard/tax-collected-card/index.ts"
541
- ],
542
- "dependencies": ["ui/card", "dashboard/loading-card"],
543
- "providers": ["sdk-provider"]
544
- },
545
- "advance-invoices/create-advance-invoice-form": {
546
- "name": "Create Advance Invoice Form",
547
- "category": "feature",
548
- "files": [
549
- "components/advance-invoices/create/create-advance-invoice-form.tsx",
550
- "components/advance-invoices/create/prepare-advance-invoice-submission.ts",
551
- "components/advance-invoices/create/locales/de.ts",
552
- "components/advance-invoices/create/locales/sl.ts",
553
- "components/advance-invoices/advance-invoices.hooks.ts"
554
- ],
555
- "dependencies": [
556
- "form/form-input",
557
- "ui/form",
558
- "ui/card",
559
- "ui/button",
560
- "ui/calendar",
561
- "ui/popover",
562
- "customers/customer-combobox"
563
- ],
564
- "providers": ["sdk-provider", "entities-provider"],
565
- "utils": ["translation", "hooks"],
566
- "schemas": ["createAdvanceInvoiceSchema"]
567
- },
568
- "advance-invoices/advance-invoice-list-table": {
569
- "name": "Advance Invoice List Table",
570
- "category": "feature",
571
- "files": [
572
- "components/advance-invoices/list/list-table.tsx",
573
- "components/advance-invoices/list/list-row-actions.tsx",
574
- "components/advance-invoices/list/use-advance-invoice-download.ts",
575
- "components/advance-invoices/list/index.ts",
576
- "components/advance-invoices/advance-invoices.hooks.ts"
577
- ],
578
- "dependencies": ["table/data-table", "ui/dropdown-menu", "ui/button", "ui/badge"],
579
- "providers": ["sdk-provider"],
580
- "utils": ["translation", "hooks"]
581
- },
582
- "activities/activity-timeline": {
583
- "name": "Activity Timeline",
531
+ "documents/document-activities-list": {
532
+ "name": "Document Activities List",
584
533
  "category": "feature",
585
534
  "files": [
586
- "components/activities/activity-timeline.tsx",
587
- "components/activities/locales/de.ts",
588
- "components/activities/locales/sl.ts",
589
- "components/activities/index.ts"
535
+ "components/documents/view/document-activities-list.tsx",
536
+ "components/documents/view/locales/de.ts",
537
+ "components/documents/view/locales/es.ts",
538
+ "components/documents/view/locales/fr.ts",
539
+ "components/documents/view/locales/hr.ts",
540
+ "components/documents/view/locales/it.ts",
541
+ "components/documents/view/locales/nl.ts",
542
+ "components/documents/view/locales/pl.ts",
543
+ "components/documents/view/locales/pt.ts",
544
+ "components/documents/view/locales/sl.ts"
590
545
  ],
591
- "dependencies": ["ui/badge"],
546
+ "dependencies": ["ui/card", "ui/badge", "ui/button"],
592
547
  "providers": ["sdk-provider"],
593
548
  "utils": ["translation"]
594
549
  },
595
- "export/document-export-form": {
596
- "name": "Document Export Form",
597
- "category": "feature",
598
- "files": [
599
- "components/export/document-export-form.tsx",
600
- "components/export/index.ts"
601
- ],
602
- "dependencies": ["ui/form", "ui/button", "ui/select", "ui/calendar", "ui/popover"],
603
- "providers": ["sdk-provider", "entities-provider"],
604
- "utils": ["translation"]
605
- },
606
- "tax-reports/kir-export-form": {
607
- "name": "KIR Export Form",
608
- "category": "feature",
609
- "files": [
610
- "components/tax-reports/kir-export-form.tsx",
611
- "components/tax-reports/index.ts"
612
- ],
613
- "dependencies": ["ui/form", "ui/button", "ui/select", "ui/calendar", "ui/popover"],
614
- "providers": ["sdk-provider", "entities-provider"],
615
- "utils": ["translation"]
616
- },
617
- "company-registry/company-registry-autocomplete": {
618
- "name": "Company Registry Autocomplete",
550
+ "dashboard/collection-rate-card": {
551
+ "name": "Collection Rate Card",
619
552
  "category": "feature",
620
553
  "files": [
621
- "components/company-registry/company-registry-autocomplete.tsx",
622
- "components/company-registry/company-registry.hooks.ts",
623
- "components/company-registry/index.ts"
554
+ "components/dashboard/collection-rate-card/collection-rate-card.tsx",
555
+ "components/dashboard/collection-rate-card/use-collection-rate.ts",
556
+ "components/dashboard/collection-rate-card/locales/sl.ts",
557
+ "components/dashboard/collection-rate-card/index.ts"
624
558
  ],
625
- "dependencies": ["ui/command", "ui/popover", "ui/button"],
559
+ "dependencies": ["ui/card", "dashboard/loading-card"],
626
560
  "providers": ["sdk-provider"],
627
561
  "utils": ["translation"]
628
- },
629
- "request-logs/request-logs-page": {
630
- "name": "Request Logs Page",
631
- "category": "feature",
632
- "files": [
633
- "components/request-logs/request-logs-page.tsx",
634
- "components/request-logs/request-log-list-table.tsx",
635
- "components/request-logs/request-log-detail.tsx",
636
- "components/request-logs/index.ts"
637
- ],
638
- "dependencies": ["table/data-table", "ui/card", "ui/badge"],
639
- "providers": ["sdk-provider"]
640
- },
641
- "customers/edit-customer-form": {
642
- "name": "Edit Customer Form",
643
- "category": "feature",
644
- "files": [
645
- "components/customers/edit-customer-form/edit-customer-form.tsx",
646
- "components/customers/edit-customer-form/locales/de.ts",
647
- "components/customers/edit-customer-form/locales/sl.ts",
648
- "components/customers/customers.hooks.ts"
649
- ],
650
- "dependencies": ["form/form-input", "ui/form"],
651
- "providers": ["sdk-provider"],
652
- "utils": ["translation", "hooks"]
653
- },
654
- "items/edit-item-form": {
655
- "name": "Edit Item Form",
656
- "category": "feature",
657
- "files": [
658
- "components/items/edit-item-form/edit-item-form.tsx",
659
- "components/items/edit-item-form/locales/de.ts",
660
- "components/items/edit-item-form/locales/sl.ts",
661
- "components/items/items.hooks.ts"
662
- ],
663
- "dependencies": ["form/form-input", "ui/form"],
664
- "providers": ["sdk-provider"],
665
- "utils": ["translation", "hooks"]
666
- },
667
- "taxes/edit-tax-form": {
668
- "name": "Edit Tax Form",
669
- "category": "feature",
670
- "files": [
671
- "components/taxes/edit-tax-form/edit-tax-form.tsx",
672
- "components/taxes/edit-tax-form/locales/de.ts",
673
- "components/taxes/edit-tax-form/locales/sl.ts",
674
- "components/taxes/taxes.hooks.ts"
675
- ],
676
- "dependencies": ["form/form-input", "ui/form"],
677
- "providers": ["sdk-provider"],
678
- "utils": ["translation", "hooks"]
679
- },
680
- "payments/edit-payment-form": {
681
- "name": "Edit Payment Form",
682
- "category": "feature",
683
- "files": [
684
- "components/payments/edit-payment-form/edit-payment-form.tsx",
685
- "components/payments/edit-payment-form/locales/de.ts",
686
- "components/payments/edit-payment-form/locales/sl.ts",
687
- "components/payments/edit-payment-form/index.ts",
688
- "components/payments/payments.hooks.ts"
689
- ],
690
- "dependencies": ["form/form-input", "ui/form", "ui/select"],
691
- "providers": ["sdk-provider"],
692
- "utils": ["translation", "hooks"]
693
- },
694
- "payments/payment-list-table": {
695
- "name": "Payment List Table",
696
- "category": "feature",
697
- "files": [
698
- "components/payments/list/list-table.tsx",
699
- "components/payments/list/list-row-actions.tsx",
700
- "components/payments/list/index.ts",
701
- "components/payments/payments.hooks.ts"
702
- ],
703
- "dependencies": ["table/data-table", "ui/dropdown-menu", "ui/button"],
704
- "providers": ["sdk-provider"],
705
- "utils": ["translation", "hooks"]
706
- },
707
- "entities/furs-settings-form": {
708
- "name": "FURS Settings Form",
709
- "category": "feature",
710
- "files": [
711
- "components/entities/furs-settings-form/furs-settings-form.tsx",
712
- "components/entities/furs-settings-form/furs-settings.hooks.ts",
713
- "components/entities/furs-settings-form/sections/general-settings-section.tsx",
714
- "components/entities/furs-settings-form/sections/certificate-settings-section.tsx",
715
- "components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx",
716
- "components/entities/furs-settings-form/sections/premises-management-section.tsx",
717
- "components/entities/furs-settings-form/sections/register-premise-dialog.tsx",
718
- "components/entities/furs-settings-form/locales/de.ts",
719
- "components/entities/furs-settings-form/locales/sl.ts",
720
- "components/entities/furs-settings-form/index.ts"
721
- ],
722
- "dependencies": ["ui/form", "ui/button", "ui/card", "ui/switch", "ui/alert", "form/form-input"],
723
- "providers": ["sdk-provider", "entities-provider"],
724
- "utils": ["translation", "hooks"]
725
- },
726
- "entities/fina-settings-form": {
727
- "name": "FINA Settings Form",
728
- "category": "feature",
729
- "files": [
730
- "components/entities/fina-settings-form/fina-settings-form.tsx",
731
- "components/entities/fina-settings-form/fina-settings.hooks.ts",
732
- "components/entities/fina-settings-form/sections/certificate-settings-section.tsx",
733
- "components/entities/fina-settings-form/sections/premises-management-section.tsx",
734
- "components/entities/fina-settings-form/sections/register-premise-dialog.tsx",
735
- "components/entities/fina-settings-form/locales/de.ts",
736
- "components/entities/fina-settings-form/locales/sl.ts",
737
- "components/entities/fina-settings-form/index.ts"
738
- ],
739
- "dependencies": ["ui/form", "ui/button", "ui/card", "ui/switch", "ui/alert", "form/form-input"],
740
- "providers": ["sdk-provider", "entities-provider"],
741
- "utils": ["translation", "hooks"]
742
- },
743
- "entities/entity-settings": {
744
- "name": "Entity Settings",
745
- "category": "feature",
746
- "files": [
747
- "components/entities/settings/company-settings-form.tsx",
748
- "components/entities/settings/email-settings-form.tsx",
749
- "components/entities/settings/branding-settings-form.tsx",
750
- "components/entities/settings/defaults-settings-form.tsx",
751
- "components/entities/settings/number-format-settings-form.tsx",
752
- "components/entities/settings/tax-rules-settings-form.tsx",
753
- "components/entities/settings/eslog-settings-form.tsx",
754
- "components/entities/settings/settings-footer.tsx",
755
- "components/entities/settings/index.ts",
756
- "components/entities/settings/pdf-template-selector/pdf-template-cards.tsx",
757
- "components/entities/settings/pdf-template-selector/demo-invoice-data.ts",
758
- "components/entities/settings/pdf-template-selector/index.ts"
759
- ],
760
- "dependencies": ["ui/form", "ui/button", "ui/card", "ui/select", "ui/switch", "ui/tabs", "form/form-input"],
761
- "providers": ["sdk-provider", "entities-provider"],
762
- "utils": ["translation", "hooks"]
763
562
  }
764
563
  }
765
564
  }
@@ -1,4 +1,4 @@
1
- import type { CreateAdvanceInvoice201, CreateAdvanceInvoiceBody } from "@spaceinvoices/js-sdk";
1
+ import type { AdvanceInvoice, CreateAdvanceInvoiceBody } from "@spaceinvoices/js-sdk";
2
2
  import { useMutation, useQueryClient } from "@tanstack/react-query";
3
3
  import { NEXT_DOCUMENT_NUMBER_CACHE_KEY } from "@/ui/hooks/use-next-document-number";
4
4
  import { useSDK } from "@/ui/providers/sdk-provider";
@@ -12,7 +12,7 @@ export const ADVANCE_INVOICES_CACHE_KEY = "advance-invoices";
12
12
 
13
13
  type UseCreateAdvanceInvoiceOptions = {
14
14
  entityId: string;
15
- onSuccess?: (data: CreateAdvanceInvoice201) => void;
15
+ onSuccess?: (data: AdvanceInvoice) => void;
16
16
  onError?: (error: unknown) => void;
17
17
  };
18
18
 
@@ -1,6 +1,5 @@
1
1
  import { useMutation, useQueryClient } from "@tanstack/react-query";
2
- import { AUTH_COOKIES } from "@/ui/lib/auth";
3
- import { getCookie } from "@/ui/lib/browser-cookies";
2
+ import { useSDK } from "@/ui/providers/sdk-provider";
4
3
 
5
4
  // Document type union for API calls
6
5
  export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
@@ -13,44 +12,6 @@ const CACHE_KEYS: Record<DocumentType, string> = {
13
12
  advance_invoice: "advance-invoices",
14
13
  };
15
14
 
16
- /**
17
- * Get API base URL from environment
18
- */
19
- function getApiBaseUrl(): string {
20
- if (typeof window === "undefined") return "";
21
- return (import.meta.env?.VITE_API_URL || import.meta.env?.BUN_PUBLIC_API_URL || "") as string;
22
- }
23
-
24
- /**
25
- * Make authenticated API request
26
- */
27
- async function apiRequest<T>(path: string, options: RequestInit & { entityId: string }): Promise<T> {
28
- const token = getCookie(AUTH_COOKIES.TOKEN);
29
- const baseUrl = getApiBaseUrl();
30
-
31
- const response = await fetch(`${baseUrl}${path}`, {
32
- ...options,
33
- headers: {
34
- "Content-Type": "application/json",
35
- Authorization: `Bearer ${token}`,
36
- "x-entity-id": options.entityId,
37
- ...options.headers,
38
- },
39
- });
40
-
41
- if (!response.ok) {
42
- const errorData = await response.json().catch(() => ({}));
43
- throw new Error(errorData.message || `Request failed with status ${response.status}`);
44
- }
45
-
46
- // DELETE returns 204 No Content
47
- if (response.status === 204) {
48
- return undefined as T;
49
- }
50
-
51
- return response.json();
52
- }
53
-
54
15
  // ============================================================================
55
16
  // Finalize Document Hook
56
17
  // ============================================================================
@@ -71,14 +32,12 @@ type FinalizeDocumentVariables = {
71
32
  * Assigns a document number and runs fiscalization (if applicable)
72
33
  */
73
34
  export function useFinalizeDocument(options: FinalizeDocumentOptions) {
35
+ const { sdk } = useSDK();
74
36
  const queryClient = useQueryClient();
75
37
 
76
38
  return useMutation({
77
39
  mutationFn: async ({ documentId, documentType }: FinalizeDocumentVariables) => {
78
- return apiRequest(`/documents/${documentId}/finalize?type=${documentType}`, {
79
- method: "POST",
80
- entityId: options.entityId,
81
- });
40
+ return sdk.documents.finalizeDocument(documentId, { type: documentType });
82
41
  },
83
42
  onSuccess: (data, variables) => {
84
43
  // Invalidate list cache
@@ -118,14 +77,12 @@ type DeleteDraftDocumentVariables = {
118
77
  * Only draft documents can be deleted
119
78
  */
120
79
  export function useDeleteDraftDocument(options: DeleteDraftDocumentOptions) {
80
+ const { sdk } = useSDK();
121
81
  const queryClient = useQueryClient();
122
82
 
123
83
  return useMutation({
124
84
  mutationFn: async ({ documentId, documentType }: DeleteDraftDocumentVariables) => {
125
- return apiRequest(`/documents/${documentId}?type=${documentType}`, {
126
- method: "DELETE",
127
- entityId: options.entityId,
128
- });
85
+ return sdk.documents.delete(documentId, { type: documentType });
129
86
  },
130
87
  onSuccess: (_, variables) => {
131
88
  // Invalidate list cache
@@ -63,6 +63,7 @@ export function DocumentPreviewDisplay({
63
63
 
64
64
  const { containerRef, contentRef, scale, contentHeight, A4_WIDTH_PX } = useA4Scaling(previewHtml);
65
65
 
66
+ // biome-ignore lint/correctness/useExhaustiveDependencies: document.updated_at intentionally triggers re-fetch when document changes server-side (e.g. after payment)
66
67
  useEffect(() => {
67
68
  const fetchPreview = async () => {
68
69
  // For public view, use per-type shareable HTML endpoint
@@ -113,7 +114,17 @@ export function DocumentPreviewDisplay({
113
114
  };
114
115
 
115
116
  fetchPreview();
116
- }, [document?.id, activeEntity?.id, template, apiBaseUrl, locale, isPublicView, shareableId, sdk]);
117
+ }, [
118
+ document?.id,
119
+ document?.updated_at,
120
+ activeEntity?.id,
121
+ template,
122
+ apiBaseUrl,
123
+ locale,
124
+ isPublicView,
125
+ shareableId,
126
+ sdk,
127
+ ]);
117
128
 
118
129
  return (
119
130
  <div ref={containerRef} className={cn("relative h-full", className)}>
@@ -42,13 +42,17 @@ const translations = { sl, de, it, fr, es, pt, nl, pl, hr } as const;
42
42
 
43
43
  type Document = Invoice | Estimate | CreditNote | AdvanceInvoice;
44
44
 
45
- const PDF_LOCALES = [
46
- { code: "en-US", label: "English" },
47
- { code: "de-DE", label: "Deutsch" },
48
- { code: "es-ES", label: "Español" },
49
- { code: "fr-FR", label: "Français" },
50
- { code: "it-IT", label: "Italiano" },
51
- { code: "sl-SI", label: "Slovenščina" },
45
+ const PDF_LOCALE_CODES = [
46
+ { label: "English", code: "en-US" },
47
+ { label: "German", code: "de-DE" },
48
+ { label: "Slovenian", code: "sl-SI" },
49
+ { label: "Italian", code: "it-IT" },
50
+ { label: "French", code: "fr-FR" },
51
+ { label: "Spanish", code: "es-ES" },
52
+ { label: "Portuguese", code: "pt-PT" },
53
+ { label: "Dutch", code: "nl-NL" },
54
+ { label: "Polish", code: "pl-PL" },
55
+ { label: "Croatian", code: "hr-HR" },
52
56
  ] as const;
53
57
 
54
58
  interface DocumentActionsBarProps extends ComponentTranslationProps {
@@ -89,11 +93,15 @@ interface DocumentActionsBarProps extends ComponentTranslationProps {
89
93
  function getApiLocale(uiLanguage: string): string {
90
94
  const localeMap: Record<string, string> = {
91
95
  en: "en-US",
92
- sl: "sl-SI",
93
96
  de: "de-DE",
94
- es: "es-ES",
95
- fr: "fr-FR",
97
+ sl: "sl-SI",
96
98
  it: "it-IT",
99
+ fr: "fr-FR",
100
+ es: "es-ES",
101
+ pt: "pt-PT",
102
+ nl: "nl-NL",
103
+ pl: "pl-PL",
104
+ hr: "hr-HR",
97
105
  };
98
106
  return localeMap[uiLanguage] || "en-US";
99
107
  }
@@ -185,13 +193,13 @@ export function DocumentActionsBar({
185
193
  </Button>
186
194
  </DropdownMenuTrigger>
187
195
  <DropdownMenuContent align="end">
188
- {PDF_LOCALES.map((locale) => (
196
+ {PDF_LOCALE_CODES.map((locale) => (
189
197
  <DropdownMenuItem
190
198
  key={locale.code}
191
199
  onClick={() => handleDownloadPdf(locale.code)}
192
200
  className="cursor-pointer"
193
201
  >
194
- {locale.label}
202
+ {t(locale.label)}
195
203
  </DropdownMenuItem>
196
204
  ))}
197
205
  </DropdownMenuContent>
@@ -0,0 +1,166 @@
1
+ import type { GetActivities200DataItem } from "@spaceinvoices/js-sdk";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { ChevronLeft, ChevronRight } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { Button } from "@/ui/components/ui/button";
6
+ import { Card, CardContent, CardHeader, CardTitle } from "@/ui/components/ui/card";
7
+ import { Skeleton } from "@/ui/components/ui/skeleton";
8
+ import type { ComponentTranslationProps } from "@/ui/lib/translation";
9
+ import { createTranslation } from "@/ui/lib/translation";
10
+ import { useSDK } from "@/ui/providers/sdk-provider";
11
+ import de from "./locales/de";
12
+ import es from "./locales/es";
13
+ import fr from "./locales/fr";
14
+ import hr from "./locales/hr";
15
+ import it from "./locales/it";
16
+ import nl from "./locales/nl";
17
+ import pl from "./locales/pl";
18
+ import pt from "./locales/pt";
19
+ import sl from "./locales/sl";
20
+
21
+ const translations = { sl, de, it, fr, es, pt, nl, pl, hr } as const;
22
+
23
+ const PAGE_SIZE = 5;
24
+
25
+ interface DocumentActivitiesListProps extends ComponentTranslationProps {
26
+ documentId: string;
27
+ entityId: string;
28
+ currentUserId?: string;
29
+ locale?: string;
30
+ }
31
+
32
+ function formatActivityDate(date: string, locale: string): string {
33
+ const d = new Date(date);
34
+ return new Intl.DateTimeFormat(locale, {
35
+ month: "short",
36
+ day: "numeric",
37
+ hour: "2-digit",
38
+ minute: "2-digit",
39
+ }).format(d);
40
+ }
41
+
42
+ function getActionLabel(action: string, t: (key: string) => string): string {
43
+ const labels: Record<string, string> = {
44
+ created: t("Created"),
45
+ updated: t("Updated"),
46
+ voided: t("Voided"),
47
+ sent: t("Sent"),
48
+ deleted: t("Deleted"),
49
+ };
50
+ return labels[action] || action;
51
+ }
52
+
53
+ function getActorLabel(activity: GetActivities200DataItem, t: (key: string) => string, currentUserId?: string): string {
54
+ if (currentUserId && activity.actor_id === currentUserId) return t("me");
55
+ if (activity.actor_label) return activity.actor_label;
56
+ const typeLabels: Record<string, string> = {
57
+ system: t("System"),
58
+ api_key: "API",
59
+ cron: t("Scheduled"),
60
+ webhook: "Webhook",
61
+ };
62
+ return typeLabels[activity.actor_type] || activity.actor_type;
63
+ }
64
+
65
+ export function DocumentActivitiesList({
66
+ documentId,
67
+ entityId,
68
+ currentUserId,
69
+ locale = "en",
70
+ ...i18nProps
71
+ }: DocumentActivitiesListProps) {
72
+ const t = createTranslation({ translations, locale, ...i18nProps });
73
+ const { sdk } = useSDK();
74
+
75
+ const [cursors, setCursors] = useState<string[]>([]);
76
+ const currentCursor = cursors.length > 0 ? cursors[cursors.length - 1] : undefined;
77
+
78
+ const { data: activitiesData, isLoading } = useQuery({
79
+ queryKey: ["activities", documentId, entityId, currentCursor],
80
+ queryFn: async () => {
81
+ if (!sdk) throw new Error("SDK not initialized");
82
+
83
+ return sdk.activities.list({
84
+ entity_id: entityId,
85
+ resource_id: documentId,
86
+ order_by: "-created_at",
87
+ limit: PAGE_SIZE,
88
+ next_cursor: currentCursor,
89
+ });
90
+ },
91
+ enabled: !!sdk && !!entityId && !!documentId,
92
+ });
93
+
94
+ const activities = activitiesData?.data || [];
95
+ const pagination = activitiesData?.pagination;
96
+
97
+ const handleNextPage = () => {
98
+ if (pagination?.next_cursor) {
99
+ setCursors((prev) => [...prev, pagination.next_cursor!]);
100
+ }
101
+ };
102
+
103
+ const handlePrevPage = () => {
104
+ setCursors((prev) => prev.slice(0, -1));
105
+ };
106
+
107
+ const hasPrev = cursors.length > 0;
108
+ const hasNext = !!pagination?.has_more;
109
+
110
+ return (
111
+ <Card>
112
+ <CardHeader className="flex flex-row items-center justify-between pb-3">
113
+ <CardTitle className="text-lg">
114
+ {t("Activity")} {pagination && pagination.total > 0 && `(${pagination.total})`}
115
+ </CardTitle>
116
+ {(hasPrev || hasNext) && (
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
+ )}
138
+ </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>
164
+ </Card>
165
+ );
166
+ }