@spaceinvoices/react-ui 0.3.0 → 0.4.1

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 (65) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/registry.json +0 -230
  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/entities/furs-settings-form/sections/premises-management-section.tsx +1 -0
  44. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +44 -32
  45. package/src/components/invoices/index.ts +1 -1
  46. package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +2 -2
  47. package/src/components/invoices/view/fiscalization-status-card.tsx +121 -0
  48. package/src/generate-schemas.ts +13 -1
  49. package/src/generated/schemas/advanceinvoice.ts +79 -187
  50. package/src/generated/schemas/creditnote.ts +63 -86
  51. package/src/generated/schemas/customadvanceinvoice.ts +70 -97
  52. package/src/generated/schemas/customcreditnote.ts +70 -97
  53. package/src/generated/schemas/customestimate.ts +68 -97
  54. package/src/generated/schemas/custominvoice.ts +70 -97
  55. package/src/generated/schemas/estimate.ts +67 -172
  56. package/src/generated/schemas/invoice.ts +79 -187
  57. package/src/generated/schemas/registerfursrealestatepremise_body.ts +11 -7
  58. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +61 -157
  59. package/src/generated/schemas/rendercreditnotepreview_body.ts +61 -157
  60. package/src/generated/schemas/renderestimatepreview_body.ts +61 -157
  61. package/src/generated/schemas/renderinvoicepreview_body.ts +61 -157
  62. package/src/hooks/use-duplicate-document.ts +19 -11
  63. package/src/providers/entities-provider.tsx +21 -0
  64. package/src/components/invoices/view/fina-info-display.tsx +0 -196
  65. 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.1");
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.1",
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
@@ -530,236 +530,6 @@
530
530
  "dependencies": ["ui/card", "dashboard/loading-card"],
531
531
  "providers": ["sdk-provider"],
532
532
  "utils": ["translation"]
533
- },
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",
584
- "category": "feature",
585
- "files": [
586
- "components/activities/activity-timeline.tsx",
587
- "components/activities/locales/de.ts",
588
- "components/activities/locales/sl.ts",
589
- "components/activities/index.ts"
590
- ],
591
- "dependencies": ["ui/badge"],
592
- "providers": ["sdk-provider"],
593
- "utils": ["translation"]
594
- },
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",
619
- "category": "feature",
620
- "files": [
621
- "components/company-registry/company-registry-autocomplete.tsx",
622
- "components/company-registry/company-registry.hooks.ts",
623
- "components/company-registry/index.ts"
624
- ],
625
- "dependencies": ["ui/command", "ui/popover", "ui/button"],
626
- "providers": ["sdk-provider"],
627
- "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
533
  }
764
534
  }
765
535
  }
@@ -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
+ }
@@ -112,15 +112,15 @@ export function DocumentDetailsCard({ document, documentType, locale = "en", ...
112
112
  {/* Document info */}
113
113
  <div className="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
114
114
  <div className="text-muted-foreground">{t("Number")}</div>
115
- <div className="font-medium">{document.number}</div>
115
+ <div className="text-right font-medium">{document.number}</div>
116
116
 
117
117
  <div className="text-muted-foreground">{t("Date")}</div>
118
- <div>{fmtDate(document.date)}</div>
118
+ <div className="text-right">{fmtDate(document.date)}</div>
119
119
 
120
120
  {isInvoiceOrAdvance && (
121
121
  <>
122
122
  <div className="text-muted-foreground">{t("Due date")}</div>
123
- <div>{fmtDate(invoiceDoc.date_due)}</div>
123
+ <div className="text-right">{fmtDate(invoiceDoc.date_due)}</div>
124
124
  </>
125
125
  )}
126
126
 
@@ -129,7 +129,7 @@ export function DocumentDetailsCard({ document, documentType, locale = "en", ...
129
129
  <div className="text-muted-foreground">
130
130
  {(invoiceDoc as any).date_service_to ? t("Service period") : t("Service date")}
131
131
  </div>
132
- <div>
132
+ <div className="text-right">
133
133
  {(invoiceDoc as any).date_service_to
134
134
  ? `${fmtDate((invoiceDoc as any).date_service)} - ${fmtDate((invoiceDoc as any).date_service_to)}`
135
135
  : fmtDate((invoiceDoc as any).date_service)}
@@ -140,12 +140,12 @@ export function DocumentDetailsCard({ document, documentType, locale = "en", ...
140
140
  {isEstimate && estimateDoc.date_valid_till && (
141
141
  <>
142
142
  <div className="text-muted-foreground">{t("Valid until")}</div>
143
- <div>{fmtDate(estimateDoc.date_valid_till)}</div>
143
+ <div className="text-right">{fmtDate(estimateDoc.date_valid_till)}</div>
144
144
  </>
145
145
  )}
146
146
 
147
147
  <div className="text-muted-foreground">{t("Customer")}</div>
148
- <div>{customerName}</div>
148
+ <div className="text-right">{customerName}</div>
149
149
  </div>
150
150
 
151
151
  <Separator />
@@ -1,4 +1,5 @@
1
1
  export { DocumentActionsBar } from "./document-actions-bar";
2
+ export { DocumentActivitiesList } from "./document-activities-list";
2
3
  export { DocumentDetailsCard } from "./document-details-card";
3
4
  export { DocumentPaymentsList } from "./document-payments-list";
4
5
  export { useDocumentDownload } from "./use-document-download";