@create-lft-app/nextjs 3.2.0 → 3.3.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 (133) hide show
  1. package/README.md +549 -549
  2. package/package.json +48 -48
  3. package/template/.claude/skills/anti-patterns.md +150 -0
  4. package/template/.claude/skills/drizzle-schema.md +178 -0
  5. package/template/.claude/skills/formatting.md +56 -0
  6. package/template/.claude/skills/module-architecture.md +143 -0
  7. package/template/.claude/skills/supabase-server-actions.md +199 -0
  8. package/template/.claude/skills/ui-patterns.md +161 -0
  9. package/template/CLAUDE.md +114 -1239
  10. package/template/drizzle.config.ts +12 -12
  11. package/template/eslint.config.mjs +16 -16
  12. package/template/gitignore +36 -36
  13. package/template/next.config.ts +7 -7
  14. package/template/package.json +86 -86
  15. package/template/postcss.config.mjs +7 -7
  16. package/template/proxy.ts +12 -12
  17. package/template/public/logolft.svg +11 -11
  18. package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
  19. package/template/src/app/(auth)/dashboard/page.tsx +9 -9
  20. package/template/src/app/(auth)/layout.tsx +7 -7
  21. package/template/src/app/(auth)/users/page.tsx +9 -9
  22. package/template/src/app/(auth)/users/users-content.tsx +26 -26
  23. package/template/src/app/(public)/layout.tsx +7 -7
  24. package/template/src/app/(public)/login/page.tsx +17 -17
  25. package/template/src/app/api/webhooks/route.ts +20 -20
  26. package/template/src/app/globals.css +249 -249
  27. package/template/src/app/layout.tsx +37 -37
  28. package/template/src/app/page.tsx +5 -5
  29. package/template/src/app/providers.tsx +27 -27
  30. package/template/src/components/layout/main-content.tsx +28 -28
  31. package/template/src/components/layout/sidebar-context.tsx +33 -33
  32. package/template/src/components/layout/sidebar.tsx +141 -141
  33. package/template/src/components/tables/data-table-column-header.tsx +68 -68
  34. package/template/src/components/tables/data-table-date-filter.tsx +203 -203
  35. package/template/src/components/tables/data-table-faceted-filter.tsx +185 -185
  36. package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -130
  37. package/template/src/components/tables/data-table-number-filter.tsx +295 -295
  38. package/template/src/components/tables/data-table-pagination.tsx +99 -99
  39. package/template/src/components/tables/data-table-toolbar.tsx +140 -140
  40. package/template/src/components/tables/data-table-view-options.tsx +63 -63
  41. package/template/src/components/tables/data-table.tsx +148 -148
  42. package/template/src/components/tables/index.ts +9 -9
  43. package/template/src/components/ui/accordion.tsx +58 -58
  44. package/template/src/components/ui/alert-dialog.tsx +165 -165
  45. package/template/src/components/ui/alert.tsx +66 -66
  46. package/template/src/components/ui/animations/index.ts +44 -44
  47. package/template/src/components/ui/avatar.tsx +55 -55
  48. package/template/src/components/ui/badge.tsx +50 -50
  49. package/template/src/components/ui/button.tsx +118 -118
  50. package/template/src/components/ui/calendar.tsx +220 -220
  51. package/template/src/components/ui/card.tsx +113 -113
  52. package/template/src/components/ui/checkbox.tsx +38 -38
  53. package/template/src/components/ui/collapsible.tsx +33 -33
  54. package/template/src/components/ui/command.tsx +196 -196
  55. package/template/src/components/ui/dialog.tsx +156 -156
  56. package/template/src/components/ui/dropdown-menu.tsx +280 -280
  57. package/template/src/components/ui/form.tsx +171 -171
  58. package/template/src/components/ui/icons.tsx +167 -167
  59. package/template/src/components/ui/input.tsx +28 -28
  60. package/template/src/components/ui/label.tsx +25 -25
  61. package/template/src/components/ui/motion.tsx +197 -197
  62. package/template/src/components/ui/page-transition.tsx +166 -166
  63. package/template/src/components/ui/popover.tsx +59 -59
  64. package/template/src/components/ui/progress.tsx +32 -32
  65. package/template/src/components/ui/radio-group.tsx +45 -45
  66. package/template/src/components/ui/scroll-area.tsx +63 -63
  67. package/template/src/components/ui/select.tsx +208 -208
  68. package/template/src/components/ui/separator.tsx +28 -28
  69. package/template/src/components/ui/sheet.tsx +170 -170
  70. package/template/src/components/ui/sidebar.tsx +726 -726
  71. package/template/src/components/ui/skeleton.tsx +15 -15
  72. package/template/src/components/ui/slider.tsx +58 -58
  73. package/template/src/components/ui/sonner.tsx +47 -47
  74. package/template/src/components/ui/spinner.tsx +27 -27
  75. package/template/src/components/ui/submit-button.tsx +47 -47
  76. package/template/src/components/ui/switch.tsx +31 -31
  77. package/template/src/components/ui/table.tsx +120 -120
  78. package/template/src/components/ui/tabs.tsx +75 -75
  79. package/template/src/components/ui/textarea.tsx +26 -26
  80. package/template/src/components/ui/tooltip.tsx +70 -70
  81. package/template/src/config/navigation.ts +59 -59
  82. package/template/src/config/roles.ts +27 -27
  83. package/template/src/config/site.ts +12 -12
  84. package/template/src/db/index.ts +12 -12
  85. package/template/src/db/schema/index.ts +1 -1
  86. package/template/src/db/schema/users.ts +16 -16
  87. package/template/src/db/seed.ts +39 -39
  88. package/template/src/hooks/index.ts +3 -3
  89. package/template/src/hooks/use-mobile.ts +21 -21
  90. package/template/src/hooks/useDataTable.ts +82 -82
  91. package/template/src/hooks/useDebounce.ts +49 -49
  92. package/template/src/hooks/useMediaQuery.ts +36 -36
  93. package/template/src/lib/date/config.ts +36 -36
  94. package/template/src/lib/date/formatters.ts +127 -127
  95. package/template/src/lib/date/index.ts +26 -26
  96. package/template/src/lib/excel/exporter.ts +89 -89
  97. package/template/src/lib/excel/index.ts +14 -14
  98. package/template/src/lib/excel/parser.ts +96 -96
  99. package/template/src/lib/query-client.ts +35 -35
  100. package/template/src/lib/supabase/admin.ts +23 -23
  101. package/template/src/lib/supabase/client.ts +11 -11
  102. package/template/src/lib/supabase/proxy.ts +67 -67
  103. package/template/src/lib/supabase/server.ts +38 -38
  104. package/template/src/lib/supabase/types.ts +53 -53
  105. package/template/src/lib/utils.ts +6 -6
  106. package/template/src/lib/validations/common.ts +75 -75
  107. package/template/src/lib/validations/index.ts +20 -20
  108. package/template/src/modules/auth/actions/auth-actions.ts +59 -59
  109. package/template/src/modules/auth/components/login-form.tsx +68 -68
  110. package/template/src/modules/auth/hooks/useAuth.ts +38 -38
  111. package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
  112. package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
  113. package/template/src/modules/auth/index.ts +12 -12
  114. package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
  115. package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
  116. package/template/src/modules/users/actions/users-actions.ts +166 -166
  117. package/template/src/modules/users/columns.tsx +106 -106
  118. package/template/src/modules/users/components/users-list.tsx +48 -48
  119. package/template/src/modules/users/hooks/useUsers.ts +39 -39
  120. package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
  121. package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
  122. package/template/src/modules/users/index.ts +30 -30
  123. package/template/src/modules/users/schemas/users.schema.ts +51 -51
  124. package/template/src/modules/users/stores/useUsersStore.ts +60 -60
  125. package/template/src/modules/users/types/auth-user.types.ts +42 -42
  126. package/template/src/modules/users/utils/user-mapper.ts +32 -32
  127. package/template/src/stores/index.ts +1 -1
  128. package/template/src/stores/useUiStore.ts +55 -55
  129. package/template/src/types/api.ts +28 -28
  130. package/template/src/types/index.ts +2 -2
  131. package/template/src/types/table.ts +34 -34
  132. package/template/supabase/config.toml +94 -94
  133. package/template/tsconfig.json +42 -42
@@ -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,36 +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
- // 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
+ 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,127 +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 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
+ 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,26 +1,26 @@
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
+ 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'