@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.
- package/README.md +549 -549
- package/package.json +48 -48
- package/template/.claude/skills/anti-patterns.md +150 -0
- package/template/.claude/skills/drizzle-schema.md +178 -0
- package/template/.claude/skills/formatting.md +56 -0
- package/template/.claude/skills/module-architecture.md +143 -0
- package/template/.claude/skills/supabase-server-actions.md +199 -0
- package/template/.claude/skills/ui-patterns.md +161 -0
- package/template/CLAUDE.md +114 -1239
- package/template/drizzle.config.ts +12 -12
- package/template/eslint.config.mjs +16 -16
- package/template/gitignore +36 -36
- package/template/next.config.ts +7 -7
- package/template/package.json +86 -86
- package/template/postcss.config.mjs +7 -7
- package/template/proxy.ts +12 -12
- package/template/public/logolft.svg +11 -11
- package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
- package/template/src/app/(auth)/dashboard/page.tsx +9 -9
- package/template/src/app/(auth)/layout.tsx +7 -7
- package/template/src/app/(auth)/users/page.tsx +9 -9
- package/template/src/app/(auth)/users/users-content.tsx +26 -26
- package/template/src/app/(public)/layout.tsx +7 -7
- package/template/src/app/(public)/login/page.tsx +17 -17
- package/template/src/app/api/webhooks/route.ts +20 -20
- package/template/src/app/globals.css +249 -249
- package/template/src/app/layout.tsx +37 -37
- package/template/src/app/page.tsx +5 -5
- package/template/src/app/providers.tsx +27 -27
- package/template/src/components/layout/main-content.tsx +28 -28
- package/template/src/components/layout/sidebar-context.tsx +33 -33
- package/template/src/components/layout/sidebar.tsx +141 -141
- package/template/src/components/tables/data-table-column-header.tsx +68 -68
- package/template/src/components/tables/data-table-date-filter.tsx +203 -203
- package/template/src/components/tables/data-table-faceted-filter.tsx +185 -185
- package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -130
- package/template/src/components/tables/data-table-number-filter.tsx +295 -295
- package/template/src/components/tables/data-table-pagination.tsx +99 -99
- package/template/src/components/tables/data-table-toolbar.tsx +140 -140
- package/template/src/components/tables/data-table-view-options.tsx +63 -63
- package/template/src/components/tables/data-table.tsx +148 -148
- package/template/src/components/tables/index.ts +9 -9
- package/template/src/components/ui/accordion.tsx +58 -58
- package/template/src/components/ui/alert-dialog.tsx +165 -165
- package/template/src/components/ui/alert.tsx +66 -66
- package/template/src/components/ui/animations/index.ts +44 -44
- package/template/src/components/ui/avatar.tsx +55 -55
- package/template/src/components/ui/badge.tsx +50 -50
- package/template/src/components/ui/button.tsx +118 -118
- package/template/src/components/ui/calendar.tsx +220 -220
- package/template/src/components/ui/card.tsx +113 -113
- package/template/src/components/ui/checkbox.tsx +38 -38
- package/template/src/components/ui/collapsible.tsx +33 -33
- package/template/src/components/ui/command.tsx +196 -196
- package/template/src/components/ui/dialog.tsx +156 -156
- package/template/src/components/ui/dropdown-menu.tsx +280 -280
- package/template/src/components/ui/form.tsx +171 -171
- package/template/src/components/ui/icons.tsx +167 -167
- package/template/src/components/ui/input.tsx +28 -28
- package/template/src/components/ui/label.tsx +25 -25
- package/template/src/components/ui/motion.tsx +197 -197
- package/template/src/components/ui/page-transition.tsx +166 -166
- package/template/src/components/ui/popover.tsx +59 -59
- package/template/src/components/ui/progress.tsx +32 -32
- package/template/src/components/ui/radio-group.tsx +45 -45
- package/template/src/components/ui/scroll-area.tsx +63 -63
- package/template/src/components/ui/select.tsx +208 -208
- package/template/src/components/ui/separator.tsx +28 -28
- package/template/src/components/ui/sheet.tsx +170 -170
- package/template/src/components/ui/sidebar.tsx +726 -726
- package/template/src/components/ui/skeleton.tsx +15 -15
- package/template/src/components/ui/slider.tsx +58 -58
- package/template/src/components/ui/sonner.tsx +47 -47
- package/template/src/components/ui/spinner.tsx +27 -27
- package/template/src/components/ui/submit-button.tsx +47 -47
- package/template/src/components/ui/switch.tsx +31 -31
- package/template/src/components/ui/table.tsx +120 -120
- package/template/src/components/ui/tabs.tsx +75 -75
- package/template/src/components/ui/textarea.tsx +26 -26
- package/template/src/components/ui/tooltip.tsx +70 -70
- package/template/src/config/navigation.ts +59 -59
- package/template/src/config/roles.ts +27 -27
- package/template/src/config/site.ts +12 -12
- package/template/src/db/index.ts +12 -12
- package/template/src/db/schema/index.ts +1 -1
- package/template/src/db/schema/users.ts +16 -16
- package/template/src/db/seed.ts +39 -39
- package/template/src/hooks/index.ts +3 -3
- package/template/src/hooks/use-mobile.ts +21 -21
- package/template/src/hooks/useDataTable.ts +82 -82
- package/template/src/hooks/useDebounce.ts +49 -49
- package/template/src/hooks/useMediaQuery.ts +36 -36
- package/template/src/lib/date/config.ts +36 -36
- package/template/src/lib/date/formatters.ts +127 -127
- package/template/src/lib/date/index.ts +26 -26
- package/template/src/lib/excel/exporter.ts +89 -89
- package/template/src/lib/excel/index.ts +14 -14
- package/template/src/lib/excel/parser.ts +96 -96
- package/template/src/lib/query-client.ts +35 -35
- package/template/src/lib/supabase/admin.ts +23 -23
- package/template/src/lib/supabase/client.ts +11 -11
- package/template/src/lib/supabase/proxy.ts +67 -67
- package/template/src/lib/supabase/server.ts +38 -38
- package/template/src/lib/supabase/types.ts +53 -53
- package/template/src/lib/utils.ts +6 -6
- package/template/src/lib/validations/common.ts +75 -75
- package/template/src/lib/validations/index.ts +20 -20
- package/template/src/modules/auth/actions/auth-actions.ts +59 -59
- package/template/src/modules/auth/components/login-form.tsx +68 -68
- package/template/src/modules/auth/hooks/useAuth.ts +38 -38
- package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
- package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
- package/template/src/modules/auth/index.ts +12 -12
- package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
- package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
- package/template/src/modules/users/actions/users-actions.ts +166 -166
- package/template/src/modules/users/columns.tsx +106 -106
- package/template/src/modules/users/components/users-list.tsx +48 -48
- package/template/src/modules/users/hooks/useUsers.ts +39 -39
- package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
- package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
- package/template/src/modules/users/index.ts +30 -30
- package/template/src/modules/users/schemas/users.schema.ts +51 -51
- package/template/src/modules/users/stores/useUsersStore.ts +60 -60
- package/template/src/modules/users/types/auth-user.types.ts +42 -42
- package/template/src/modules/users/utils/user-mapper.ts +32 -32
- package/template/src/stores/index.ts +1 -1
- package/template/src/stores/useUiStore.ts +55 -55
- package/template/src/types/api.ts +28 -28
- package/template/src/types/index.ts +2 -2
- package/template/src/types/table.ts +34 -34
- package/template/supabase/config.toml +94 -94
- 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'
|