@spaceinvoices/react-ui 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) 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 +61 -12
  5. package/src/components/advance-invoices/create/locales/de.ts +17 -0
  6. package/src/components/advance-invoices/create/locales/es.ts +17 -0
  7. package/src/components/advance-invoices/create/locales/fr.ts +17 -0
  8. package/src/components/advance-invoices/create/locales/hr.ts +16 -0
  9. package/src/components/advance-invoices/create/locales/it.ts +17 -0
  10. package/src/components/advance-invoices/create/locales/nl.ts +17 -0
  11. package/src/components/advance-invoices/create/locales/pl.ts +16 -0
  12. package/src/components/advance-invoices/create/locales/pt.ts +16 -0
  13. package/src/components/advance-invoices/create/locales/sl.ts +16 -0
  14. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +0 -1
  15. package/src/components/advance-invoices/list/list-row-actions.tsx +48 -1
  16. package/src/components/advance-invoices/list/list-table.tsx +100 -50
  17. package/src/components/advance-invoices/list/locales/de.ts +9 -0
  18. package/src/components/advance-invoices/list/locales/en.ts +7 -0
  19. package/src/components/advance-invoices/list/locales/es.ts +9 -0
  20. package/src/components/advance-invoices/list/locales/fr.ts +9 -0
  21. package/src/components/advance-invoices/list/locales/hr.ts +8 -0
  22. package/src/components/advance-invoices/list/locales/it.ts +9 -0
  23. package/src/components/advance-invoices/list/locales/nl.ts +9 -0
  24. package/src/components/advance-invoices/list/locales/pl.ts +8 -0
  25. package/src/components/advance-invoices/list/locales/pt.ts +9 -0
  26. package/src/components/advance-invoices/list/locales/sl.ts +8 -0
  27. package/src/components/credit-notes/create/create-credit-note-form.tsx +185 -11
  28. package/src/components/credit-notes/create/locales/de.ts +18 -0
  29. package/src/components/credit-notes/create/locales/es.ts +18 -0
  30. package/src/components/credit-notes/create/locales/fr.ts +18 -0
  31. package/src/components/credit-notes/create/locales/hr.ts +17 -0
  32. package/src/components/credit-notes/create/locales/it.ts +18 -0
  33. package/src/components/credit-notes/create/locales/nl.ts +18 -0
  34. package/src/components/credit-notes/create/locales/pl.ts +17 -0
  35. package/src/components/credit-notes/create/locales/pt.ts +17 -0
  36. package/src/components/credit-notes/create/locales/sl.ts +18 -1
  37. package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
  38. package/src/components/credit-notes/list/list-row-actions.tsx +44 -1
  39. package/src/components/credit-notes/list/list-table.tsx +93 -32
  40. package/src/components/credit-notes/list/locales/de.ts +7 -0
  41. package/src/components/credit-notes/list/locales/en.ts +6 -0
  42. package/src/components/credit-notes/list/locales/es.ts +7 -0
  43. package/src/components/credit-notes/list/locales/fr.ts +7 -0
  44. package/src/components/credit-notes/list/locales/hr.ts +7 -0
  45. package/src/components/credit-notes/list/locales/it.ts +7 -0
  46. package/src/components/credit-notes/list/locales/nl.ts +7 -0
  47. package/src/components/credit-notes/list/locales/pl.ts +7 -0
  48. package/src/components/credit-notes/list/locales/pt.ts +7 -0
  49. package/src/components/credit-notes/list/locales/sl.ts +7 -0
  50. package/src/components/customers/customer-list-table/customer-list-table.tsx +0 -3
  51. package/src/components/customers/customer-list-table/locales/de.ts +1 -0
  52. package/src/components/customers/customer-list-table/locales/es.ts +1 -0
  53. package/src/components/customers/customer-list-table/locales/fr.ts +1 -0
  54. package/src/components/customers/customer-list-table/locales/hr.ts +1 -0
  55. package/src/components/customers/customer-list-table/locales/it.ts +1 -0
  56. package/src/components/customers/customer-list-table/locales/nl.ts +1 -0
  57. package/src/components/customers/customer-list-table/locales/pl.ts +1 -0
  58. package/src/components/customers/customer-list-table/locales/pt.ts +1 -0
  59. package/src/components/customers/customer-list-table/locales/sl.ts +1 -0
  60. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -9
  61. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +77 -48
  62. package/src/components/dashboard/shared/use-revenue-data.ts +77 -9
  63. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +67 -7
  64. package/src/components/delivery-notes/create/locales/de.ts +16 -0
  65. package/src/components/delivery-notes/create/locales/es.ts +16 -0
  66. package/src/components/delivery-notes/create/locales/fr.ts +16 -0
  67. package/src/components/delivery-notes/create/locales/hr.ts +16 -0
  68. package/src/components/delivery-notes/create/locales/it.ts +16 -0
  69. package/src/components/delivery-notes/create/locales/nl.ts +16 -0
  70. package/src/components/delivery-notes/create/locales/pl.ts +16 -0
  71. package/src/components/delivery-notes/create/locales/pt.ts +16 -0
  72. package/src/components/delivery-notes/create/locales/sl.ts +17 -1
  73. package/src/components/delivery-notes/list/list-row-actions.tsx +20 -1
  74. package/src/components/delivery-notes/list/list-table.tsx +50 -8
  75. package/src/components/delivery-notes/list/locales/de.ts +35 -0
  76. package/src/components/delivery-notes/list/locales/en.ts +33 -0
  77. package/src/components/delivery-notes/list/locales/es.ts +35 -0
  78. package/src/components/delivery-notes/list/locales/fr.ts +35 -0
  79. package/src/components/delivery-notes/list/locales/hr.ts +35 -0
  80. package/src/components/delivery-notes/list/locales/it.ts +35 -0
  81. package/src/components/delivery-notes/list/locales/nl.ts +35 -0
  82. package/src/components/delivery-notes/list/locales/pl.ts +35 -0
  83. package/src/components/delivery-notes/list/locales/pt.ts +35 -0
  84. package/src/components/delivery-notes/list/locales/sl.ts +35 -0
  85. package/src/components/documents/create/document-add-item-form.tsx +70 -0
  86. package/src/components/documents/create/document-details-section.tsx +163 -29
  87. package/src/components/documents/create/document-items-section.tsx +21 -4
  88. package/src/components/documents/create/linked-documents-info.tsx +82 -0
  89. package/src/components/documents/create/live-preview.tsx +25 -5
  90. package/src/components/documents/create/mark-as-paid-section.tsx +29 -20
  91. package/src/components/documents/create/prepare-document-submission.ts +19 -7
  92. package/src/components/documents/shared/document-preview-display.tsx +3 -4
  93. package/src/components/documents/types.ts +2 -2
  94. package/src/components/documents/view/document-actions-bar.tsx +7 -27
  95. package/src/components/documents/view/document-activities-list.tsx +65 -47
  96. package/src/components/documents/view/document-details-card.tsx +118 -76
  97. package/src/components/documents/view/document-payments-list.tsx +99 -65
  98. package/src/components/documents/view/document-relations-list.tsx +43 -28
  99. package/src/components/documents/view/document-sidebar.tsx +151 -0
  100. package/src/components/documents/view/index.ts +2 -0
  101. package/src/components/documents/view/locales/de.ts +4 -0
  102. package/src/components/documents/view/locales/es.ts +4 -0
  103. package/src/components/documents/view/locales/fr.ts +4 -0
  104. package/src/components/documents/view/locales/hr.ts +4 -0
  105. package/src/components/documents/view/locales/it.ts +4 -0
  106. package/src/components/documents/view/locales/nl.ts +4 -0
  107. package/src/components/documents/view/locales/pl.ts +4 -0
  108. package/src/components/documents/view/locales/pt.ts +4 -0
  109. package/src/components/documents/view/locales/sl.ts +5 -1
  110. package/src/components/documents/view/use-document-download.ts +7 -3
  111. package/src/components/entities/create-entity-form.tsx +166 -13
  112. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +101 -1
  113. package/src/components/entities/entity-settings-form/input-with-preview.tsx +30 -2
  114. package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
  115. package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
  116. package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
  117. package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
  118. package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
  119. package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
  120. package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
  121. package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
  122. package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
  123. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +6 -6
  124. package/src/components/entities/fina-settings-form/locales/de.ts +2 -2
  125. package/src/components/entities/fina-settings-form/locales/en.ts +2 -2
  126. package/src/components/entities/fina-settings-form/locales/es.ts +2 -2
  127. package/src/components/entities/fina-settings-form/locales/fr.ts +2 -2
  128. package/src/components/entities/fina-settings-form/locales/hr.ts +2 -2
  129. package/src/components/entities/fina-settings-form/locales/it.ts +2 -2
  130. package/src/components/entities/fina-settings-form/locales/nl.ts +2 -2
  131. package/src/components/entities/fina-settings-form/locales/pl.ts +2 -2
  132. package/src/components/entities/fina-settings-form/locales/pt.ts +2 -2
  133. package/src/components/entities/fina-settings-form/locales/sl.ts +2 -2
  134. package/src/components/entities/furs-settings-form/locales/en.ts +0 -1
  135. package/src/components/entities/settings/company-settings-form.tsx +173 -20
  136. package/src/components/entities/settings/pdf-template-selector/locales/de.ts +3 -1
  137. package/src/components/entities/settings/pdf-template-selector/locales/es.ts +3 -1
  138. package/src/components/entities/settings/pdf-template-selector/locales/fr.ts +4 -1
  139. package/src/components/entities/settings/pdf-template-selector/locales/hr.ts +4 -1
  140. package/src/components/entities/settings/pdf-template-selector/locales/it.ts +3 -1
  141. package/src/components/entities/settings/pdf-template-selector/locales/nl.ts +3 -1
  142. package/src/components/entities/settings/pdf-template-selector/locales/pl.ts +3 -1
  143. package/src/components/entities/settings/pdf-template-selector/locales/pt.ts +3 -1
  144. package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +3 -1
  145. package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +1 -0
  146. package/src/components/estimates/create/create-estimate-form.tsx +93 -8
  147. package/src/components/estimates/create/locales/de.ts +16 -0
  148. package/src/components/estimates/create/locales/es.ts +16 -0
  149. package/src/components/estimates/create/locales/fr.ts +16 -0
  150. package/src/components/estimates/create/locales/hr.ts +16 -0
  151. package/src/components/estimates/create/locales/it.ts +16 -0
  152. package/src/components/estimates/create/locales/nl.ts +16 -0
  153. package/src/components/estimates/create/locales/pl.ts +16 -0
  154. package/src/components/estimates/create/locales/pt.ts +16 -0
  155. package/src/components/estimates/create/locales/sl.ts +17 -1
  156. package/src/components/estimates/list/list-table.tsx +14 -8
  157. package/src/components/estimates/list/locales/de.ts +2 -0
  158. package/src/components/estimates/list/locales/en.ts +1 -0
  159. package/src/components/estimates/list/locales/es.ts +2 -0
  160. package/src/components/estimates/list/locales/fr.ts +2 -0
  161. package/src/components/estimates/list/locales/hr.ts +2 -0
  162. package/src/components/estimates/list/locales/it.ts +2 -0
  163. package/src/components/estimates/list/locales/nl.ts +2 -0
  164. package/src/components/estimates/list/locales/pl.ts +2 -0
  165. package/src/components/estimates/list/locales/pt.ts +2 -0
  166. package/src/components/estimates/list/locales/sl.ts +2 -0
  167. package/src/components/export/document-export-form.tsx +55 -14
  168. package/src/components/export/index.ts +2 -0
  169. package/src/components/export/sales-per-item-export-form.tsx +223 -0
  170. package/src/components/invoices/create/create-invoice-form.tsx +106 -11
  171. package/src/components/invoices/create/locales/de.ts +26 -0
  172. package/src/components/invoices/create/locales/es.ts +26 -0
  173. package/src/components/invoices/create/locales/fr.ts +26 -0
  174. package/src/components/invoices/create/locales/hr.ts +25 -0
  175. package/src/components/invoices/create/locales/it.ts +26 -0
  176. package/src/components/invoices/create/locales/nl.ts +26 -0
  177. package/src/components/invoices/create/locales/pl.ts +25 -0
  178. package/src/components/invoices/create/locales/pt.ts +25 -0
  179. package/src/components/invoices/create/locales/sl.ts +26 -1
  180. package/src/components/invoices/invoices-furs.hooks.ts +4 -1
  181. package/src/components/invoices/list/list-table.tsx +77 -31
  182. package/src/components/invoices/list/locales/de.ts +5 -0
  183. package/src/components/invoices/list/locales/en.ts +4 -0
  184. package/src/components/invoices/list/locales/es.ts +5 -0
  185. package/src/components/invoices/list/locales/fr.ts +5 -0
  186. package/src/components/invoices/list/locales/hr.ts +5 -0
  187. package/src/components/invoices/list/locales/it.ts +5 -0
  188. package/src/components/invoices/list/locales/nl.ts +5 -0
  189. package/src/components/invoices/list/locales/pl.ts +5 -0
  190. package/src/components/invoices/list/locales/pt.ts +5 -0
  191. package/src/components/invoices/list/locales/sl.ts +5 -0
  192. package/src/components/invoices/view/fiscalization-status-card.tsx +44 -24
  193. package/src/components/items/item-combobox.tsx +5 -3
  194. package/src/components/items/item-list-table/item-list-header.tsx +4 -17
  195. package/src/components/items/item-list-table/item-list-table.tsx +3 -3
  196. package/src/components/items/item-list-table/locales/de.ts +1 -0
  197. package/src/components/items/item-list-table/locales/es.ts +1 -0
  198. package/src/components/items/item-list-table/locales/fr.ts +1 -0
  199. package/src/components/items/item-list-table/locales/hr.ts +1 -0
  200. package/src/components/items/item-list-table/locales/it.ts +1 -0
  201. package/src/components/items/item-list-table/locales/nl.ts +1 -0
  202. package/src/components/items/item-list-table/locales/pl.ts +1 -0
  203. package/src/components/items/item-list-table/locales/pt.ts +1 -0
  204. package/src/components/items/item-list-table/locales/sl.ts +1 -0
  205. package/src/components/payments/list/list-table.tsx +0 -4
  206. package/src/components/payments/list/locales/de.ts +1 -0
  207. package/src/components/payments/list/locales/es.ts +1 -0
  208. package/src/components/payments/list/locales/fr.ts +1 -0
  209. package/src/components/payments/list/locales/hr.ts +1 -0
  210. package/src/components/payments/list/locales/it.ts +1 -0
  211. package/src/components/payments/list/locales/nl.ts +1 -0
  212. package/src/components/payments/list/locales/pl.ts +1 -0
  213. package/src/components/payments/list/locales/pt.ts +1 -0
  214. package/src/components/payments/list/locales/sl.ts +1 -0
  215. package/src/components/recurring-invoices/list/list-table.tsx +0 -7
  216. package/src/components/recurring-invoices/list/locales/de.ts +1 -0
  217. package/src/components/recurring-invoices/list/locales/es.ts +1 -0
  218. package/src/components/recurring-invoices/list/locales/fr.ts +1 -0
  219. package/src/components/recurring-invoices/list/locales/hr.ts +1 -0
  220. package/src/components/recurring-invoices/list/locales/it.ts +1 -0
  221. package/src/components/recurring-invoices/list/locales/nl.ts +1 -0
  222. package/src/components/recurring-invoices/list/locales/pl.ts +1 -0
  223. package/src/components/recurring-invoices/list/locales/pt.ts +1 -0
  224. package/src/components/recurring-invoices/list/locales/sl.ts +1 -0
  225. package/src/components/request-logs/request-log-list-table.tsx +0 -3
  226. package/src/components/table/README.md +14 -121
  227. package/src/components/table/data-table.tsx +22 -37
  228. package/src/components/table/hooks/use-table-state.ts +3 -27
  229. package/src/components/table/index.ts +0 -2
  230. package/src/components/table/search-input.tsx +17 -0
  231. package/src/components/table/selection-toolbar.tsx +35 -1
  232. package/src/components/table/table-empty-state.tsx +6 -3
  233. package/src/components/table/table-no-results.tsx +10 -5
  234. package/src/components/table/types.ts +0 -5
  235. package/src/components/taxes/tax-list-table/locales/de.ts +1 -0
  236. package/src/components/taxes/tax-list-table/locales/es.ts +1 -0
  237. package/src/components/taxes/tax-list-table/locales/fr.ts +1 -0
  238. package/src/components/taxes/tax-list-table/locales/hr.ts +1 -0
  239. package/src/components/taxes/tax-list-table/locales/it.ts +1 -0
  240. package/src/components/taxes/tax-list-table/locales/nl.ts +1 -0
  241. package/src/components/taxes/tax-list-table/locales/pl.ts +1 -0
  242. package/src/components/taxes/tax-list-table/locales/pt.ts +1 -0
  243. package/src/components/taxes/tax-list-table/locales/sl.ts +1 -0
  244. package/src/components/taxes/tax-list-table/tax-list-header.tsx +3 -14
  245. package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -3
  246. package/src/components/ui/popover.tsx +3 -1
  247. package/src/components/ui/tooltip.tsx +3 -1
  248. package/src/generated/schemas/advanceinvoice.ts +4 -2
  249. package/src/generated/schemas/creditnote.ts +2 -1
  250. package/src/generated/schemas/deliverynote.ts +2 -1
  251. package/src/generated/schemas/estimate.ts +4 -2
  252. package/src/generated/schemas/index.ts +1 -1
  253. package/src/generated/schemas/invoice.ts +2 -1
  254. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -23
  255. package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -23
  256. package/src/generated/schemas/renderdeliverynotepreview_body.ts +17 -23
  257. package/src/generated/schemas/renderestimatepreview_body.ts +17 -23
  258. package/src/generated/schemas/renderinvoicepreview_body.ts +19 -23
  259. package/src/hooks/use-duplicate-document.ts +56 -14
  260. package/src/hooks/use-vies-check.ts +3 -0
  261. package/src/lib/fiscalization.ts +12 -0
  262. package/src/lib/schemas/advance-invoice.ts +0 -1
  263. package/src/components/table/sortable-header.tsx +0 -56
