@create-lft-app/nextjs 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +549 -549
  2. package/package.json +48 -48
  3. package/template/CLAUDE.md +1239 -279
  4. package/template/drizzle.config.ts +12 -12
  5. package/template/eslint.config.mjs +16 -16
  6. package/template/gitignore +36 -36
  7. package/template/next.config.ts +7 -7
  8. package/template/package.json +86 -86
  9. package/template/postcss.config.mjs +7 -7
  10. package/template/proxy.ts +12 -12
  11. package/template/public/logolft.svg +11 -11
  12. package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
  13. package/template/src/app/(auth)/dashboard/page.tsx +9 -9
  14. package/template/src/app/(auth)/layout.tsx +7 -7
  15. package/template/src/app/(auth)/users/page.tsx +9 -9
  16. package/template/src/app/(auth)/users/users-content.tsx +26 -26
  17. package/template/src/app/(public)/layout.tsx +7 -7
  18. package/template/src/app/(public)/login/page.tsx +17 -17
  19. package/template/src/app/api/webhooks/route.ts +20 -20
  20. package/template/src/app/globals.css +249 -249
  21. package/template/src/app/layout.tsx +37 -37
  22. package/template/src/app/page.tsx +5 -5
  23. package/template/src/app/providers.tsx +27 -27
  24. package/template/src/components/layout/main-content.tsx +28 -28
  25. package/template/src/components/layout/sidebar-context.tsx +33 -33
  26. package/template/src/components/layout/sidebar.tsx +141 -146
  27. package/template/src/components/tables/data-table-column-header.tsx +68 -68
  28. package/template/src/components/tables/data-table-date-filter.tsx +203 -0
  29. package/template/src/components/tables/data-table-faceted-filter.tsx +185 -0
  30. package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -0
  31. package/template/src/components/tables/data-table-number-filter.tsx +295 -0
  32. package/template/src/components/tables/data-table-pagination.tsx +99 -99
  33. package/template/src/components/tables/data-table-toolbar.tsx +140 -50
  34. package/template/src/components/tables/data-table-view-options.tsx +63 -59
  35. package/template/src/components/tables/data-table.tsx +148 -128
  36. package/template/src/components/tables/index.ts +9 -5
  37. package/template/src/components/ui/accordion.tsx +58 -58
  38. package/template/src/components/ui/alert-dialog.tsx +165 -165
  39. package/template/src/components/ui/alert.tsx +66 -66
  40. package/template/src/components/ui/animations/index.ts +44 -44
  41. package/template/src/components/ui/avatar.tsx +55 -55
  42. package/template/src/components/ui/badge.tsx +50 -50
  43. package/template/src/components/ui/button.tsx +118 -118
  44. package/template/src/components/ui/calendar.tsx +220 -220
  45. package/template/src/components/ui/card.tsx +113 -113
  46. package/template/src/components/ui/checkbox.tsx +38 -38
  47. package/template/src/components/ui/collapsible.tsx +33 -33
  48. package/template/src/components/ui/command.tsx +196 -196
  49. package/template/src/components/ui/dialog.tsx +156 -156
  50. package/template/src/components/ui/dropdown-menu.tsx +280 -280
  51. package/template/src/components/ui/form.tsx +171 -171
  52. package/template/src/components/ui/icons.tsx +167 -167
  53. package/template/src/components/ui/input.tsx +28 -28
  54. package/template/src/components/ui/label.tsx +25 -25
  55. package/template/src/components/ui/motion.tsx +197 -197
  56. package/template/src/components/ui/page-transition.tsx +166 -166
  57. package/template/src/components/ui/popover.tsx +59 -59
  58. package/template/src/components/ui/progress.tsx +32 -32
  59. package/template/src/components/ui/radio-group.tsx +45 -45
  60. package/template/src/components/ui/scroll-area.tsx +63 -63
  61. package/template/src/components/ui/select.tsx +208 -208
  62. package/template/src/components/ui/separator.tsx +28 -28
  63. package/template/src/components/ui/sheet.tsx +170 -170
  64. package/template/src/components/ui/sidebar.tsx +726 -726
  65. package/template/src/components/ui/skeleton.tsx +15 -15
  66. package/template/src/components/ui/slider.tsx +58 -58
  67. package/template/src/components/ui/sonner.tsx +47 -47
  68. package/template/src/components/ui/spinner.tsx +27 -27
  69. package/template/src/components/ui/submit-button.tsx +47 -47
  70. package/template/src/components/ui/switch.tsx +31 -31
  71. package/template/src/components/ui/table.tsx +120 -120
  72. package/template/src/components/ui/tabs.tsx +75 -75
  73. package/template/src/components/ui/textarea.tsx +26 -26
  74. package/template/src/components/ui/tooltip.tsx +70 -70
  75. package/template/src/config/navigation.ts +59 -69
  76. package/template/src/config/roles.ts +27 -0
  77. package/template/src/config/site.ts +12 -12
  78. package/template/src/db/index.ts +12 -12
  79. package/template/src/db/schema/index.ts +1 -1
  80. package/template/src/db/schema/users.ts +16 -16
  81. package/template/src/db/seed.ts +39 -39
  82. package/template/src/hooks/index.ts +3 -3
  83. package/template/src/hooks/use-mobile.ts +21 -21
  84. package/template/src/hooks/useDataTable.ts +82 -82
  85. package/template/src/hooks/useDebounce.ts +49 -49
  86. package/template/src/hooks/useMediaQuery.ts +36 -36
  87. package/template/src/lib/date/config.ts +36 -34
  88. package/template/src/lib/date/formatters.ts +127 -120
  89. package/template/src/lib/date/index.ts +26 -19
  90. package/template/src/lib/excel/exporter.ts +89 -89
  91. package/template/src/lib/excel/index.ts +14 -14
  92. package/template/src/lib/excel/parser.ts +96 -96
  93. package/template/src/lib/query-client.ts +35 -35
  94. package/template/src/lib/supabase/admin.ts +23 -0
  95. package/template/src/lib/supabase/client.ts +11 -11
  96. package/template/src/lib/supabase/proxy.ts +67 -67
  97. package/template/src/lib/supabase/server.ts +38 -38
  98. package/template/src/lib/supabase/types.ts +53 -53
  99. package/template/src/lib/utils.ts +6 -6
  100. package/template/src/lib/validations/common.ts +75 -75
  101. package/template/src/lib/validations/index.ts +20 -20
  102. package/template/src/modules/auth/actions/auth-actions.ts +59 -59
  103. package/template/src/modules/auth/components/login-form.tsx +68 -68
  104. package/template/src/modules/auth/hooks/useAuth.ts +38 -38
  105. package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
  106. package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
  107. package/template/src/modules/auth/index.ts +12 -12
  108. package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
  109. package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
  110. package/template/src/modules/users/actions/users-actions.ts +166 -94
  111. package/template/src/modules/users/columns.tsx +106 -86
  112. package/template/src/modules/users/components/users-list.tsx +48 -22
  113. package/template/src/modules/users/hooks/useUsers.ts +39 -39
  114. package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
  115. package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
  116. package/template/src/modules/users/index.ts +30 -12
  117. package/template/src/modules/users/schemas/users.schema.ts +51 -23
  118. package/template/src/modules/users/stores/useUsersStore.ts +60 -60
  119. package/template/src/modules/users/types/auth-user.types.ts +42 -0
  120. package/template/src/modules/users/utils/user-mapper.ts +32 -0
  121. package/template/src/stores/index.ts +1 -1
  122. package/template/src/stores/useUiStore.ts +55 -55
  123. package/template/src/types/api.ts +28 -28
  124. package/template/src/types/index.ts +2 -2
  125. package/template/src/types/table.ts +34 -34
  126. package/template/supabase/config.toml +94 -94
  127. package/template/tsconfig.json +42 -42
  128. package/template/tsconfig.tsbuildinfo +1 -1
