@spaceinvoices/react-ui 0.4.3 → 0.4.4

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