@@ -1,6 +1,6 @@
1
1
  # Table Component Library
2
2
 
3
- A comprehensive, type-safe table system with built-in sorting, search, pagination, and loading states.
3
+ A comprehensive, type-safe table system with built-in search, pagination, and loading states.
4
4
 
5
5
  ## Features
6
6
 
@@ -32,7 +32,6 @@ function InvoiceTable() {
32
32
  {
33
33
  id: "number",
34
34
  header: "Invoice #",
35
- sortable: true,
36
35
  cell: (invoice) => (
37
36
  <a href={`/invoices/${invoice.id}`} className="underline">
38
37
  {invoice.number}
@@ -47,14 +46,12 @@ function InvoiceTable() {
47
46
  {
48
47
  id: "date",
49
48
  header: "Date",
50
- sortable: true,
51
49
  cell: (invoice) => <FormattedDate date={invoice.date} />,
52
50
  },
53
51
  {
54
52
  id: "total",
55
53
  header: "Total",
56
54
  align: "right",
57
- sortable: true,
58
55
  cell: (invoice) => `$${invoice.total}`,
59
56
  },
60
57
  ]}
@@ -81,17 +78,12 @@ function InvoiceTable() {
81
78
  return (
82
79
  <DataTable<Invoice>
83
80
  columns={[
84
- { field: "number", header: "Number", sortable: true },
85
- { field: "customer", header: "Customer" },
86
- { field: "date", header: "Date", sortable: true },
87
- { field: "total", header: "Total", sortable: true, align: "right" },
81
+ { id: "number", header: "Number" },
82
+ { id: "customer", header: "Customer" },
83
+ { id: "date", header: "Date" },
84
+ { id: "total", header: "Total", align: "right" },
88
85
  ]}
89
- renderHeader={(props) => (
90
- <InvoiceListHeader
91
- orderBy={props.orderBy}
92
- onSort={props.onSort}
93
- />
94
- )}
86
+ renderHeader={() => <InvoiceListHeader />}
95
87
  renderRow={(invoice) => (
96
88
  <InvoiceListRow
97
89
  key={invoice.id}
@@ -117,7 +109,6 @@ function InvoiceTable() {
117
109
  | `cacheKey` | `string` | Yes | Unique key for react-query cache |
118
110
  | `onFetch` | `(params) => Promise<Response>` | Yes | Data fetch function |
119
111
  | `resourceName` | `string` | Yes | Resource name for empty states |
120
- | `defaultOrderBy` | `string` | No | Default sort order (e.g., "-id") |
121
112
  | `queryParams` | `TableQueryParams` | No | External query parameters |
122
113
  | `onChangeParams` | `(params) => void` | No | Callback for param changes |
123
114
  | `entityId` | `string` | No | Entity ID for multi-tenant filtering |
@@ -125,7 +116,7 @@ function InvoiceTable() {
125
116
  | `createNewTrigger` | `ReactNode` | No | Custom create action |
126
117
  | `onRowClick` | `(item: T) => void` | No | Row click handler |
127
118
  | `renderRow` | `(item: T) => ReactNode` | No | Custom row renderer |
128
- | `renderHeader` | `(props) => ReactNode` | No | Custom header renderer |
119
+ | `renderHeader` | `() => ReactNode` | No | Custom header renderer |
129
120
 
130
121
  ### Column Definition
131
122
 
@@ -133,8 +124,6 @@ function InvoiceTable() {
133
124
  type Column<T> = {
134
125
  id: string; // Unique column identifier
135
126
  header: ReactNode; // Header label or component
136
- sortField?: string; // Field name for sorting (if different from id)
137
- sortable?: boolean; // Enable sorting
138
127
  align?: "left" | "center" | "right"; // Text alignment
139
128
  cell?: (item: T) => ReactNode; // Cell renderer function
140
129
  className?: string; // Optional CSS classes
@@ -150,9 +139,8 @@ Manages table state internally with optional URL sync:
150
139
  ```tsx
151
140
  import { useTableState } from "@space-invoices/ui";
152
141
 
153
- const { params, handleSort, handleSearch, handlePageChange } = useTableState({
154
- initialParams: { order_by: "-created_at" },
155
- defaultOrderBy: "-id",
142
+ const { params, handleSearch, handlePageChange } = useTableState({
143
+ initialParams: {},
156
144
  onChangeParams: (params) => {
157
145
  // Optional: sync with router or external state
158
146
  navigate({ search: params });
@@ -170,7 +158,7 @@ import { useTableQuery } from "@space-invoices/ui";
170
158
  const { data, isFetching } = useTableQuery({
171
159
  cacheKey: "customers",
172
160
  fetchFn: (params) => sdk.customers.getCustomers(params),
173
- params: { order_by: "-id", search: "acme" },
161
+ params: { search: "acme" },
174
162
  entityId: "entity-123",
175
163
  });
176
164
  ```
@@ -203,21 +191,6 @@ Search input with optional debouncing:
203
191
  />
204
192
  ```
205
193
 
206
- ### SortableHeader
207
-
208
- Sortable column header with visual indicators:
209
-
210
- ```tsx
211
- <SortableHeader
212
- field="name"
213
- currentOrder="-name"
214
- onSort={(order) => console.log(order)}
215
- align="left"
216
- >
217
- Customer Name
218
- </SortableHeader>
219
- ```
220
-
221
194
  ### Pagination
222
195
 
223
196
  Cursor-based pagination controls:
@@ -259,7 +232,6 @@ For straightforward tables, define columns with cell renderers:
259
232
  {
260
233
  id: "name",
261
234
  header: "Name",
262
- sortable: true,
263
235
  cell: (item) => item.name,
264
236
  },
265
237
  {
@@ -281,14 +253,14 @@ For advanced tables with complex row/header components:
281
253
  ```tsx
282
254
  <DataTable
283
255
  columns={[
284
- { id: "name", header: "Name", sortable: true },
256
+ { id: "name", header: "Name" },
285
257
  { id: "email", header: "Email" },
286
258
  ]}
287
259
  renderRow={(item) => (
288
260
  <CustomRow key={item.id} item={item} />
289
261
  )}
290
- renderHeader={(props) => (
291
- <CustomHeader {...props} />
262
+ renderHeader={() => (
263
+ <CustomHeader />
292
264
  )}
293
265
  cacheKey="users"
294
266
  resourceName="user"
@@ -296,84 +268,6 @@ For advanced tables with complex row/header components:
296
268
  />
297
269
  ```
298
270
 
299
- ## Key Improvements
300
-
301
- ### 1. Simplified State Management
302
-
303
- **Before:**
304
- - Manual `useState` for params
305
- - Manual URL sync with `updateQueryParams`
306
- - Complex callback chains
307
-
308
- **After:**
309
- - `useTableState` hook handles everything
310
- - Automatic URL sync
311
- - Single source of truth
312
-
313
- ### 2. Cleaner Hooks
314
-
315
- **Before:**
316
- ```tsx
317
- const { data, isFetching } = useTableQuery({
318
- cacheKey,
319
- fetchFn,
320
- params,
321
- entityId,
322
- });
323
-
324
- // Unnecessary isMounted ref logic
325
- const isMounted = useRef(true);
326
- ```
327
-
328
- **After:**
329
- ```tsx
330
- // Simplified, no ref needed
331
- const { data, isFetching } = useTableQuery({
332
- cacheKey,
333
- fetchFn,
334
- params,
335
- entityId,
336
- });
337
- ```
338
-
339
- ### 3. Better Column API
340
-
341
- **Before:**
342
- - Column definitions not used for rendering
343
- - Must implement custom row/header components
344
-
345
- **After:**
346
- - Column definitions drive rendering
347
- - Optional custom renderers for flexibility
348
- - Built-in cell renderers via `cell` prop
349
-
350
- ### 4. Improved Type Safety
351
-
352
- **Before:**
353
- ```typescript
354
- // Loose typing on columns
355
- columns: { field: string; header: React.ReactNode }[]
356
- ```
357
-
358
- **After:**
359
- ```typescript
360
- // Strongly typed with generics
361
- columns: Column<T>[]
362
- // T is your data type (e.g., Invoice, Customer)
363
- ```
364
-
365
- ### 5. Better UX
366
-
367
- **Before:**
368
- - Basic search input
369
- - Minimal empty states
370
-
371
- **After:**
372
- - Search with clear button
373
- - Rich empty states with icons
374
- - Better error handling in date formatter
375
- - Improved accessibility
376
-
377
271
  ## Best Practices
378
272
 
379
273
  1. **Use column definitions for simple tables**: Less code, easier to maintain
@@ -382,8 +276,7 @@ columns: Column<T>[]
382
276
  4. **Handle loading states**: The component handles this automatically
383
277
  5. **Provide meaningful resource names**: Used in empty states
384
278
  6. **Use FormattedDate**: Consistent date formatting with error handling
385
- 7. **Enable sorting selectively**: Not all columns need sorting
386
- 8. **Use proper TypeScript types**: Import from `@spaceinvoices/js-sdk`
279
+ 7. **Use proper TypeScript types**: Import from `@spaceinvoices/js-sdk`
387
280
 
388
281
  ## Testing
389
282
 
@@ -6,7 +6,6 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
6
6
  import { FilterBar } from "./filter-bar";
7
7
  import { useTableQuery } from "./hooks/use-table-query";
8
8
  import { useTableState } from "./hooks/use-table-state";
9
- import { SortableHeader } from "./sortable-header";
10
9
  import { TableEmptyState } from "./table-empty-state";
11
10
  import { TableNoResults } from "./table-no-results";
12
11
  import { Pagination } from "./table-pagination";
@@ -22,8 +21,6 @@ export type DataTableProps<T> = {
22
21
  onFetch: (params: TableQueryParams) => Promise<TableQueryResponse<T>>;
23
22
  /** Resource name for empty states (e.g., "customer", "invoice") */
24
23
  resourceName: string;
25
- /** Default sort order (e.g., "-id", "name") */
26
- defaultOrderBy?: string;
27
24
  /** Initial/external query parameters */
28
25
  queryParams?: TableQueryParams;
29
26
  /** Callback when params change (for external state management) */
@@ -36,12 +33,14 @@ export type DataTableProps<T> = {
36
33
  createNewLink?: string;
37
34
  /** Custom trigger for create action */
38
35
  createNewTrigger?: ReactNode;
36
+ /** Callback for "Create new" click — enables client-side navigation instead of full page reload */
37
+ onCreateNew?: () => void;
39
38
  /** Optional row click handler */
40
39
  onRowClick?: (item: T) => void;
41
40
  /** Custom row renderer (overrides default cell rendering) */
42
41
  renderRow?: (item: T) => ReactNode;
43
42
  /** Custom header renderer (overrides default header) */
44
- renderHeader?: (props: { orderBy?: string; onSort?: (order: string | null) => void }) => ReactNode;
43
+ renderHeader?: () => ReactNode;
45
44
  /** Filter configuration (date fields, status filter) */
46
45
  filterConfig?: FilterConfig;
47
46
  /** Translation function */
@@ -55,7 +54,7 @@ export type DataTableProps<T> = {
55
54
  /** Callback when selection changes */
56
55
  onSelectionChange?: (selectedIds: Set<string>) => void;
57
56
  /** Content to render in selection toolbar (shown when items selected) */
58
- selectionToolbar?: (selectedCount: number) => ReactNode;
57
+ selectionToolbar?: (selectedCount: number, data: T[]) => ReactNode;
59
58
  };
60
59
 
61
60
  /**
@@ -66,13 +65,13 @@ export function DataTable<T extends { id: string }>({
66
65
  cacheKey,
67
66
  onFetch,
68
67
  resourceName,
69
- defaultOrderBy = "-id",
70
68
  queryParams,
71
69
  onChangeParams,
72
70
  disableUrlSync,
73
71
  entityId,
74
72
  createNewLink,
75
73
  createNewTrigger,
74
+ onCreateNew,
76
75
  onRowClick,
77
76
  renderRow,
78
77
  renderHeader,
@@ -94,14 +93,12 @@ export function DataTable<T extends { id: string }>({
94
93
  );
95
94
  const [filterPanelOpen, setFilterPanelOpen] = useState(hasInitialFilters);
96
95
 
97
- // Manage table state (sort, search, pagination, filters)
98
- const { params, apiParams, filterState, handleSort, handleSearch, handlePageChange, handleFilterChange } =
99
- useTableState({
100
- initialParams: queryParams,
101
- defaultOrderBy,
102
- onChangeParams,
103
- disableUrlSync,
104
- });
96
+ // Manage table state (search, pagination, filters)
97
+ const { params, apiParams, filterState, handleSearch, handlePageChange, handleFilterChange } = useTableState({
98
+ initialParams: queryParams,
99
+ onChangeParams,
100
+ disableUrlSync,
101
+ });
105
102
 
106
103
  // Fetch table data (use apiParams which has the query JSON for API)
107
104
  const { data: queryResult, isFetching } = useTableQuery<T>({
@@ -121,6 +118,12 @@ export function DataTable<T extends { id: string }>({
121
118
  params.filter_http_status,
122
119
  );
123
120
 
121
+ // Combined clear handler for both search and filters
122
+ const handleClearAll = useCallback(() => {
123
+ handleSearch(null);
124
+ handleFilterChange(null);
125
+ }, [handleSearch, handleFilterChange]);
126
+
124
127
  // Selection helpers
125
128
  const pageIds = useMemo(() => data.map((item) => item.id), [data]);
126
129
  const selectedCount = selectedIds?.size ?? 0;
@@ -186,6 +189,7 @@ export function DataTable<T extends { id: string }>({
186
189
  resource={resourceName}
187
190
  createNewLink={createNewLink}
188
191
  createNewTrigger={createNewTrigger}
192
+ onCreateNew={onCreateNew}
189
193
  t={t}
190
194
  />
191
195
  );
@@ -207,19 +211,17 @@ export function DataTable<T extends { id: string }>({
207
211
 
208
212
  {selectable && selectedCount > 0 && selectionToolbar && (
209
213
  <div className="flex items-center gap-3 rounded-lg border bg-muted/50 px-4 py-2">
210
- {selectionToolbar(selectedCount)}
214
+ {selectionToolbar(selectedCount, data)}
211
215
  </div>
212
216
  )}
213
217
 
214
218
  <div className="rounded-lg border">
215
219
  <Table>
216
220
  {renderHeader ? (
217
- renderHeader({ orderBy: params.order_by, onSort: handleSort })
221
+ renderHeader()
218
222
  ) : (
219
223
  <DefaultTableHeader
220
224
  columns={columns}
221
- orderBy={params.order_by}
222
- onSort={handleSort}
223
225
  selectable={selectable}
224
226
  allPageSelected={allPageSelected}
225
227
  somePageSelected={somePageSelected}
@@ -249,7 +251,7 @@ export function DataTable<T extends { id: string }>({
249
251
  );
250
252
  })
251
253
  ) : (
252
- <TableNoResults resource={resourceName} search={handleSearch} t={t} />
254
+ <TableNoResults resource={resourceName} search={handleSearch} onClear={handleClearAll} t={t} />
253
255
  )}
254
256
  </TableBody>
255
257
  </Table>
@@ -271,16 +273,12 @@ export function DataTable<T extends { id: string }>({
271
273
  */
272
274
  const DefaultTableHeader = memo(function DefaultTableHeader<T>({
273
275
  columns,
274
- orderBy,
275
- onSort,
276
276
  selectable,
277
277
  allPageSelected,
278
278
  somePageSelected,
279
279
  onToggleAll,
280
280
  }: {
281
281
  columns: Column<T>[];
282
- orderBy?: string;
283
- onSort?: (order: string | null) => void;
284
282
  selectable?: boolean;
285
283
  allPageSelected?: boolean;
286
284
  somePageSelected?: boolean;
@@ -300,18 +298,7 @@ const DefaultTableHeader = memo(function DefaultTableHeader<T>({
300
298
  )}
301
299
  {columns.map((column) => (
302
300
  <TableHead key={column.id} className={column.className} style={{ textAlign: column.align }}>
303
- {column.sortable ? (
304
- <SortableHeader
305
- field={column.sortField ?? column.id}
306
- currentOrder={orderBy}
307
- onSort={onSort}
308
- align={column.align}
309
- >
310
- {column.header}
311
- </SortableHeader>
312
- ) : (
313
- column.header
314
- )}
301
+ {column.header}
315
302
  </TableHead>
316
303
  ))}
317
304
  </TableRow>
@@ -319,8 +306,6 @@ const DefaultTableHeader = memo(function DefaultTableHeader<T>({
319
306
  );
320
307
  }) as <T>(props: {
321
308
  columns: Column<T>[];
322
- orderBy?: string;
323
- onSort?: (order: string | null) => void;
324
309
  selectable?: boolean;
325
310
  allPageSelected?: boolean;
326
311
  somePageSelected?: boolean;
@@ -3,7 +3,6 @@ import type { FilterState, HttpMethodFilter, HttpStatusCodeFilter, StatusFilter,
3
3
 
4
4
  type UseTableStateProps = {
5
5
  initialParams?: TableQueryParams;
6
- defaultOrderBy?: string;
7
6
  onChangeParams?: (params: TableQueryParams) => void;
8
7
  /** When true, disables URL sync entirely (for embedded tables like dashboard) */
9
8
  disableUrlSync?: boolean;
@@ -115,17 +114,11 @@ export function buildQueryFromFilterState(state: FilterState | null): string | u
115
114
  }
116
115
 
117
116
  /**
118
- * Manages table state (sorting, search, pagination) with optional URL sync
117
+ * Manages table state (search, pagination, filters) with optional URL sync
119
118
  */
120
- export function useTableState({
121
- initialParams = {},
122
- defaultOrderBy = "-id",
123
- onChangeParams,
124
- disableUrlSync = false,
125
- }: UseTableStateProps) {
119
+ export function useTableState({ initialParams = {}, onChangeParams, disableUrlSync = false }: UseTableStateProps) {
126
120
  const [params, setParams] = useState<TableQueryParams>({
127
121
  ...initialParams,
128
- order_by: initialParams.order_by ?? defaultOrderBy,
129
122
  });
130
123
 
131
124
  // Use ref for onChangeParams to keep it stable
@@ -147,10 +140,9 @@ export function useTableState({
147
140
  isUpdatingFromInitialRef.current = true;
148
141
  setParams({
149
142
  ...initialParams,
150
- order_by: initialParams.order_by ?? defaultOrderBy,
151
143
  });
152
144
  }
153
- }, [initialParams, defaultOrderBy]);
145
+ }, [initialParams]);
154
146
 
155
147
  // Sync params to parent or URL when they change
156
148
  useEffect(() => {
@@ -185,21 +177,6 @@ export function useTableState({
185
177
  }
186
178
  }, [params, disableUrlSync]);
187
179
 
188
- /**
189
- * Handle sort change
190
- */
191
- const handleSort = useCallback(
192
- (order: string | null) => {
193
- setParams((prevParams) => ({
194
- ...prevParams,
195
- order_by: order ?? defaultOrderBy,
196
- prev_cursor: undefined,
197
- next_cursor: undefined,
198
- }));
199
- },
200
- [defaultOrderBy],
201
- );
202
-
203
180
  /**
204
181
  * Handle search change
205
182
  */
@@ -285,7 +262,6 @@ export function useTableState({
285
262
  params,
286
263
  apiParams,
287
264
  filterState,
288
- handleSort,
289
265
  handleSearch,
290
266
  handlePageChange,
291
267
  handleFilterChange,
@@ -2,7 +2,6 @@
2
2
  * Table Component Library
3
3
  *
4
4
  * A comprehensive, type-safe table system with built-in:
5
- * - Sorting (client and server-side)
6
5
  * - Search/filtering
7
6
  * - Cursor-based pagination
8
7
  * - Loading states
@@ -20,7 +19,6 @@ export { useTableQuery } from "./hooks/use-table-query";
20
19
  export { useTableState } from "./hooks/use-table-state";
21
20
  // Supporting components
22
21
  export { SearchInput } from "./search-input";
23
- export { SortableHeader } from "./sortable-header";
24
22
  export { TableEmptyState } from "./table-empty-state";
25
23
  export { TableNoResults } from "./table-no-results";
26
24
  export { Pagination } from "./table-pagination";
@@ -16,6 +16,7 @@ type SearchInputProps = {
16
16
  */
17
17
  export function SearchInput({ initialValue = "", onSearch, placeholder = "Search...", debounceMs }: SearchInputProps) {
18
18
  const [value, setValue] = useState(initialValue);
19
+ const [isFocused, setIsFocused] = useState(false);
19
20
 
20
21
  // Use ref to keep onSearch stable in useEffect
21
22
  const onSearchRef = useRef(onSearch);
@@ -56,6 +57,9 @@ export function SearchInput({ initialValue = "", onSearch, placeholder = "Search
56
57
  onSearchRef.current(null);
57
58
  }, []);
58
59
 
60
+ // Show "Enter ↵" hint when input requires Enter to search (no debounce) and user is typing
61
+ const showEnterHint = debounceMs === undefined && value && isFocused;
62
+
59
63
  return (
60
64
  <form onSubmit={handleSubmit} className="relative inline-block" data-testid="search-form">
61
65
  <Search className="pointer-events-none absolute top-1/2 left-2.5 z-10 h-3 w-3 -translate-y-1/2 text-muted-foreground" />
@@ -66,8 +70,21 @@ export function SearchInput({ initialValue = "", onSearch, placeholder = "Search
66
70
  placeholder={placeholder}
67
71
  value={value}
68
72
  onChange={handleChange}
73
+ onFocus={() => setIsFocused(true)}
74
+ onBlur={() => setIsFocused(false)}
69
75
  className="h-8 w-[150px] pr-8 pl-8 lg:w-[250px] [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden"
70
76
  />
77
+ {/* Overlay that mirrors input padding to position hint right after typed text */}
78
+ {showEnterHint && (
79
+ <span
80
+ aria-hidden="true"
81
+ className="pointer-events-none absolute inset-0 flex items-center overflow-hidden pl-8 text-sm"
82
+ >
83
+ {/* Invisible text matching input value to push hint to the right */}
84
+ <span className="invisible whitespace-pre">{value}</span>
85
+ <span className="ml-1.5 shrink-0 text-muted-foreground/50 text-xs">Enter &crarr;</span>
86
+ </span>
87
+ )}
71
88
  {value && (
72
89
  <Button
73
90
  type="button"
@@ -1,9 +1,14 @@
1
- import { FileDown, X } from "lucide-react";
1
+ import { FileDown, FileText, RefreshCw, X } from "lucide-react";
2
2
  import { Button } from "@/ui/components/ui/button";
3
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
3
4
 
4
5
  type SelectionToolbarProps = {
5
6
  selectedCount: number;
6
7
  onExportPdfs?: () => void;
8
+ onCopyToInvoice?: () => void;
9
+ onRetryFiscalization?: () => void;
10
+ retryFiscalizationDisabled?: boolean;
11
+ retryFiscalizationTooltip?: string;
7
12
  onDeselectAll?: () => void;
8
13
  t?: (key: string) => string;
9
14
  };
@@ -11,6 +16,10 @@ type SelectionToolbarProps = {
11
16
  export function SelectionToolbar({
12
17
  selectedCount,
13
18
  onExportPdfs,
19
+ onCopyToInvoice,
20
+ onRetryFiscalization,
21
+ retryFiscalizationDisabled,
22
+ retryFiscalizationTooltip,
14
23
  onDeselectAll,
15
24
  t = (key) => key,
16
25
  }: SelectionToolbarProps) {
@@ -25,6 +34,31 @@ export function SelectionToolbar({
25
34
  {t("Export PDFs")}
26
35
  </Button>
27
36
  )}
37
+ {onCopyToInvoice && (
38
+ <Button variant="outline" size="sm" onClick={onCopyToInvoice}>
39
+ <FileText className="mr-1.5 size-4" />
40
+ {t("Copy to Invoice")}
41
+ </Button>
42
+ )}
43
+ {onRetryFiscalization &&
44
+ (retryFiscalizationDisabled && retryFiscalizationTooltip ? (
45
+ <Tooltip>
46
+ <TooltipTrigger>
47
+ <span>
48
+ <Button variant="outline" size="sm" disabled>
49
+ <RefreshCw className="mr-1.5 size-4" />
50
+ {t("Retry Fiscalization")}
51
+ </Button>
52
+ </span>
53
+ </TooltipTrigger>
54
+ <TooltipContent>{retryFiscalizationTooltip}</TooltipContent>
55
+ </Tooltip>
56
+ ) : (
57
+ <Button variant="outline" size="sm" onClick={onRetryFiscalization}>
58
+ <RefreshCw className="mr-1.5 size-4" />
59
+ {t("Retry Fiscalization")}
60
+ </Button>
61
+ ))}
28
62
  {onDeselectAll && (
29
63
  <Button variant="ghost" size="sm" onClick={onDeselectAll}>
30
64
  <X className="mr-1.5 size-4" />
@@ -6,6 +6,8 @@ type TableEmptyStateProps = {
6
6
  resource: string;
7
7
  createNewLink?: string;
8
8
  createNewTrigger?: React.ReactNode;
9
+ /** Callback for "Create new" click — enables client-side navigation instead of full page reload */
10
+ onCreateNew?: () => void;
9
11
  /** Number of rows to calculate height (default: 10) */
10
12
  rows?: number;
11
13
  /** Translation function */
@@ -21,6 +23,7 @@ const ROW_HEIGHT = 53;
21
23
  export function TableEmptyState({
22
24
  createNewLink,
23
25
  createNewTrigger,
26
+ onCreateNew,
24
27
  rows = 10,
25
28
  t = (key) => key,
26
29
  }: TableEmptyStateProps) {
@@ -32,13 +35,13 @@ export function TableEmptyState({
32
35
  <Sprout size={70} strokeWidth={0.35} className="text-muted-foreground" />
33
36
  <div className="space-y-1 text-center">
34
37
  <p className="font-light text-lg text-muted-foreground">{t("Your list is empty")}</p>
35
- {(createNewLink || createNewTrigger) && (
38
+ {(createNewLink || createNewTrigger || onCreateNew) && (
36
39
  <p className="text-muted-foreground text-sm">{t("Get started by creating your first entry")}</p>
37
40
  )}
38
41
  </div>
39
42
  {createNewLink && (
40
- <Button variant="default" size="sm" asChild>
41
- <a href={createNewLink}>{t("Create new")}</a>
43
+ <Button variant="default" size="sm" {...(onCreateNew ? { onClick: onCreateNew } : { asChild: true })}>
44
+ {onCreateNew ? t("Create new") : <a href={createNewLink}>{t("Create new")}</a>}
42
45
  </Button>
43
46
  )}
44
47
  {createNewTrigger && !createNewLink && <div>{createNewTrigger}</div>}
@@ -6,6 +6,8 @@ import { TableCell, TableRow } from "@/ui/components/ui/table";
6
6
  type TableNoResultsProps = {
7
7
  resource: string;
8
8
  search?: (value: null) => void;
9
+ /** Combined clear handler for both search and filters */
10
+ onClear?: () => void;
9
11
  /** Number of rows to calculate height (default: 10) */
10
12
  rows?: number;
11
13
  /** Translation function */
@@ -18,10 +20,13 @@ const ROW_HEIGHT = 53;
18
20
  /**
19
21
  * No results message shown when search returns empty
20
22
  */
21
- export function TableNoResults({ search, rows = 10, t = (key) => key }: TableNoResultsProps) {
23
+ export function TableNoResults({ search, onClear, rows = 10, t = (key) => key }: TableNoResultsProps) {
22
24
  // Calculate height based on row count (min 150px)
23
25
  const height = Math.max(rows * ROW_HEIGHT, 150);
24
26
 
27
+ const handleClear = onClear ?? (search ? () => search(null) : undefined);
28
+ const clearLabel = onClear ? t("Clear all") : t("Clear search");
29
+
25
30
  return (
26
31
  <TableRow className="hover:bg-transparent">
27
32
  <TableCell colSpan={100} className="text-center align-middle" style={{ height }}>
@@ -29,11 +34,11 @@ export function TableNoResults({ search, rows = 10, t = (key) => key }: TableNoR
29
34
  <FileX size={32} strokeWidth={1.5} className="text-muted-foreground" />
30
35
  <div className="space-y-1">
31
36
  <p className="font-medium text-muted-foreground">{t("No results found")}</p>
32
- {search && <p className="text-muted-foreground text-sm">{t("Try adjusting your search criteria")}</p>}
37
+ {handleClear && <p className="text-muted-foreground text-sm">{t("Try adjusting your search criteria")}</p>}
33
38
  </div>
34
- {search && (
35
- <Button variant="link" size="sm" onClick={() => search(null)} className="underline">
36
- {t("Clear search")}
39
+ {handleClear && (
40
+ <Button variant="link" size="sm" onClick={handleClear} className="underline">
41
+ {clearLabel}
37
42
  </Button>
38
43
  )}
39
44
  </div>
@@ -10,10 +10,6 @@ export type Column<T> = {
10
10
  header: ReactNode;
11
11
  /** Cell renderer function - returns content for each row. Optional when using renderRow. */
12
12
  cell?: (item: T) => ReactNode;
13
- /** Field name for sorting (if different from id) */
14
- sortField?: string;
15
- /** Whether this column is sortable */
16
- sortable?: boolean;
17
13
  /** Text alignment */
18
14
  align?: "left" | "center" | "right";
19
15
  /** Optional CSS class for the column */
@@ -24,7 +20,6 @@ export type Column<T> = {
24
20
  * Query parameters for table data fetching
25
21
  */
26
22
  export type TableQueryParams = {
27
- order_by?: string;
28
23
  search?: string;
29
24
  prev_cursor?: string;
30
25
  next_cursor?: string;