@@ -1,36 +1,36 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
-
5
- export function useMediaQuery(query: string): boolean {
6
- const [matches, setMatches] = useState(false)
7
-
8
- useEffect(() => {
9
- const media = window.matchMedia(query)
10
-
11
- if (media.matches !== matches) {
12
- setMatches(media.matches)
13
- }
14
-
15
- const listener = (event: MediaQueryListEvent) => {
16
- setMatches(event.matches)
17
- }
18
-
19
- media.addEventListener('change', listener)
20
- return () => media.removeEventListener('change', listener)
21
- }, [matches, query])
22
-
23
- return matches
24
- }
25
-
26
- export function useIsMobile(): boolean {
27
- return useMediaQuery('(max-width: 768px)')
28
- }
29
-
30
- export function useIsTablet(): boolean {
31
- return useMediaQuery('(min-width: 769px) and (max-width: 1024px)')
32
- }
33
-
34
- export function useIsDesktop(): boolean {
35
- return useMediaQuery('(min-width: 1025px)')
36
- }
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+
5
+ export function useMediaQuery(query: string): boolean {
6
+ const [matches, setMatches] = useState(false)
7
+
8
+ useEffect(() => {
9
+ const media = window.matchMedia(query)
10
+
11
+ if (media.matches !== matches) {
12
+ setMatches(media.matches)
13
+ }
14
+
15
+ const listener = (event: MediaQueryListEvent) => {
16
+ setMatches(event.matches)
17
+ }
18
+
19
+ media.addEventListener('change', listener)
20
+ return () => media.removeEventListener('change', listener)
21
+ }, [matches, query])
22
+
23
+ return matches
24
+ }
25
+
26
+ export function useIsMobile(): boolean {
27
+ return useMediaQuery('(max-width: 768px)')
28
+ }
29
+
30
+ export function useIsTablet(): boolean {
31
+ return useMediaQuery('(min-width: 769px) and (max-width: 1024px)')
32
+ }
33
+
34
+ export function useIsDesktop(): boolean {
35
+ return useMediaQuery('(min-width: 1025px)')
36
+ }
@@ -1,34 +1,36 @@
1
- import dayjs from 'dayjs'
2
- import utc from 'dayjs/plugin/utc'
3
- import timezone from 'dayjs/plugin/timezone'
4
- import relativeTime from 'dayjs/plugin/relativeTime'
5
- import localizedFormat from 'dayjs/plugin/localizedFormat'
6
- import isBetween from 'dayjs/plugin/isBetween'
7
- import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
8
- import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
9
- import 'dayjs/locale/es'
10
-
11
- // Extend dayjs with plugins
12
- dayjs.extend(utc)
13
- dayjs.extend(timezone)
14
- dayjs.extend(relativeTime)
15
- dayjs.extend(localizedFormat)
16
- dayjs.extend(isBetween)
17
- dayjs.extend(isSameOrAfter)
18
- dayjs.extend(isSameOrBefore)
19
-
20
- // Set default locale to Spanish
21
- dayjs.locale('es')
22
-
23
- // Set default timezone (can be overridden)
24
- const DEFAULT_TIMEZONE = 'America/Mexico_City'
25
-
26
- export function setDefaultTimezone(tz: string) {
27
- dayjs.tz.setDefault(tz)
28
- }
29
-
30
- // Initialize with default timezone
31
- setDefaultTimezone(DEFAULT_TIMEZONE)
32
-
33
- export { dayjs }
34
- export default dayjs
1
+ import dayjs from 'dayjs'
2
+ import utc from 'dayjs/plugin/utc'
3
+ import timezone from 'dayjs/plugin/timezone'
4
+ import relativeTime from 'dayjs/plugin/relativeTime'
5
+ import localizedFormat from 'dayjs/plugin/localizedFormat'
6
+ import isBetween from 'dayjs/plugin/isBetween'
7
+ import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
8
+ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
9
+ import 'dayjs/locale/es'
10
+
11
+ // Extend dayjs with plugins
12
+ dayjs.extend(utc)
13
+ dayjs.extend(timezone)
14
+ dayjs.extend(relativeTime)
15
+ dayjs.extend(localizedFormat)
16
+ dayjs.extend(isBetween)
17
+ dayjs.extend(isSameOrAfter)
18
+ dayjs.extend(isSameOrBefore)
19
+
20
+ // Set default locale to Spanish
21
+ dayjs.locale('es')
22
+
23
+ // Configuración de internacionalización
24
+ export const DEFAULT_LOCALE = 'es-AR'
25
+ export const DEFAULT_TIMEZONE = 'America/Argentina/Buenos_Aires'
26
+ export const DEFAULT_CURRENCY = 'ARS'
27
+
28
+ export function setDefaultTimezone(tz: string) {
29
+ dayjs.tz.setDefault(tz)
30
+ }
31
+
32
+ // Initialize with default timezone
33
+ setDefaultTimezone(DEFAULT_TIMEZONE)
34
+
35
+ export { dayjs }
36
+ export default dayjs
@@ -1,120 +1,127 @@
1
- import { dayjs } from './config'
2
- import type { Dayjs, ConfigType } from 'dayjs'
3
-
4
- type DateInput = ConfigType
5
-
6
- /**
7
- * Format date to localized short format (e.g., "15/01/2024")
8
- */
9
- export function formatDate(date: DateInput): string {
10
- return dayjs(date).format('DD/MM/YYYY')
11
- }
12
-
13
- /**
14
- * Format date to localized long format (e.g., "15 de enero de 2024")
15
- */
16
- export function formatDateLong(date: DateInput): string {
17
- return dayjs(date).format('D [de] MMMM [de] YYYY')
18
- }
19
-
20
- /**
21
- * Format time (e.g., "14:30")
22
- */
23
- export function formatTime(date: DateInput): string {
24
- return dayjs(date).format('HH:mm')
25
- }
26
-
27
- /**
28
- * Format time with seconds (e.g., "14:30:45")
29
- */
30
- export function formatTimeWithSeconds(date: DateInput): string {
31
- return dayjs(date).format('HH:mm:ss')
32
- }
33
-
34
- /**
35
- * Format date and time (e.g., "15/01/2024 14:30")
36
- */
37
- export function formatDateTime(date: DateInput): string {
38
- return dayjs(date).format('DD/MM/YYYY HH:mm')
39
- }
40
-
41
- /**
42
- * Format date and time long (e.g., "15 de enero de 2024 a las 14:30")
43
- */
44
- export function formatDateTimeLong(date: DateInput): string {
45
- return dayjs(date).format('D [de] MMMM [de] YYYY [a las] HH:mm')
46
- }
47
-
48
- /**
49
- * Format relative time (e.g., "hace 2 horas")
50
- */
51
- export function formatRelative(date: DateInput): string {
52
- return dayjs(date).fromNow()
53
- }
54
-
55
- /**
56
- * Format ISO string
57
- */
58
- export function formatISO(date: DateInput): string {
59
- return dayjs(date).toISOString()
60
- }
61
-
62
- /**
63
- * Format for API requests (ISO format)
64
- */
65
- export function formatForApi(date: DateInput): string {
66
- return dayjs(date).utc().format()
67
- }
68
-
69
- /**
70
- * Parse a date string and return a Dayjs object
71
- */
72
- export function parseDate(date: DateInput): Dayjs {
73
- return dayjs(date)
74
- }
75
-
76
- /**
77
- * Get start of day
78
- */
79
- export function startOfDay(date: DateInput): Dayjs {
80
- return dayjs(date).startOf('day')
81
- }
82
-
83
- /**
84
- * Get end of day
85
- */
86
- export function endOfDay(date: DateInput): Dayjs {
87
- return dayjs(date).endOf('day')
88
- }
89
-
90
- /**
91
- * Check if a date is today
92
- */
93
- export function isToday(date: DateInput): boolean {
94
- return dayjs(date).isSame(dayjs(), 'day')
95
- }
96
-
97
- /**
98
- * Check if a date is in the past
99
- */
100
- export function isPast(date: DateInput): boolean {
101
- return dayjs(date).isBefore(dayjs())
102
- }
103
-
104
- /**
105
- * Check if a date is in the future
106
- */
107
- export function isFuture(date: DateInput): boolean {
108
- return dayjs(date).isAfter(dayjs())
109
- }
110
-
111
- /**
112
- * Get difference between two dates in specified unit
113
- */
114
- export function diff(
115
- date1: DateInput,
116
- date2: DateInput,
117
- unit: 'day' | 'week' | 'month' | 'year' | 'hour' | 'minute' = 'day'
118
- ): number {
119
- return dayjs(date1).diff(dayjs(date2), unit)
120
- }
1
+ import { dayjs } from './config'
2
+ import type { Dayjs, ConfigType } from 'dayjs'
3
+
4
+ type DateInput = ConfigType
5
+
6
+ /**
7
+ * Format date to localized short format (e.g., "15/01/2024")
8
+ */
9
+ export function formatDate(date: DateInput): string {
10
+ return dayjs(date).format('DD/MM/YYYY')
11
+ }
12
+
13
+ /**
14
+ * Format date to localized long format (e.g., "15 de enero de 2024")
15
+ */
16
+ export function formatDateLong(date: DateInput): string {
17
+ return dayjs(date).format('D [de] MMMM [de] YYYY')
18
+ }
19
+
20
+ /**
21
+ * Format date to short format (e.g., "15 ene")
22
+ */
23
+ export function formatDateShort(date: DateInput): string {
24
+ return dayjs(date).format('D MMM')
25
+ }
26
+
27
+ /**
28
+ * Format time (e.g., "14:30")
29
+ */
30
+ export function formatTime(date: DateInput): string {
31
+ return dayjs(date).format('HH:mm')
32
+ }
33
+
34
+ /**
35
+ * Format time with seconds (e.g., "14:30:45")
36
+ */
37
+ export function formatTimeWithSeconds(date: DateInput): string {
38
+ return dayjs(date).format('HH:mm:ss')
39
+ }
40
+
41
+ /**
42
+ * Format date and time (e.g., "15/01/2024 14:30")
43
+ */
44
+ export function formatDateTime(date: DateInput): string {
45
+ return dayjs(date).format('DD/MM/YYYY HH:mm')
46
+ }
47
+
48
+ /**
49
+ * Format date and time long (e.g., "15 de enero de 2024 a las 14:30")
50
+ */
51
+ export function formatDateTimeLong(date: DateInput): string {
52
+ return dayjs(date).format('D [de] MMMM [de] YYYY [a las] HH:mm')
53
+ }
54
+
55
+ /**
56
+ * Format relative time (e.g., "hace 2 horas")
57
+ */
58
+ export function formatRelative(date: DateInput): string {
59
+ return dayjs(date).fromNow()
60
+ }
61
+
62
+ /**
63
+ * Format ISO string
64
+ */
65
+ export function formatISO(date: DateInput): string {
66
+ return dayjs(date).toISOString()
67
+ }
68
+
69
+ /**
70
+ * Format for API requests (ISO format)
71
+ */
72
+ export function formatForApi(date: DateInput): string {
73
+ return dayjs(date).utc().format()
74
+ }
75
+
76
+ /**
77
+ * Parse a date string and return a Dayjs object
78
+ */
79
+ export function parseDate(date: DateInput): Dayjs {
80
+ return dayjs(date)
81
+ }
82
+
83
+ /**
84
+ * Get start of day
85
+ */
86
+ export function startOfDay(date: DateInput): Dayjs {
87
+ return dayjs(date).startOf('day')
88
+ }
89
+
90
+ /**
91
+ * Get end of day
92
+ */
93
+ export function endOfDay(date: DateInput): Dayjs {
94
+ return dayjs(date).endOf('day')
95
+ }
96
+
97
+ /**
98
+ * Check if a date is today
99
+ */
100
+ export function isToday(date: DateInput): boolean {
101
+ return dayjs(date).isSame(dayjs(), 'day')
102
+ }
103
+
104
+ /**
105
+ * Check if a date is in the past
106
+ */
107
+ export function isPast(date: DateInput): boolean {
108
+ return dayjs(date).isBefore(dayjs())
109
+ }
110
+
111
+ /**
112
+ * Check if a date is in the future
113
+ */
114
+ export function isFuture(date: DateInput): boolean {
115
+ return dayjs(date).isAfter(dayjs())
116
+ }
117
+
118
+ /**
119
+ * Get difference between two dates in specified unit
120
+ */
121
+ export function diff(
122
+ date1: DateInput,
123
+ date2: DateInput,
124
+ unit: 'day' | 'week' | 'month' | 'year' | 'hour' | 'minute' = 'day'
125
+ ): number {
126
+ return dayjs(date1).diff(dayjs(date2), unit)
127
+ }
@@ -1,19 +1,26 @@
1
- export { dayjs, setDefaultTimezone } from './config'
2
- export {
3
- formatDate,
4
- formatDateLong,
5
- formatTime,
6
- formatTimeWithSeconds,
7
- formatDateTime,
8
- formatDateTimeLong,
9
- formatRelative,
10
- formatISO,
11
- formatForApi,
12
- parseDate,
13
- startOfDay,
14
- endOfDay,
15
- isToday,
16
- isPast,
17
- isFuture,
18
- diff,
19
- } from './formatters'
1
+ export {
2
+ dayjs,
3
+ setDefaultTimezone,
4
+ DEFAULT_LOCALE,
5
+ DEFAULT_TIMEZONE,
6
+ DEFAULT_CURRENCY,
7
+ } from './config'
8
+ export {
9
+ formatDate,
10
+ formatDateLong,
11
+ formatDateShort,
12
+ formatTime,
13
+ formatTimeWithSeconds,
14
+ formatDateTime,
15
+ formatDateTimeLong,
16
+ formatRelative,
17
+ formatISO,
18
+ formatForApi,
19
+ parseDate,
20
+ startOfDay,
21
+ endOfDay,
22
+ isToday,
23
+ isPast,
24
+ isFuture,
25
+ diff,
26
+ } from './formatters'
@@ -1,89 +1,89 @@
1
- import * as XLSX from 'xlsx'
2
-
3
- export interface ExportOptions {
4
- filename?: string
5
- sheetName?: string
6
- headers?: string[]
7
- columnWidths?: number[]
8
- }
9
-
10
- /**
11
- * Export data to Excel file and trigger download
12
- */
13
- export function exportToExcel<T extends Record<string, unknown>>(
14
- data: T[],
15
- options: ExportOptions = {}
16
- ): void {
17
- const {
18
- filename = 'export',
19
- sheetName = 'Sheet1',
20
- headers,
21
- columnWidths,
22
- } = options
23
-
24
- // Create worksheet from data
25
- const worksheet = XLSX.utils.json_to_sheet(data, {
26
- header: headers,
27
- })
28
-
29
- // Set column widths if provided
30
- if (columnWidths) {
31
- worksheet['!cols'] = columnWidths.map((width) => ({ wch: width }))
32
- }
33
-
34
- // Create workbook
35
- const workbook = XLSX.utils.book_new()
36
- XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
37
-
38
- // Generate filename with extension
39
- const fullFilename = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`
40
-
41
- // Trigger download
42
- XLSX.writeFile(workbook, fullFilename)
43
- }
44
-
45
- /**
46
- * Export data to Excel buffer (for server-side usage)
47
- */
48
- export function exportToExcelBuffer<T extends Record<string, unknown>>(
49
- data: T[],
50
- options: Omit<ExportOptions, 'filename'> = {}
51
- ): Buffer {
52
- const { sheetName = 'Sheet1', headers, columnWidths } = options
53
-
54
- const worksheet = XLSX.utils.json_to_sheet(data, {
55
- header: headers,
56
- })
57
-
58
- if (columnWidths) {
59
- worksheet['!cols'] = columnWidths.map((width) => ({ wch: width }))
60
- }
61
-
62
- const workbook = XLSX.utils.book_new()
63
- XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
64
-
65
- return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' })
66
- }
67
-
68
- /**
69
- * Create an Excel template with headers only
70
- */
71
- export function createTemplate(
72
- headers: string[],
73
- options: Omit<ExportOptions, 'headers'> = {}
74
- ): void {
75
- const { filename = 'template', sheetName = 'Sheet1' } = options
76
-
77
- const worksheet = XLSX.utils.aoa_to_sheet([headers])
78
-
79
- // Auto-fit column widths based on header length
80
- worksheet['!cols'] = headers.map((header) => ({
81
- wch: Math.max(header.length + 2, 12),
82
- }))
83
-
84
- const workbook = XLSX.utils.book_new()
85
- XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
86
-
87
- const fullFilename = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`
88
- XLSX.writeFile(workbook, fullFilename)
89
- }
1
+ import * as XLSX from 'xlsx'
2
+
3
+ export interface ExportOptions {
4
+ filename?: string
5
+ sheetName?: string
6
+ headers?: string[]
7
+ columnWidths?: number[]
8
+ }
9
+
10
+ /**
11
+ * Export data to Excel file and trigger download
12
+ */
13
+ export function exportToExcel<T extends Record<string, unknown>>(
14
+ data: T[],
15
+ options: ExportOptions = {}
16
+ ): void {
17
+ const {
18
+ filename = 'export',
19
+ sheetName = 'Sheet1',
20
+ headers,
21
+ columnWidths,
22
+ } = options
23
+
24
+ // Create worksheet from data
25
+ const worksheet = XLSX.utils.json_to_sheet(data, {
26
+ header: headers,
27
+ })
28
+
29
+ // Set column widths if provided
30
+ if (columnWidths) {
31
+ worksheet['!cols'] = columnWidths.map((width) => ({ wch: width }))
32
+ }
33
+
34
+ // Create workbook
35
+ const workbook = XLSX.utils.book_new()
36
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
37
+
38
+ // Generate filename with extension
39
+ const fullFilename = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`
40
+
41
+ // Trigger download
42
+ XLSX.writeFile(workbook, fullFilename)
43
+ }
44
+
45
+ /**
46
+ * Export data to Excel buffer (for server-side usage)
47
+ */
48
+ export function exportToExcelBuffer<T extends Record<string, unknown>>(
49
+ data: T[],
50
+ options: Omit<ExportOptions, 'filename'> = {}
51
+ ): Buffer {
52
+ const { sheetName = 'Sheet1', headers, columnWidths } = options
53
+
54
+ const worksheet = XLSX.utils.json_to_sheet(data, {
55
+ header: headers,
56
+ })
57
+
58
+ if (columnWidths) {
59
+ worksheet['!cols'] = columnWidths.map((width) => ({ wch: width }))
60
+ }
61
+
62
+ const workbook = XLSX.utils.book_new()
63
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
64
+
65
+ return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' })
66
+ }
67
+
68
+ /**
69
+ * Create an Excel template with headers only
70
+ */
71
+ export function createTemplate(
72
+ headers: string[],
73
+ options: Omit<ExportOptions, 'headers'> = {}
74
+ ): void {
75
+ const { filename = 'template', sheetName = 'Sheet1' } = options
76
+
77
+ const worksheet = XLSX.utils.aoa_to_sheet([headers])
78
+
79
+ // Auto-fit column widths based on header length
80
+ worksheet['!cols'] = headers.map((header) => ({
81
+ wch: Math.max(header.length + 2, 12),
82
+ }))
83
+
84
+ const workbook = XLSX.utils.book_new()
85
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
86
+
87
+ const fullFilename = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`
88
+ XLSX.writeFile(workbook, fullFilename)
89
+ }
@@ -1,14 +1,14 @@
1
- export {
2
- parseExcelBuffer,
3
- parseExcelFile,
4
- validateExcelData,
5
- type ParseOptions,
6
- type ParseResult,
7
- } from './parser'
8
-
9
- export {
10
- exportToExcel,
11
- exportToExcelBuffer,
12
- createTemplate,
13
- type ExportOptions,
14
- } from './exporter'
1
+ export {
2
+ parseExcelBuffer,
3
+ parseExcelFile,
4
+ validateExcelData,
5
+ type ParseOptions,
6
+ type ParseResult,
7
+ } from './parser'
8
+
9
+ export {
10
+ exportToExcel,
11
+ exportToExcelBuffer,
12
+ createTemplate,
13
+ type ExportOptions,
14
+ } from './exporter'