@create-lft-app/nextjs 2.0.1 → 3.0.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/package.json +3 -2
- package/template/CLAUDE.md +279 -0
- package/template/drizzle.config.ts +12 -0
- package/template/package.json +31 -6
- package/template/proxy.ts +12 -0
- package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -0
- package/template/src/app/(auth)/dashboard/page.tsx +9 -0
- package/template/src/app/(auth)/layout.tsx +7 -0
- package/template/src/app/(auth)/users/page.tsx +9 -0
- package/template/src/app/(auth)/users/users-content.tsx +26 -0
- package/template/src/app/(public)/layout.tsx +7 -0
- package/template/src/app/(public)/login/page.tsx +17 -0
- package/template/src/app/api/webhooks/route.ts +20 -0
- package/template/src/app/layout.tsx +13 -12
- package/template/src/app/providers.tsx +27 -0
- package/template/src/components/layout/{midday-sidebar.tsx → sidebar.tsx} +2 -7
- package/template/src/components/tables/data-table-column-header.tsx +68 -0
- package/template/src/components/tables/data-table-pagination.tsx +99 -0
- package/template/src/components/tables/data-table-toolbar.tsx +50 -0
- package/template/src/components/tables/data-table-view-options.tsx +59 -0
- package/template/src/components/tables/data-table.tsx +128 -0
- package/template/src/components/tables/index.ts +5 -0
- package/template/src/components/ui/animations/index.ts +44 -0
- package/template/src/components/ui/button.tsx +50 -21
- package/template/src/components/ui/card.tsx +27 -3
- package/template/src/components/ui/dialog.tsx +38 -35
- package/template/src/components/ui/motion.tsx +197 -0
- package/template/src/components/ui/page-transition.tsx +166 -0
- package/template/src/components/ui/sheet.tsx +65 -41
- package/template/src/config/navigation.ts +69 -0
- package/template/src/config/site.ts +12 -0
- package/template/src/db/index.ts +12 -0
- package/template/src/db/schema/index.ts +1 -0
- package/template/src/db/schema/users.ts +16 -0
- package/template/src/db/seed.ts +39 -0
- package/template/src/hooks/index.ts +3 -0
- package/template/src/hooks/useDataTable.ts +82 -0
- package/template/src/hooks/useDebounce.ts +49 -0
- package/template/src/hooks/useMediaQuery.ts +36 -0
- package/template/src/lib/date/config.ts +34 -0
- package/template/src/lib/date/formatters.ts +120 -0
- package/template/src/lib/date/index.ts +19 -0
- package/template/src/lib/excel/exporter.ts +89 -0
- package/template/src/lib/excel/index.ts +14 -0
- package/template/src/lib/excel/parser.ts +96 -0
- package/template/src/lib/query-client.ts +35 -0
- package/template/src/lib/supabase/client.ts +5 -2
- package/template/src/lib/supabase/proxy.ts +67 -0
- package/template/src/lib/supabase/server.ts +6 -4
- package/template/src/lib/supabase/types.ts +53 -0
- package/template/src/lib/validations/common.ts +75 -0
- package/template/src/lib/validations/index.ts +20 -0
- package/template/src/modules/auth/actions/auth-actions.ts +51 -4
- package/template/src/modules/auth/components/login-form.tsx +68 -0
- package/template/src/modules/auth/hooks/useAuth.ts +38 -0
- package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -0
- package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -0
- package/template/src/modules/auth/index.ts +12 -0
- package/template/src/modules/auth/schemas/auth.schema.ts +32 -0
- package/template/src/modules/auth/stores/useAuthStore.ts +37 -0
- package/template/src/modules/users/actions/users-actions.ts +94 -0
- package/template/src/modules/users/columns.tsx +86 -0
- package/template/src/modules/users/components/users-list.tsx +22 -0
- package/template/src/modules/users/hooks/useUsers.ts +39 -0
- package/template/src/modules/users/hooks/useUsersMutations.ts +55 -0
- package/template/src/modules/users/hooks/useUsersQueries.ts +35 -0
- package/template/src/modules/users/index.ts +12 -0
- package/template/src/modules/users/schemas/users.schema.ts +23 -0
- package/template/src/modules/users/stores/useUsersStore.ts +60 -0
- package/template/src/stores/index.ts +1 -0
- package/template/src/stores/useUiStore.ts +55 -0
- package/template/src/types/api.ts +28 -0
- package/template/src/types/index.ts +2 -0
- package/template/src/types/table.ts +34 -0
- package/template/supabase/config.toml +94 -0
- package/template/tsconfig.json +2 -1
- package/template/tsconfig.tsbuildinfo +1 -0
- package/template/src/app/dashboard/page.tsx +0 -111
- package/template/src/components/dashboard/widget.tsx +0 -113
- package/template/src/test/test.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@create-lft-app/nextjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Next.js template para proyectos LFT con Midday Design System",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"!template/node_modules",
|
|
13
13
|
"!template/.next",
|
|
14
14
|
"!template/package-lock.json",
|
|
15
|
+
"!template/pnpm-lock.yaml",
|
|
15
16
|
"!template/next-env.d.ts",
|
|
16
17
|
"!template/.env*"
|
|
17
18
|
],
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"license": "MIT",
|
|
31
32
|
"repository": {
|
|
32
33
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/somosdcg/next-boiler"
|
|
34
35
|
},
|
|
35
36
|
"publishConfig": {
|
|
36
37
|
"access": "public"
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## 🚨 CRITICAL: MANDATORY ARCHITECTURAL PATTERN 🚨
|
|
6
|
+
|
|
7
|
+
**⚠️ BEFORE WRITING ANY CODE, YOU MUST READ AND FOLLOW THIS PATTERN ⚠️**
|
|
8
|
+
|
|
9
|
+
This section is **NON-NEGOTIABLE** and MUST be followed in **100% of cases** when creating or modifying features in this codebase.
|
|
10
|
+
|
|
11
|
+
## ⚠️ The Most Common Mistake (DON'T DO THIS!)
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// ❌ WRONG - NEVER IMPORT MUTATIONS OR QUERIES DIRECTLY IN COMPONENTS
|
|
15
|
+
import { useEntityMutations } from '@/modules/[entity]/hooks/useEntityMutations'
|
|
16
|
+
import { useEntityQueries } from '@/modules/[entity]/hooks/useEntityQueries'
|
|
17
|
+
|
|
18
|
+
export function MyComponent() {
|
|
19
|
+
const { createEntity } = useEntityMutations() // ❌ ARCHITECTURAL VIOLATION
|
|
20
|
+
const { entities } = useEntityQueries() // ❌ ARCHITECTURAL VIOLATION
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ✅ CORRECT - ALWAYS USE THE UNIFIED HOOK
|
|
24
|
+
import { useEntity } from '@/modules/[entity]/hooks/useEntity'
|
|
25
|
+
|
|
26
|
+
export function MyComponent() {
|
|
27
|
+
const { createEntity, entities } = useEntity() // ✅ CORRECT PATTERN
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 🚨 MANDATORY Pattern: Store → Queries → Mutations → Unified Hook → Component
|
|
32
|
+
|
|
33
|
+
**THIS IS THE ONLY ACCEPTABLE ARCHITECTURE FOR THIS CODEBASE.**
|
|
34
|
+
|
|
35
|
+
Every module MUST follow this exact structure:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
modules/
|
|
39
|
+
└── [entity]/
|
|
40
|
+
├── actions/
|
|
41
|
+
│ └── [entity]-actions.ts # Server actions with RLS
|
|
42
|
+
├── components/
|
|
43
|
+
│ ├── [entity]-list.tsx # Component imports ONLY unified hook
|
|
44
|
+
│ ├── [entity]-form.tsx
|
|
45
|
+
│ └── [entity]-detail.tsx
|
|
46
|
+
├── hooks/
|
|
47
|
+
│ ├── use[Entity]Queries.ts # TanStack Query (read operations)
|
|
48
|
+
│ ├── use[Entity]Mutations.ts # TanStack Mutations (write operations)
|
|
49
|
+
│ └── use[Entity].ts # UNIFIED HOOK (components use THIS)
|
|
50
|
+
├── stores/
|
|
51
|
+
│ └── use[Entity]Store.ts # Zustand store (UI state only)
|
|
52
|
+
├── schemas/
|
|
53
|
+
│ └── [entity].schema.ts # Zod validation schemas
|
|
54
|
+
├── columns.tsx # TanStack Table column definitions (if needed)
|
|
55
|
+
└── index.ts # Barrel export
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 📋 Mandatory Implementation Checklist
|
|
59
|
+
|
|
60
|
+
Before writing ANY code, verify this checklist:
|
|
61
|
+
|
|
62
|
+
- [ ] **Step 1**: Create Drizzle schema in `db/schema/[entity].ts`
|
|
63
|
+
- [ ] **Step 2**: Create Zod schemas in `modules/[entity]/schemas/[entity].schema.ts`
|
|
64
|
+
- [ ] **Step 3**: Create Server actions in `modules/[entity]/actions/[entity]-actions.ts`
|
|
65
|
+
- [ ] **Step 4**: Create Zustand store with separate state/actions selectors using `useShallow` in `modules/[entity]/stores/`
|
|
66
|
+
- [ ] **Step 5**: Create Queries hook in `modules/[entity]/hooks/use[Entity]Queries.ts`
|
|
67
|
+
- [ ] **Step 6**: Create Mutations hook with cache invalidation in `modules/[entity]/hooks/use[Entity]Mutations.ts`
|
|
68
|
+
- [ ] **Step 7**: Create Unified hook that combines store + queries + mutations in `modules/[entity]/hooks/use[Entity].ts`
|
|
69
|
+
- [ ] **Step 8**: Components ONLY import the unified hook (NEVER queries/mutations directly)
|
|
70
|
+
- [ ] **Step 9**: Test with `pnpm typecheck && pnpm lint` before committing
|
|
71
|
+
|
|
72
|
+
## 🔴 NEVER DO THIS (Anti-Patterns)
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// ❌ NEVER: Import queries or mutations directly in components
|
|
76
|
+
import { useEntityQueries } from '@/modules/[entity]/hooks/useEntityQueries'
|
|
77
|
+
import { useEntityMutations } from '@/modules/[entity]/hooks/useEntityMutations'
|
|
78
|
+
|
|
79
|
+
// ❌ NEVER: Consume entire store (causes re-renders)
|
|
80
|
+
const store = useEntityStore()
|
|
81
|
+
|
|
82
|
+
// ❌ NEVER: Manual data fetching in useEffect
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
fetch('/api/entities').then(setData)
|
|
85
|
+
}, [])
|
|
86
|
+
|
|
87
|
+
// ❌ NEVER: Server actions without authenticated user context (RLS won't work)
|
|
88
|
+
export async function deleteEntity(id: string) {
|
|
89
|
+
const supabase = createClient() // Anonymous client - RLS will fail!
|
|
90
|
+
await supabase.from('entities').delete().eq('id', id)
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## ✅ ALWAYS DO THIS (Correct Patterns)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// ✅ ALWAYS: Use the unified hook in components
|
|
98
|
+
import { useEntity } from '@/modules/[entity]/hooks/useEntity'
|
|
99
|
+
|
|
100
|
+
// ✅ ALWAYS: Use separate selectors with useShallow
|
|
101
|
+
import { useEntityStore } from '@/modules/[entity]/stores/useEntityStore'
|
|
102
|
+
import { useShallow } from 'zustand/react/shallow'
|
|
103
|
+
|
|
104
|
+
const state = useEntityStore(useShallow((s) => s.state))
|
|
105
|
+
const actions = useEntityStore(useShallow((s) => s.actions))
|
|
106
|
+
|
|
107
|
+
// ✅ ALWAYS: Use TanStack Query for data fetching
|
|
108
|
+
const { data } = useQuery({ queryKey: ['entities'], queryFn: fetchEntities })
|
|
109
|
+
|
|
110
|
+
// ✅ ALWAYS: Use authenticated Supabase client in server actions (RLS enforced)
|
|
111
|
+
'use server'
|
|
112
|
+
|
|
113
|
+
import { createClient } from '@/lib/supabase/server'
|
|
114
|
+
|
|
115
|
+
export async function deleteEntity(id: string) {
|
|
116
|
+
const supabase = await createClient() // Authenticated client with user context
|
|
117
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
118
|
+
|
|
119
|
+
if (!user) throw new Error('Unauthorized')
|
|
120
|
+
|
|
121
|
+
// RLS policies will automatically enforce authorization
|
|
122
|
+
const { error } = await supabase.from('entities').delete().eq('id', id)
|
|
123
|
+
|
|
124
|
+
if (error) throw error
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 📁 Complete Project Structure
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
src/
|
|
132
|
+
├── app/
|
|
133
|
+
│ ├── (auth)/ # Rutas autenticadas
|
|
134
|
+
│ │ ├── dashboard/
|
|
135
|
+
│ │ │ └── page.tsx
|
|
136
|
+
│ │ └── layout.tsx
|
|
137
|
+
│ ├── (public)/ # Rutas públicas
|
|
138
|
+
│ │ ├── login/
|
|
139
|
+
│ │ │ └── page.tsx
|
|
140
|
+
│ │ └── layout.tsx
|
|
141
|
+
│ ├── api/
|
|
142
|
+
│ │ └── webhooks/
|
|
143
|
+
│ │ └── route.ts
|
|
144
|
+
│ ├── layout.tsx
|
|
145
|
+
│ ├── page.tsx
|
|
146
|
+
│ └── providers.tsx
|
|
147
|
+
│
|
|
148
|
+
├── modules/ # Feature-based modules
|
|
149
|
+
│ ├── auth/
|
|
150
|
+
│ │ ├── actions/
|
|
151
|
+
│ │ │ └── auth-actions.ts
|
|
152
|
+
│ │ ├── components/
|
|
153
|
+
│ │ │ └── login-form.tsx
|
|
154
|
+
│ │ ├── hooks/
|
|
155
|
+
│ │ │ ├── useAuthQueries.ts
|
|
156
|
+
│ │ │ ├── useAuthMutations.ts
|
|
157
|
+
│ │ │ └── useAuth.ts
|
|
158
|
+
│ │ ├── stores/
|
|
159
|
+
│ │ │ └── useAuthStore.ts
|
|
160
|
+
│ │ ├── schemas/
|
|
161
|
+
│ │ │ └── auth.schema.ts
|
|
162
|
+
│ │ └── index.ts
|
|
163
|
+
│ └── users/
|
|
164
|
+
│ ├── actions/
|
|
165
|
+
│ │ └── users-actions.ts
|
|
166
|
+
│ ├── components/
|
|
167
|
+
│ │ ├── users-list.tsx
|
|
168
|
+
│ │ ├── users-form.tsx
|
|
169
|
+
│ │ └── users-detail.tsx
|
|
170
|
+
│ ├── hooks/
|
|
171
|
+
│ │ ├── useUsersQueries.ts
|
|
172
|
+
│ │ ├── useUsersMutations.ts
|
|
173
|
+
│ │ └── useUsers.ts
|
|
174
|
+
│ ├── stores/
|
|
175
|
+
│ │ └── useUsersStore.ts
|
|
176
|
+
│ ├── schemas/
|
|
177
|
+
│ │ └── users.schema.ts
|
|
178
|
+
│ ├── columns.tsx
|
|
179
|
+
│ └── index.ts
|
|
180
|
+
│
|
|
181
|
+
├── components/
|
|
182
|
+
│ ├── ui/ # Radix custom components
|
|
183
|
+
│ │ ├── button.tsx
|
|
184
|
+
│ │ ├── dialog.tsx
|
|
185
|
+
│ │ └── index.ts
|
|
186
|
+
│ ├── tables/ # TanStack Table components
|
|
187
|
+
│ │ ├── data-table.tsx
|
|
188
|
+
│ │ ├── data-table-pagination.tsx
|
|
189
|
+
│ │ ├── data-table-toolbar.tsx
|
|
190
|
+
│ │ ├── data-table-column-header.tsx
|
|
191
|
+
│ │ └── index.ts
|
|
192
|
+
│ ├── layout/
|
|
193
|
+
│ │ ├── header.tsx
|
|
194
|
+
│ │ └── sidebar.tsx
|
|
195
|
+
│ └── shared/
|
|
196
|
+
│ └── ...
|
|
197
|
+
│
|
|
198
|
+
├── stores/ # Global Zustand stores
|
|
199
|
+
│ ├── useUiStore.ts
|
|
200
|
+
│ └── index.ts
|
|
201
|
+
│
|
|
202
|
+
├── hooks/ # Global hooks
|
|
203
|
+
│ ├── useMediaQuery.ts
|
|
204
|
+
│ ├── useDebounce.ts
|
|
205
|
+
│ └── useDataTable.ts
|
|
206
|
+
│
|
|
207
|
+
├── lib/
|
|
208
|
+
│ ├── supabase/
|
|
209
|
+
│ │ ├── client.ts
|
|
210
|
+
│ │ ├── server.ts
|
|
211
|
+
│ │ ├── proxy.ts
|
|
212
|
+
│ │ └── types.ts
|
|
213
|
+
│ ├── excel/
|
|
214
|
+
│ │ ├── parser.ts
|
|
215
|
+
│ │ ├── exporter.ts
|
|
216
|
+
│ │ └── index.ts
|
|
217
|
+
│ ├── date/
|
|
218
|
+
│ │ ├── config.ts
|
|
219
|
+
│ │ ├── formatters.ts
|
|
220
|
+
│ │ └── index.ts
|
|
221
|
+
│ ├── validations/
|
|
222
|
+
│ │ ├── common.ts
|
|
223
|
+
│ │ └── index.ts
|
|
224
|
+
│ ├── query-client.ts
|
|
225
|
+
│ └── utils.ts
|
|
226
|
+
│
|
|
227
|
+
├── db/
|
|
228
|
+
│ ├── schema/
|
|
229
|
+
│ │ ├── users.ts
|
|
230
|
+
│ │ └── index.ts
|
|
231
|
+
│ ├── migrations/
|
|
232
|
+
│ ├── index.ts
|
|
233
|
+
│ └── seed.ts
|
|
234
|
+
│
|
|
235
|
+
├── types/
|
|
236
|
+
│ ├── api.ts
|
|
237
|
+
│ ├── table.ts
|
|
238
|
+
│ └── index.ts
|
|
239
|
+
│
|
|
240
|
+
├── config/
|
|
241
|
+
│ ├── site.ts
|
|
242
|
+
│ └── navigation.ts
|
|
243
|
+
│
|
|
244
|
+
└── styles/
|
|
245
|
+
└── globals.css
|
|
246
|
+
|
|
247
|
+
supabase/
|
|
248
|
+
├── functions/
|
|
249
|
+
│ ├── process-excel/
|
|
250
|
+
│ │ └── index.ts
|
|
251
|
+
│ └── send-notification/
|
|
252
|
+
│ └── index.ts
|
|
253
|
+
├── migrations/
|
|
254
|
+
└── config.toml
|
|
255
|
+
|
|
256
|
+
# Root files
|
|
257
|
+
├── .env.local
|
|
258
|
+
├── .env.example
|
|
259
|
+
├── drizzle.config.ts
|
|
260
|
+
├── proxy.ts
|
|
261
|
+
├── next.config.ts
|
|
262
|
+
├── package.json
|
|
263
|
+
├── tsconfig.json
|
|
264
|
+
└── tailwind.config.ts
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## 🛠️ Tech Stack
|
|
268
|
+
|
|
269
|
+
- **Framework**: Next.js 16 (App Router)
|
|
270
|
+
- **State Management**: Zustand 5
|
|
271
|
+
- **Data Fetching**: TanStack Query 5
|
|
272
|
+
- **Tables**: TanStack Table 8
|
|
273
|
+
- **Database**: Supabase + Drizzle ORM
|
|
274
|
+
- **Validation**: Zod
|
|
275
|
+
- **UI**: Radix UI + Tailwind CSS
|
|
276
|
+
- **Forms**: React Hook Form + Zod resolver
|
|
277
|
+
- **Excel**: xlsx (SheetJS)
|
|
278
|
+
- **Dates**: dayjs
|
|
279
|
+
- **Package Manager**: pnpm
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
schema: './src/db/schema/index.ts',
|
|
5
|
+
out: './src/db/migrations',
|
|
6
|
+
dialect: 'postgresql',
|
|
7
|
+
dbCredentials: {
|
|
8
|
+
url: process.env.DATABASE_URL!,
|
|
9
|
+
},
|
|
10
|
+
verbose: true,
|
|
11
|
+
strict: true,
|
|
12
|
+
})
|
package/template/package.json
CHANGED
|
@@ -6,12 +6,18 @@
|
|
|
6
6
|
"dev": "next dev --turbopack",
|
|
7
7
|
"build": "next build",
|
|
8
8
|
"start": "next start",
|
|
9
|
-
"lint": "next lint"
|
|
9
|
+
"lint": "next lint",
|
|
10
|
+
"db:generate": "drizzle-kit generate",
|
|
11
|
+
"db:migrate": "drizzle-kit migrate",
|
|
12
|
+
"db:push": "drizzle-kit push",
|
|
13
|
+
"db:studio": "drizzle-kit studio",
|
|
14
|
+
"db:seed": "tsx src/db/seed.ts"
|
|
10
15
|
},
|
|
11
16
|
"dependencies": {
|
|
12
17
|
"next": "^16.0.0",
|
|
13
18
|
"react": "^19.0.0",
|
|
14
19
|
"react-dom": "^19.0.0",
|
|
20
|
+
|
|
15
21
|
"@radix-ui/react-accordion": "^1.2.0",
|
|
16
22
|
"@radix-ui/react-alert-dialog": "^1.1.0",
|
|
17
23
|
"@radix-ui/react-avatar": "^1.1.0",
|
|
@@ -31,21 +37,38 @@
|
|
|
31
37
|
"@radix-ui/react-switch": "^1.1.0",
|
|
32
38
|
"@radix-ui/react-tabs": "^1.1.0",
|
|
33
39
|
"@radix-ui/react-tooltip": "^1.1.0",
|
|
40
|
+
|
|
34
41
|
"class-variance-authority": "^0.7.0",
|
|
35
42
|
"clsx": "^2.1.0",
|
|
36
43
|
"tailwind-merge": "^2.5.0",
|
|
37
44
|
"lucide-react": "^0.400.0",
|
|
45
|
+
|
|
38
46
|
"react-hook-form": "^7.50.0",
|
|
39
47
|
"@hookform/resolvers": "^3.9.0",
|
|
48
|
+
"zod": "^3.23.0",
|
|
49
|
+
|
|
40
50
|
"cmdk": "^1.0.0",
|
|
41
|
-
"react-day-picker": "^9.0.0",
|
|
42
|
-
"date-fns": "^4.0.0",
|
|
43
51
|
"sonner": "^1.5.0",
|
|
44
52
|
"next-themes": "^0.4.0",
|
|
45
53
|
"tw-animate-css": "^1.2.0",
|
|
46
|
-
"
|
|
54
|
+
"react-day-picker": "^9.4.0",
|
|
55
|
+
|
|
47
56
|
"@supabase/supabase-js": "^2.45.0",
|
|
48
|
-
"@supabase/ssr": "^0.5.0"
|
|
57
|
+
"@supabase/ssr": "^0.5.0",
|
|
58
|
+
|
|
59
|
+
"zustand": "^5.0.0",
|
|
60
|
+
|
|
61
|
+
"@tanstack/react-query": "^5.60.0",
|
|
62
|
+
"@tanstack/react-query-devtools": "^5.60.0",
|
|
63
|
+
"@tanstack/react-table": "^8.20.0",
|
|
64
|
+
|
|
65
|
+
"drizzle-orm": "^0.36.0",
|
|
66
|
+
"postgres": "^3.4.0",
|
|
67
|
+
|
|
68
|
+
"xlsx": "^0.18.5",
|
|
69
|
+
"dayjs": "^1.11.0",
|
|
70
|
+
|
|
71
|
+
"framer-motion": "^11.15.0"
|
|
49
72
|
},
|
|
50
73
|
"devDependencies": {
|
|
51
74
|
"@types/node": "^22.0.0",
|
|
@@ -56,6 +79,8 @@
|
|
|
56
79
|
"eslint-config-next": "^16.0.0",
|
|
57
80
|
"tailwindcss": "^4.0.0",
|
|
58
81
|
"@tailwindcss/postcss": "^4.0.0",
|
|
59
|
-
"tailwindcss-animate": "^1.0.0"
|
|
82
|
+
"tailwindcss-animate": "^1.0.0",
|
|
83
|
+
"drizzle-kit": "^0.28.0",
|
|
84
|
+
"tsx": "^4.19.0"
|
|
60
85
|
}
|
|
61
86
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type NextRequest } from 'next/server'
|
|
2
|
+
import { updateSession } from '@/lib/supabase/proxy'
|
|
3
|
+
|
|
4
|
+
export async function proxy(request: NextRequest) {
|
|
5
|
+
return await updateSession(request)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const config = {
|
|
9
|
+
unstable_allowDynamic: [
|
|
10
|
+
'/node_modules/@supabase/**',
|
|
11
|
+
],
|
|
12
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
PageTransition,
|
|
5
|
+
PageHeader,
|
|
6
|
+
PageTitle,
|
|
7
|
+
PageDescription,
|
|
8
|
+
MotionDiv,
|
|
9
|
+
staggerContainer,
|
|
10
|
+
staggerItem,
|
|
11
|
+
} from '@/components/ui/animations'
|
|
12
|
+
import {
|
|
13
|
+
Card,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CardDescription,
|
|
17
|
+
CardContent,
|
|
18
|
+
} from '@/components/ui/card'
|
|
19
|
+
import { Users, FileText, TrendingUp, DollarSign } from 'lucide-react'
|
|
20
|
+
|
|
21
|
+
interface DashboardContentProps {
|
|
22
|
+
userEmail: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stats = [
|
|
26
|
+
{
|
|
27
|
+
title: 'Usuarios Totales',
|
|
28
|
+
value: '2,350',
|
|
29
|
+
description: '+180 este mes',
|
|
30
|
+
icon: Users,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: 'Documentos',
|
|
34
|
+
value: '12,234',
|
|
35
|
+
description: '+1,234 esta semana',
|
|
36
|
+
icon: FileText,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: 'Crecimiento',
|
|
40
|
+
value: '+23%',
|
|
41
|
+
description: 'vs mes anterior',
|
|
42
|
+
icon: TrendingUp,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
title: 'Ingresos',
|
|
46
|
+
value: '$45,231',
|
|
47
|
+
description: '+20.1% este mes',
|
|
48
|
+
icon: DollarSign,
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
export function DashboardContent({ userEmail }: DashboardContentProps) {
|
|
53
|
+
return (
|
|
54
|
+
<PageTransition className="flex flex-1 flex-col gap-6 p-6">
|
|
55
|
+
<PageHeader>
|
|
56
|
+
<PageTitle>Dashboard</PageTitle>
|
|
57
|
+
<PageDescription>Bienvenido, {userEmail}</PageDescription>
|
|
58
|
+
</PageHeader>
|
|
59
|
+
|
|
60
|
+
<MotionDiv
|
|
61
|
+
variants={staggerContainer}
|
|
62
|
+
initial="hidden"
|
|
63
|
+
animate="visible"
|
|
64
|
+
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"
|
|
65
|
+
>
|
|
66
|
+
{stats.map((stat) => (
|
|
67
|
+
<MotionDiv key={stat.title} variants={staggerItem}>
|
|
68
|
+
<Card className="h-full">
|
|
69
|
+
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
70
|
+
<CardTitle className="text-sm font-medium">
|
|
71
|
+
{stat.title}
|
|
72
|
+
</CardTitle>
|
|
73
|
+
<stat.icon className="h-4 w-4 text-muted-foreground" />
|
|
74
|
+
</CardHeader>
|
|
75
|
+
<CardContent>
|
|
76
|
+
<div className="text-2xl font-bold">{stat.value}</div>
|
|
77
|
+
<CardDescription>{stat.description}</CardDescription>
|
|
78
|
+
</CardContent>
|
|
79
|
+
</Card>
|
|
80
|
+
</MotionDiv>
|
|
81
|
+
))}
|
|
82
|
+
</MotionDiv>
|
|
83
|
+
|
|
84
|
+
<MotionDiv
|
|
85
|
+
variants={staggerContainer}
|
|
86
|
+
initial="hidden"
|
|
87
|
+
animate="visible"
|
|
88
|
+
className="grid grid-cols-1 lg:grid-cols-2 gap-4"
|
|
89
|
+
>
|
|
90
|
+
<MotionDiv variants={staggerItem}>
|
|
91
|
+
<Card className="h-[400px]">
|
|
92
|
+
<CardHeader>
|
|
93
|
+
<CardTitle>Actividad Reciente</CardTitle>
|
|
94
|
+
<CardDescription>
|
|
95
|
+
Últimas acciones en el sistema
|
|
96
|
+
</CardDescription>
|
|
97
|
+
</CardHeader>
|
|
98
|
+
<CardContent>
|
|
99
|
+
<div className="flex items-center justify-center h-[280px] text-muted-foreground">
|
|
100
|
+
Gráfico de actividad
|
|
101
|
+
</div>
|
|
102
|
+
</CardContent>
|
|
103
|
+
</Card>
|
|
104
|
+
</MotionDiv>
|
|
105
|
+
|
|
106
|
+
<MotionDiv variants={staggerItem}>
|
|
107
|
+
<Card className="h-[400px]">
|
|
108
|
+
<CardHeader>
|
|
109
|
+
<CardTitle>Resumen</CardTitle>
|
|
110
|
+
<CardDescription>
|
|
111
|
+
Estadísticas generales del período
|
|
112
|
+
</CardDescription>
|
|
113
|
+
</CardHeader>
|
|
114
|
+
<CardContent>
|
|
115
|
+
<div className="flex items-center justify-center h-[280px] text-muted-foreground">
|
|
116
|
+
Gráfico de resumen
|
|
117
|
+
</div>
|
|
118
|
+
</CardContent>
|
|
119
|
+
</Card>
|
|
120
|
+
</MotionDiv>
|
|
121
|
+
</MotionDiv>
|
|
122
|
+
</PageTransition>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createClient } from '@/lib/supabase/server'
|
|
2
|
+
import { DashboardContent } from './dashboard-content'
|
|
3
|
+
|
|
4
|
+
export default async function DashboardPage() {
|
|
5
|
+
const supabase = await createClient()
|
|
6
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
7
|
+
|
|
8
|
+
return <DashboardContent userEmail={user?.email ?? ''} />
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createClient } from '@/lib/supabase/server'
|
|
2
|
+
import { UsersContent } from './users-content'
|
|
3
|
+
|
|
4
|
+
export default async function UsersPage() {
|
|
5
|
+
const supabase = await createClient()
|
|
6
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
7
|
+
|
|
8
|
+
return <UsersContent userEmail={user?.email ?? ''} />
|
|
9
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
PageTransition,
|
|
5
|
+
PageHeader,
|
|
6
|
+
PageTitle,
|
|
7
|
+
PageDescription,
|
|
8
|
+
} from '@/components/ui/animations'
|
|
9
|
+
import { UsersList } from '@/modules/users'
|
|
10
|
+
|
|
11
|
+
interface UsersContentProps {
|
|
12
|
+
userEmail: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function UsersContent({ userEmail }: UsersContentProps) {
|
|
16
|
+
return (
|
|
17
|
+
<PageTransition className="flex flex-1 flex-col gap-6 p-6">
|
|
18
|
+
<PageHeader>
|
|
19
|
+
<PageTitle>Usuarios</PageTitle>
|
|
20
|
+
<PageDescription>Gestión de usuarios del sistema</PageDescription>
|
|
21
|
+
</PageHeader>
|
|
22
|
+
|
|
23
|
+
<UsersList />
|
|
24
|
+
</PageTransition>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LoginForm } from '@/modules/auth/components/login-form'
|
|
2
|
+
|
|
3
|
+
export default function LoginPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex min-h-screen items-center justify-center">
|
|
6
|
+
<div className="w-full max-w-md space-y-8 px-4">
|
|
7
|
+
<div className="text-center">
|
|
8
|
+
<h1 className="text-2xl font-bold">Iniciar sesión</h1>
|
|
9
|
+
<p className="mt-2 text-muted-foreground">
|
|
10
|
+
Ingresa tus credenciales para acceder
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
<LoginForm />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
+
|
|
3
|
+
export async function POST(request: NextRequest) {
|
|
4
|
+
try {
|
|
5
|
+
const body = await request.json()
|
|
6
|
+
|
|
7
|
+
// Verificar firma del webhook aquí
|
|
8
|
+
// const signature = request.headers.get('x-webhook-signature')
|
|
9
|
+
|
|
10
|
+
console.log('Webhook received:', body)
|
|
11
|
+
|
|
12
|
+
return NextResponse.json({ received: true })
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error('Webhook error:', error)
|
|
15
|
+
return NextResponse.json(
|
|
16
|
+
{ error: 'Webhook processing failed' },
|
|
17
|
+
{ status: 500 }
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Metadata } from "next"
|
|
2
2
|
import { Inter } from "next/font/google"
|
|
3
3
|
import "./globals.css"
|
|
4
|
+
import { Providers } from "./providers"
|
|
4
5
|
import { SidebarProvider } from "@/components/layout/sidebar-context"
|
|
5
|
-
import { Sidebar } from "@/components/layout/
|
|
6
|
+
import { Sidebar } from "@/components/layout/sidebar"
|
|
6
7
|
import { MainContent } from "@/components/layout/main-content"
|
|
7
|
-
import { Toaster } from "@/components/ui/sonner"
|
|
8
8
|
|
|
9
9
|
const inter = Inter({ subsets: ["latin"] })
|
|
10
10
|
|
|
@@ -19,17 +19,18 @@ export default function RootLayout({
|
|
|
19
19
|
children: React.ReactNode
|
|
20
20
|
}) {
|
|
21
21
|
return (
|
|
22
|
-
<html lang="es"
|
|
22
|
+
<html lang="es" suppressHydrationWarning>
|
|
23
23
|
<body className={`${inter.className} font-sans antialiased`}>
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
<Providers>
|
|
25
|
+
<SidebarProvider>
|
|
26
|
+
<div className="min-h-screen bg-background flex">
|
|
27
|
+
<Sidebar />
|
|
28
|
+
<MainContent>
|
|
29
|
+
{children}
|
|
30
|
+
</MainContent>
|
|
31
|
+
</div>
|
|
32
|
+
</SidebarProvider>
|
|
33
|
+
</Providers>
|
|
33
34
|
</body>
|
|
34
35
|
</html>
|
|
35
36
|
)
|