@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,106 +1,106 @@
1
- 'use client'
2
-
3
- import { ColumnDef } from '@tanstack/react-table'
4
- import { MoreHorizontal } from 'lucide-react'
5
- import { Button } from '@/components/ui/button'
6
- import {
7
- DropdownMenu,
8
- DropdownMenuContent,
9
- DropdownMenuItem,
10
- DropdownMenuLabel,
11
- DropdownMenuSeparator,
12
- DropdownMenuTrigger,
13
- } from '@/components/ui/dropdown-menu'
14
- import { DataTableColumnHeader } from '@/components/tables/data-table-column-header'
15
- import { dateRangeFilterFn } from '@/components/tables/data-table-date-filter'
16
- // numberRangeFilterFn disponible para campos numéricos: import { numberRangeFilterFn } from '@/components/tables/data-table-number-filter'
17
- import { formatDate } from '@/lib/date'
18
- import { ROLE_LABELS } from '@/config/roles'
19
- import type { User } from './schemas/users.schema'
20
- import type { UserRole } from '@/config/roles'
21
-
22
- export const columns: ColumnDef<User>[] = [
23
- {
24
- accessorKey: 'name',
25
- header: ({ column }) => (
26
- <DataTableColumnHeader column={column} title="Nombre" />
27
- ),
28
- },
29
- {
30
- accessorKey: 'email',
31
- header: ({ column }) => (
32
- <DataTableColumnHeader column={column} title="Email" />
33
- ),
34
- },
35
- {
36
- accessorKey: 'role',
37
- header: ({ column }) => (
38
- <DataTableColumnHeader column={column} title="Rol" />
39
- ),
40
- cell: ({ row }) => {
41
- const role = row.getValue('role') as UserRole
42
- return ROLE_LABELS[role] ?? role
43
- },
44
- filterFn: (row, id, value) => {
45
- return value.includes(row.getValue(id))
46
- },
47
- },
48
- {
49
- accessorKey: 'created_at',
50
- header: ({ column }) => (
51
- <DataTableColumnHeader column={column} title="Creado" />
52
- ),
53
- cell: ({ row }) => {
54
- return formatDate(row.getValue('created_at'))
55
- },
56
- filterFn: dateRangeFilterFn,
57
- },
58
- // Ejemplo de columna numérica con filtro de rango:
59
- // import { DEFAULT_LOCALE, DEFAULT_CURRENCY } from '@/lib/date'
60
- // {
61
- // accessorKey: 'balance',
62
- // header: ({ column }) => (
63
- // <DataTableColumnHeader column={column} title="Balance" />
64
- // ),
65
- // cell: ({ row }) => {
66
- // const balance = row.getValue('balance') as number | undefined
67
- // if (balance === undefined || balance === null) return '-'
68
- // return new Intl.NumberFormat(DEFAULT_LOCALE, {
69
- // style: 'currency',
70
- // currency: DEFAULT_CURRENCY,
71
- // }).format(balance)
72
- // },
73
- // filterFn: numberRangeFilterFn,
74
- // },
75
- {
76
- id: 'actions',
77
- cell: ({ row }) => {
78
- const user = row.original
79
-
80
- return (
81
- <DropdownMenu>
82
- <DropdownMenuTrigger asChild>
83
- <Button variant="ghost" className="h-8 w-8 p-0">
84
- <span className="sr-only">Abrir menú</span>
85
- <MoreHorizontal className="h-4 w-4" />
86
- </Button>
87
- </DropdownMenuTrigger>
88
- <DropdownMenuContent align="end">
89
- <DropdownMenuLabel>Acciones</DropdownMenuLabel>
90
- <DropdownMenuItem
91
- onClick={() => navigator.clipboard.writeText(user.id)}
92
- >
93
- Copiar ID
94
- </DropdownMenuItem>
95
- <DropdownMenuSeparator />
96
- <DropdownMenuItem>Ver detalles</DropdownMenuItem>
97
- <DropdownMenuItem>Editar</DropdownMenuItem>
98
- <DropdownMenuItem className="text-destructive">
99
- Eliminar
100
- </DropdownMenuItem>
101
- </DropdownMenuContent>
102
- </DropdownMenu>
103
- )
104
- },
105
- },
106
- ]
1
+ 'use client'
2
+
3
+ import { ColumnDef } from '@tanstack/react-table'
4
+ import { MoreHorizontal } from 'lucide-react'
5
+ import { Button } from '@/components/ui/button'
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ DropdownMenuLabel,
11
+ DropdownMenuSeparator,
12
+ DropdownMenuTrigger,
13
+ } from '@/components/ui/dropdown-menu'
14
+ import { DataTableColumnHeader } from '@/components/tables/data-table-column-header'
15
+ import { dateRangeFilterFn } from '@/components/tables/data-table-date-filter'
16
+ // numberRangeFilterFn disponible para campos numéricos: import { numberRangeFilterFn } from '@/components/tables/data-table-number-filter'
17
+ import { formatDate } from '@/lib/date'
18
+ import { ROLE_LABELS } from '@/config/roles'
19
+ import type { User } from './schemas/users.schema'
20
+ import type { UserRole } from '@/config/roles'
21
+
22
+ export const columns: ColumnDef<User>[] = [
23
+ {
24
+ accessorKey: 'name',
25
+ header: ({ column }) => (
26
+ <DataTableColumnHeader column={column} title="Nombre" />
27
+ ),
28
+ },
29
+ {
30
+ accessorKey: 'email',
31
+ header: ({ column }) => (
32
+ <DataTableColumnHeader column={column} title="Email" />
33
+ ),
34
+ },
35
+ {
36
+ accessorKey: 'role',
37
+ header: ({ column }) => (
38
+ <DataTableColumnHeader column={column} title="Rol" />
39
+ ),
40
+ cell: ({ row }) => {
41
+ const role = row.getValue('role') as UserRole
42
+ return ROLE_LABELS[role] ?? role
43
+ },
44
+ filterFn: (row, id, value) => {
45
+ return value.includes(row.getValue(id))
46
+ },
47
+ },
48
+ {
49
+ accessorKey: 'created_at',
50
+ header: ({ column }) => (
51
+ <DataTableColumnHeader column={column} title="Creado" />
52
+ ),
53
+ cell: ({ row }) => {
54
+ return formatDate(row.getValue('created_at'))
55
+ },
56
+ filterFn: dateRangeFilterFn,
57
+ },
58
+ // Ejemplo de columna numérica con filtro de rango:
59
+ // import { DEFAULT_LOCALE, DEFAULT_CURRENCY } from '@/lib/date'
60
+ // {
61
+ // accessorKey: 'balance',
62
+ // header: ({ column }) => (
63
+ // <DataTableColumnHeader column={column} title="Balance" />
64
+ // ),
65
+ // cell: ({ row }) => {
66
+ // const balance = row.getValue('balance') as number | undefined
67
+ // if (balance === undefined || balance === null) return '-'
68
+ // return new Intl.NumberFormat(DEFAULT_LOCALE, {
69
+ // style: 'currency',
70
+ // currency: DEFAULT_CURRENCY,
71
+ // }).format(balance)
72
+ // },
73
+ // filterFn: numberRangeFilterFn,
74
+ // },
75
+ {
76
+ id: 'actions',
77
+ cell: ({ row }) => {
78
+ const user = row.original
79
+
80
+ return (
81
+ <DropdownMenu>
82
+ <DropdownMenuTrigger asChild>
83
+ <Button variant="ghost" className="h-8 w-8 p-0">
84
+ <span className="sr-only">Abrir menú</span>
85
+ <MoreHorizontal className="h-4 w-4" />
86
+ </Button>
87
+ </DropdownMenuTrigger>
88
+ <DropdownMenuContent align="end">
89
+ <DropdownMenuLabel>Acciones</DropdownMenuLabel>
90
+ <DropdownMenuItem
91
+ onClick={() => navigator.clipboard.writeText(user.id)}
92
+ >
93
+ Copiar ID
94
+ </DropdownMenuItem>
95
+ <DropdownMenuSeparator />
96
+ <DropdownMenuItem>Ver detalles</DropdownMenuItem>
97
+ <DropdownMenuItem>Editar</DropdownMenuItem>
98
+ <DropdownMenuItem className="text-destructive">
99
+ Eliminar
100
+ </DropdownMenuItem>
101
+ </DropdownMenuContent>
102
+ </DropdownMenu>
103
+ )
104
+ },
105
+ },
106
+ ]
@@ -1,48 +1,48 @@
1
- 'use client'
2
-
3
- import { DataTable, type FilterConfig } from '@/components/tables/data-table'
4
- import { ROLE_OPTIONS } from '@/config/roles'
5
- import { useUsers } from '../hooks/useUsers'
6
- import { columns } from '../columns'
7
-
8
- // Configuración de filtros para la tabla de usuarios
9
- const filters: FilterConfig[] = [
10
- {
11
- columnId: 'role',
12
- type: 'faceted',
13
- title: 'Rol',
14
- options: ROLE_OPTIONS,
15
- },
16
- {
17
- columnId: 'created_at',
18
- type: 'date-range',
19
- title: 'Fecha',
20
- },
21
- // Ejemplo de filtro numérico (rango):
22
- // {
23
- // columnId: 'balance',
24
- // type: 'number-range',
25
- // title: 'Balance',
26
- // format: 'currency',
27
- // currencySymbol: '$',
28
- // },
29
- ]
30
-
31
- export function UsersList() {
32
- const { users, isLoading } = useUsers()
33
-
34
- if (isLoading) {
35
- return <div>Cargando usuarios...</div>
36
- }
37
-
38
- return (
39
- <DataTable
40
- columns={columns}
41
- data={users}
42
- searchKey="name"
43
- searchPlaceholder="Buscar por nombre..."
44
- filters={filters}
45
- filterMode="inline"
46
- />
47
- )
48
- }
1
+ 'use client'
2
+
3
+ import { DataTable, type FilterConfig } from '@/components/tables/data-table'
4
+ import { ROLE_OPTIONS } from '@/config/roles'
5
+ import { useUsers } from '../hooks/useUsers'
6
+ import { columns } from '../columns'
7
+
8
+ // Configuración de filtros para la tabla de usuarios
9
+ const filters: FilterConfig[] = [
10
+ {
11
+ columnId: 'role',
12
+ type: 'faceted',
13
+ title: 'Rol',
14
+ options: ROLE_OPTIONS,
15
+ },
16
+ {
17
+ columnId: 'created_at',
18
+ type: 'date-range',
19
+ title: 'Fecha',
20
+ },
21
+ // Ejemplo de filtro numérico (rango):
22
+ // {
23
+ // columnId: 'balance',
24
+ // type: 'number-range',
25
+ // title: 'Balance',
26
+ // format: 'currency',
27
+ // currencySymbol: '$',
28
+ // },
29
+ ]
30
+
31
+ export function UsersList() {
32
+ const { users, isLoading } = useUsers()
33
+
34
+ if (isLoading) {
35
+ return <div>Cargando usuarios...</div>
36
+ }
37
+
38
+ return (
39
+ <DataTable
40
+ columns={columns}
41
+ data={users}
42
+ searchKey="name"
43
+ searchPlaceholder="Buscar por nombre..."
44
+ filters={filters}
45
+ filterMode="inline"
46
+ />
47
+ )
48
+ }
@@ -1,39 +1,39 @@
1
- 'use client'
2
-
3
- import { useShallow } from 'zustand/react/shallow'
4
- import { useUsersStore } from '../stores/useUsersStore'
5
- import { useUsersQueries } from './useUsersQueries'
6
- import { useUsersMutations } from './useUsersMutations'
7
-
8
- /**
9
- * Hook unificado para usuarios.
10
- * Los componentes SOLO deben importar este hook, nunca useUsersQueries o useUsersMutations directamente.
11
- */
12
- export function useUsers() {
13
- const state = useUsersStore(useShallow((s) => s.state))
14
- const actions = useUsersStore(useShallow((s) => s.actions))
15
-
16
- const queries = useUsersQueries(state.selectedUserId ?? undefined)
17
- const mutations = useUsersMutations()
18
-
19
- return {
20
- // State from store (UI state)
21
- ...state,
22
- ...actions,
23
-
24
- // Queries
25
- users: queries.users,
26
- user: queries.user,
27
- isLoading: queries.isLoading,
28
- isLoadingUser: queries.isLoadingUser,
29
- error: queries.error,
30
-
31
- // Mutations
32
- createUser: mutations.create,
33
- updateUser: mutations.update,
34
- deleteUser: mutations.delete,
35
- isCreating: mutations.isCreating,
36
- isUpdating: mutations.isUpdating,
37
- isDeleting: mutations.isDeleting,
38
- }
39
- }
1
+ 'use client'
2
+
3
+ import { useShallow } from 'zustand/react/shallow'
4
+ import { useUsersStore } from '../stores/useUsersStore'
5
+ import { useUsersQueries } from './useUsersQueries'
6
+ import { useUsersMutations } from './useUsersMutations'
7
+
8
+ /**
9
+ * Hook unificado para usuarios.
10
+ * Los componentes SOLO deben importar este hook, nunca useUsersQueries o useUsersMutations directamente.
11
+ */
12
+ export function useUsers() {
13
+ const state = useUsersStore(useShallow((s) => s.state))
14
+ const actions = useUsersStore(useShallow((s) => s.actions))
15
+
16
+ const queries = useUsersQueries(state.selectedUserId ?? undefined)
17
+ const mutations = useUsersMutations()
18
+
19
+ return {
20
+ // State from store (UI state)
21
+ ...state,
22
+ ...actions,
23
+
24
+ // Queries
25
+ users: queries.users,
26
+ user: queries.user,
27
+ isLoading: queries.isLoading,
28
+ isLoadingUser: queries.isLoadingUser,
29
+ error: queries.error,
30
+
31
+ // Mutations
32
+ createUser: mutations.create,
33
+ updateUser: mutations.update,
34
+ deleteUser: mutations.delete,
35
+ isCreating: mutations.isCreating,
36
+ isUpdating: mutations.isUpdating,
37
+ isDeleting: mutations.isDeleting,
38
+ }
39
+ }
@@ -1,55 +1,55 @@
1
- 'use client'
2
-
3
- import { useMutation, useQueryClient } from '@tanstack/react-query'
4
- import { toast } from 'sonner'
5
- import { createUser, updateUser, deleteUser } from '../actions/users-actions'
6
- import { usersKeys } from './useUsersQueries'
7
- import type { CreateAuthUserInput, UpdateAuthUserInput } from '../schemas/users.schema'
8
-
9
- export function useUsersMutations() {
10
- const queryClient = useQueryClient()
11
-
12
- const createMutation = useMutation({
13
- mutationFn: (input: CreateAuthUserInput) => createUser(input),
14
- onSuccess: () => {
15
- queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
16
- toast.success('Usuario creado correctamente')
17
- },
18
- onError: (error) => {
19
- toast.error(error.message)
20
- },
21
- })
22
-
23
- const updateMutation = useMutation({
24
- mutationFn: ({ id, input }: { id: string; input: UpdateAuthUserInput }) =>
25
- updateUser(id, input),
26
- onSuccess: (_, { id }) => {
27
- queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
28
- queryClient.invalidateQueries({ queryKey: usersKeys.detail(id) })
29
- toast.success('Usuario actualizado correctamente')
30
- },
31
- onError: (error) => {
32
- toast.error(error.message)
33
- },
34
- })
35
-
36
- const deleteMutation = useMutation({
37
- mutationFn: (id: string) => deleteUser(id),
38
- onSuccess: () => {
39
- queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
40
- toast.success('Usuario eliminado correctamente')
41
- },
42
- onError: (error) => {
43
- toast.error(error.message)
44
- },
45
- })
46
-
47
- return {
48
- create: createMutation.mutateAsync,
49
- update: updateMutation.mutateAsync,
50
- delete: deleteMutation.mutateAsync,
51
- isCreating: createMutation.isPending,
52
- isUpdating: updateMutation.isPending,
53
- isDeleting: deleteMutation.isPending,
54
- }
55
- }
1
+ 'use client'
2
+
3
+ import { useMutation, useQueryClient } from '@tanstack/react-query'
4
+ import { toast } from 'sonner'
5
+ import { createUser, updateUser, deleteUser } from '../actions/users-actions'
6
+ import { usersKeys } from './useUsersQueries'
7
+ import type { CreateAuthUserInput, UpdateAuthUserInput } from '../schemas/users.schema'
8
+
9
+ export function useUsersMutations() {
10
+ const queryClient = useQueryClient()
11
+
12
+ const createMutation = useMutation({
13
+ mutationFn: (input: CreateAuthUserInput) => createUser(input),
14
+ onSuccess: () => {
15
+ queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
16
+ toast.success('Usuario creado correctamente')
17
+ },
18
+ onError: (error) => {
19
+ toast.error(error.message)
20
+ },
21
+ })
22
+
23
+ const updateMutation = useMutation({
24
+ mutationFn: ({ id, input }: { id: string; input: UpdateAuthUserInput }) =>
25
+ updateUser(id, input),
26
+ onSuccess: (_, { id }) => {
27
+ queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
28
+ queryClient.invalidateQueries({ queryKey: usersKeys.detail(id) })
29
+ toast.success('Usuario actualizado correctamente')
30
+ },
31
+ onError: (error) => {
32
+ toast.error(error.message)
33
+ },
34
+ })
35
+
36
+ const deleteMutation = useMutation({
37
+ mutationFn: (id: string) => deleteUser(id),
38
+ onSuccess: () => {
39
+ queryClient.invalidateQueries({ queryKey: usersKeys.lists() })
40
+ toast.success('Usuario eliminado correctamente')
41
+ },
42
+ onError: (error) => {
43
+ toast.error(error.message)
44
+ },
45
+ })
46
+
47
+ return {
48
+ create: createMutation.mutateAsync,
49
+ update: updateMutation.mutateAsync,
50
+ delete: deleteMutation.mutateAsync,
51
+ isCreating: createMutation.isPending,
52
+ isUpdating: updateMutation.isPending,
53
+ isDeleting: deleteMutation.isPending,
54
+ }
55
+ }
@@ -1,35 +1,35 @@
1
- 'use client'
2
-
3
- import { useQuery } from '@tanstack/react-query'
4
- import { getUsers, getUserById } from '../actions/users-actions'
5
-
6
- export const usersKeys = {
7
- all: ['users'] as const,
8
- lists: () => [...usersKeys.all, 'list'] as const,
9
- list: (filters: Record<string, unknown>) => [...usersKeys.lists(), filters] as const,
10
- details: () => [...usersKeys.all, 'detail'] as const,
11
- detail: (id: string) => [...usersKeys.details(), id] as const,
12
- }
13
-
14
- export function useUsersQueries(userId?: string) {
15
- const usersQuery = useQuery({
16
- queryKey: usersKeys.lists(),
17
- queryFn: () => getUsers(),
18
- })
19
-
20
- const userQuery = useQuery({
21
- queryKey: usersKeys.detail(userId!),
22
- queryFn: () => getUserById(userId!),
23
- enabled: !!userId,
24
- })
25
-
26
- return {
27
- users: usersQuery.data ?? [],
28
- user: userQuery.data,
29
- isLoading: usersQuery.isLoading,
30
- isLoadingUser: userQuery.isLoading,
31
- error: usersQuery.error,
32
- usersQuery,
33
- userQuery,
34
- }
35
- }
1
+ 'use client'
2
+
3
+ import { useQuery } from '@tanstack/react-query'
4
+ import { getUsers, getUserById } from '../actions/users-actions'
5
+
6
+ export const usersKeys = {
7
+ all: ['users'] as const,
8
+ lists: () => [...usersKeys.all, 'list'] as const,
9
+ list: (filters: Record<string, unknown>) => [...usersKeys.lists(), filters] as const,
10
+ details: () => [...usersKeys.all, 'detail'] as const,
11
+ detail: (id: string) => [...usersKeys.details(), id] as const,
12
+ }
13
+
14
+ export function useUsersQueries(userId?: string) {
15
+ const usersQuery = useQuery({
16
+ queryKey: usersKeys.lists(),
17
+ queryFn: () => getUsers(),
18
+ })
19
+
20
+ const userQuery = useQuery({
21
+ queryKey: usersKeys.detail(userId!),
22
+ queryFn: () => getUserById(userId!),
23
+ enabled: !!userId,
24
+ })
25
+
26
+ return {
27
+ users: usersQuery.data ?? [],
28
+ user: userQuery.data,
29
+ isLoading: usersQuery.isLoading,
30
+ isLoadingUser: userQuery.isLoading,
31
+ error: usersQuery.error,
32
+ usersQuery,
33
+ userQuery,
34
+ }
35
+ }
@@ -1,30 +1,30 @@
1
- // Components
2
- export { UsersList } from './components/users-list'
3
-
4
- // Hooks - Solo exportar el hook unificado
5
- export { useUsers } from './hooks/useUsers'
6
-
7
- // Schemas
8
- export {
9
- userSchema,
10
- createUserSchema,
11
- updateUserSchema,
12
- createAuthUserSchema,
13
- updateAuthUserSchema,
14
- } from './schemas/users.schema'
15
- export type {
16
- User,
17
- CreateUserInput,
18
- UpdateUserInput,
19
- CreateAuthUserInput,
20
- UpdateAuthUserInput,
21
- } from './schemas/users.schema'
22
-
23
- // Types - Auth User types
24
- export type { UserRole, UserAppMetadata, UserMetadata } from './types/auth-user.types'
25
-
26
- // Utils - Mappers
27
- export { mapAuthUserToUser, mapAuthUsersToUsers } from './utils/user-mapper'
28
-
29
- // Columns
30
- export { columns as usersColumns } from './columns'
1
+ // Components
2
+ export { UsersList } from './components/users-list'
3
+
4
+ // Hooks - Solo exportar el hook unificado
5
+ export { useUsers } from './hooks/useUsers'
6
+
7
+ // Schemas
8
+ export {
9
+ userSchema,
10
+ createUserSchema,
11
+ updateUserSchema,
12
+ createAuthUserSchema,
13
+ updateAuthUserSchema,
14
+ } from './schemas/users.schema'
15
+ export type {
16
+ User,
17
+ CreateUserInput,
18
+ UpdateUserInput,
19
+ CreateAuthUserInput,
20
+ UpdateAuthUserInput,
21
+ } from './schemas/users.schema'
22
+
23
+ // Types - Auth User types
24
+ export type { UserRole, UserAppMetadata, UserMetadata } from './types/auth-user.types'
25
+
26
+ // Utils - Mappers
27
+ export { mapAuthUserToUser, mapAuthUsersToUsers } from './utils/user-mapper'
28
+
29
+ // Columns
30
+ export { columns as usersColumns } from './columns'