@webdevarif/dashui 0.3.13 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@webdevarif/dashui",
3
- "version": "0.3.13",
4
- "description": "Universal dashboard UI component library with design token system by webdevarif",
3
+ "version": "0.5.0",
4
+ "description": "Universal dashboard UI component library forms, inputs, media, tables, layouts. Modular categories: primitives, forms, dashboard, media, data, editors, ecommerce, cms.",
5
5
  "keywords": [
6
6
  "dashboard",
7
7
  "ui",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "files": [
36
36
  "dist",
37
+ "templates",
37
38
  "README.md"
38
39
  ],
39
40
  "scripts": {
@@ -0,0 +1,74 @@
1
+ # dashui Templates
2
+
3
+ Copy-paste starter code for your Next.js project. Customize to your API/database.
4
+
5
+ ## What's included
6
+
7
+ - **nextauth/** — NextAuth v6 edge-safe configuration + route handlers
8
+ - **prisma/** — Base multi-tenant Prisma schemas (SaaS, E-commerce, CMS)
9
+ - **hooks/** — SWR data fetching patterns (useFetch, useCreate, useUpdate, useDelete)
10
+ - **middleware/** — Auth middleware, error handling, rate limiting
11
+ - **api-patterns/** — CRUD route templates, file uploads, webhooks
12
+
13
+ ## Quick start
14
+
15
+ ### 1. Copy NextAuth config
16
+ ```bash
17
+ cp templates/nextauth/auth.config.ts ./
18
+ cp templates/nextauth/auth.ts ./
19
+ cp -r templates/nextauth/route-handlers ./app/api/auth/
20
+ ```
21
+
22
+ Then customize your provider credentials in `.env`.
23
+
24
+ ### 2. Copy Prisma schema
25
+ ```bash
26
+ cp templates/prisma/multi-tenant-schema.prisma ./prisma/schema.prisma
27
+ ```
28
+
29
+ Add your custom models.
30
+
31
+ ### 3. Copy data fetching hooks
32
+ ```bash
33
+ cp templates/hooks/*.ts ./lib/
34
+ ```
35
+
36
+ Use in components:
37
+ ```tsx
38
+ const { data, error, isLoading } = useFetch('/api/products')
39
+ ```
40
+
41
+ ### 4. Copy middleware
42
+ ```bash
43
+ cp templates/middleware/auth-middleware.ts ./lib/
44
+ ```
45
+
46
+ Use in API routes:
47
+ ```tsx
48
+ import { requireAuth } from '@/lib/auth-middleware'
49
+
50
+ export async function POST(req) {
51
+ const user = await requireAuth(req)
52
+ // user is authenticated
53
+ }
54
+ ```
55
+
56
+ ## Before publishing
57
+
58
+ Each template is a starting point. **Customize to your needs:**
59
+ - Environment variables (OAuth credentials, database URL, API keys)
60
+ - Prisma relations (add your custom models)
61
+ - API endpoints (match your backend structure)
62
+ - Error handling (log to your service)
63
+ - Rate limits (adjust for your use case)
64
+
65
+ ## FAQ
66
+
67
+ **Q: Can I modify these templates?**
68
+ A: Yes! They're in your project now. Make them yours.
69
+
70
+ **Q: Why not a generator/CLI?**
71
+ A: Copy-paste is simpler, more transparent, easier to customize. You see exactly what's installed.
72
+
73
+ **Q: How do I keep up with dashui updates?**
74
+ A: Components update → just re-export from dashui. Templates are static starter code — no updates needed unless you want them.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * CRUD API Route Pattern
3
+ *
4
+ * Copy and modify for your models.
5
+ * This example shows: GET, POST, PATCH, DELETE
6
+ */
7
+
8
+ import { NextRequest, NextResponse } from 'next/server'
9
+ import { requireAuth, requireRole } from '@/lib/auth-middleware'
10
+ import { prisma } from '@/lib/prisma'
11
+
12
+ // GET — List with pagination + filters
13
+ export async function GET(req: NextRequest) {
14
+ try {
15
+ const user = await requireAuth(req)
16
+
17
+ const url = new URL(req.url)
18
+ const page = parseInt(url.searchParams.get('page') || '1')
19
+ const limit = parseInt(url.searchParams.get('limit') || '20')
20
+ const skip = (page - 1) * limit
21
+
22
+ // TODO: Add your filters/search logic here
23
+ const items = await prisma.product.findMany({
24
+ skip,
25
+ take: limit,
26
+ // where: { storeId: user.storeId }, // filter by user's store if applicable
27
+ })
28
+
29
+ const total = await prisma.product.count()
30
+
31
+ return NextResponse.json({
32
+ data: items,
33
+ pagination: { page, limit, total, pages: Math.ceil(total / limit) },
34
+ })
35
+ } catch (err) {
36
+ console.error('GET error:', err)
37
+ return NextResponse.json({ error: 'Failed to fetch' }, { status: 500 })
38
+ }
39
+ }
40
+
41
+ // POST — Create
42
+ export async function POST(req: NextRequest) {
43
+ try {
44
+ const user = await requireAuth(req)
45
+ const body = await req.json()
46
+
47
+ // TODO: Validate body with zod or similar
48
+ if (!body.name) {
49
+ return NextResponse.json({ error: 'Name required' }, { status: 400 })
50
+ }
51
+
52
+ // TODO: Check permissions (user can create?)
53
+ const item = await prisma.product.create({
54
+ data: {
55
+ name: body.name,
56
+ description: body.description,
57
+ price: body.price,
58
+ // storeId: user.storeId, // if multi-tenant
59
+ },
60
+ })
61
+
62
+ return NextResponse.json(item, { status: 201 })
63
+ } catch (err) {
64
+ console.error('POST error:', err)
65
+ return NextResponse.json({ error: 'Failed to create' }, { status: 500 })
66
+ }
67
+ }
68
+
69
+ // PATCH — Update
70
+ export async function PATCH(req: NextRequest) {
71
+ try {
72
+ const user = await requireAuth(req)
73
+ const body = await req.json()
74
+ const { id, ...data } = body
75
+
76
+ if (!id) {
77
+ return NextResponse.json({ error: 'ID required' }, { status: 400 })
78
+ }
79
+
80
+ // TODO: Check permissions (user owns this item?)
81
+ const item = await prisma.product.update({
82
+ where: { id },
83
+ data,
84
+ })
85
+
86
+ return NextResponse.json(item)
87
+ } catch (err) {
88
+ console.error('PATCH error:', err)
89
+ return NextResponse.json({ error: 'Failed to update' }, { status: 500 })
90
+ }
91
+ }
92
+
93
+ // DELETE
94
+ export async function DELETE(req: NextRequest) {
95
+ try {
96
+ const user = await requireAuth(req)
97
+ const { searchParams } = new URL(req.url)
98
+ const id = searchParams.get('id')
99
+
100
+ if (!id) {
101
+ return NextResponse.json({ error: 'ID required' }, { status: 400 })
102
+ }
103
+
104
+ // TODO: Check permissions (user owns this item?)
105
+ await prisma.product.delete({ where: { id } })
106
+
107
+ return NextResponse.json({ success: true })
108
+ } catch (err) {
109
+ console.error('DELETE error:', err)
110
+ return NextResponse.json({ error: 'Failed to delete' }, { status: 500 })
111
+ }
112
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * useCreate — POST request with optimistic update
3
+ *
4
+ * Copy to: ./lib/hooks/useCreate.ts
5
+ *
6
+ * Usage:
7
+ * const { mutate, isLoading, error } = useCreate('/api/products')
8
+ * await mutate({ name: 'New Product', price: 99.99 })
9
+ */
10
+
11
+ import { useState } from 'react'
12
+
13
+ export function useCreate<T = any>(endpoint: string) {
14
+ const [isLoading, setIsLoading] = useState(false)
15
+ const [error, setError] = useState<Error | null>(null)
16
+
17
+ async function mutate(data: any) {
18
+ setIsLoading(true)
19
+ setError(null)
20
+
21
+ try {
22
+ const response = await fetch(endpoint, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ body: JSON.stringify(data),
28
+ })
29
+
30
+ if (!response.ok) {
31
+ throw new Error(`Create failed: ${response.statusText}`)
32
+ }
33
+
34
+ const result = await response.json()
35
+ return result as T
36
+ } catch (err) {
37
+ const error = err instanceof Error ? err : new Error('Unknown error')
38
+ setError(error)
39
+ throw error
40
+ } finally {
41
+ setIsLoading(false)
42
+ }
43
+ }
44
+
45
+ return { mutate, isLoading, error }
46
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * useDelete — DELETE request
3
+ *
4
+ * Copy to: ./lib/hooks/useDelete.ts
5
+ *
6
+ * Usage:
7
+ * const { mutate, isLoading, error } = useDelete('/api/products/123')
8
+ * await mutate()
9
+ */
10
+
11
+ import { useState } from 'react'
12
+
13
+ export function useDelete<T = any>(endpoint: string) {
14
+ const [isLoading, setIsLoading] = useState(false)
15
+ const [error, setError] = useState<Error | null>(null)
16
+
17
+ async function mutate() {
18
+ setIsLoading(true)
19
+ setError(null)
20
+
21
+ try {
22
+ const response = await fetch(endpoint, {
23
+ method: 'DELETE',
24
+ })
25
+
26
+ if (!response.ok) {
27
+ throw new Error(`Delete failed: ${response.statusText}`)
28
+ }
29
+
30
+ const result = await response.json()
31
+ return result as T
32
+ } catch (err) {
33
+ const error = err instanceof Error ? err : new Error('Unknown error')
34
+ setError(error)
35
+ throw error
36
+ } finally {
37
+ setIsLoading(false)
38
+ }
39
+ }
40
+
41
+ return { mutate, isLoading, error }
42
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * useFetch — Generic SWR wrapper for GET requests
3
+ *
4
+ * Copy to: ./lib/hooks/useFetch.ts
5
+ *
6
+ * Usage:
7
+ * const { data, error, isLoading } = useFetch('/api/products')
8
+ */
9
+
10
+ import useSWR from 'swr'
11
+
12
+ const fetcher = (url: string) => fetch(url).then((r) => r.json())
13
+
14
+ export function useFetch<T = any>(endpoint: string | null) {
15
+ const { data, error, isLoading } = useSWR<T>(
16
+ endpoint, // null = skip fetching
17
+ fetcher,
18
+ {
19
+ revalidateOnFocus: false,
20
+ revalidateOnReconnect: false,
21
+ dedupingInterval: 60000,
22
+ }
23
+ )
24
+
25
+ return {
26
+ data,
27
+ error,
28
+ isLoading,
29
+ }
30
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * useUpdate — PATCH request with optimistic update
3
+ *
4
+ * Copy to: ./lib/hooks/useUpdate.ts
5
+ *
6
+ * Usage:
7
+ * const { mutate, isLoading, error } = useUpdate('/api/products/123')
8
+ * await mutate({ name: 'Updated Product' })
9
+ */
10
+
11
+ import { useState } from 'react'
12
+
13
+ export function useUpdate<T = any>(endpoint: string) {
14
+ const [isLoading, setIsLoading] = useState(false)
15
+ const [error, setError] = useState<Error | null>(null)
16
+
17
+ async function mutate(data: any) {
18
+ setIsLoading(true)
19
+ setError(null)
20
+
21
+ try {
22
+ const response = await fetch(endpoint, {
23
+ method: 'PATCH',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ body: JSON.stringify(data),
28
+ })
29
+
30
+ if (!response.ok) {
31
+ throw new Error(`Update failed: ${response.statusText}`)
32
+ }
33
+
34
+ const result = await response.json()
35
+ return result as T
36
+ } catch (err) {
37
+ const error = err instanceof Error ? err : new Error('Unknown error')
38
+ setError(error)
39
+ throw error
40
+ } finally {
41
+ setIsLoading(false)
42
+ }
43
+ }
44
+
45
+ return { mutate, isLoading, error }
46
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * requireAuth — API route middleware to enforce authentication
3
+ *
4
+ * Copy to: ./lib/auth-middleware.ts
5
+ *
6
+ * Usage in API routes:
7
+ * export async function POST(req: Request) {
8
+ * const user = await requireAuth(req)
9
+ * // user is authenticated; access user.id, user.email, etc.
10
+ * }
11
+ */
12
+
13
+ import { auth } from '@/auth'
14
+ import { NextRequest } from 'next/server'
15
+
16
+ export async function requireAuth(req: NextRequest) {
17
+ const session = await auth()
18
+
19
+ if (!session || !session.user) {
20
+ throw new Error('Unauthorized: Not authenticated', { cause: 'UNAUTHENTICATED' })
21
+ }
22
+
23
+ return session.user
24
+ }
25
+
26
+ export async function requireRole(req: NextRequest, requiredRole: string) {
27
+ const user = await requireAuth(req)
28
+
29
+ const role = (user as any).role || 'user'
30
+
31
+ // Simple role check (admin can do everything)
32
+ if (role === 'admin') return user
33
+
34
+ if (role !== requiredRole) {
35
+ throw new Error(`Forbidden: Requires role ${requiredRole}`, { cause: 'FORBIDDEN' })
36
+ }
37
+
38
+ return user
39
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * NextAuth v6 Edge-safe Config
3
+ *
4
+ * Copy to: ./auth.config.ts
5
+ * Customize: Add your OAuth provider credentials to .env
6
+ *
7
+ * Providers:
8
+ * - Credentials (username/password)
9
+ * - GitHub (OAuth)
10
+ * - Google (OAuth)
11
+ * - Add more as needed
12
+ */
13
+
14
+ import type { NextAuthConfig } from 'next-auth'
15
+ import Credentials from 'next-auth/providers/credentials'
16
+ import GitHub from 'next-auth/providers/github'
17
+ import Google from 'next-auth/providers/google'
18
+ import { PrismaAdapter } from '@auth/prisma-adapter'
19
+ import { prisma } from '@/lib/prisma'
20
+
21
+ export const authConfig = {
22
+ providers: [
23
+ // Credentials provider (username/password)
24
+ Credentials({
25
+ async authorize(credentials) {
26
+ if (!credentials?.email || !credentials?.password) {
27
+ return null
28
+ }
29
+
30
+ // TODO: Implement your credential validation logic
31
+ // Example: find user, verify password hash, return user
32
+ const user = await prisma.user.findUnique({
33
+ where: { email: credentials.email as string },
34
+ })
35
+
36
+ if (!user) return null
37
+
38
+ // Verify password (use bcrypt or similar)
39
+ // const isValid = await bcrypt.compare(credentials.password, user.password)
40
+ // if (!isValid) return null
41
+
42
+ return {
43
+ id: user.id,
44
+ email: user.email,
45
+ name: user.name,
46
+ image: user.image,
47
+ }
48
+ },
49
+ }),
50
+
51
+ // GitHub OAuth (register app: https://github.com/settings/apps)
52
+ GitHub({
53
+ clientId: process.env.GITHUB_ID,
54
+ clientSecret: process.env.GITHUB_SECRET,
55
+ }),
56
+
57
+ // Google OAuth (create: https://console.cloud.google.com/)
58
+ Google({
59
+ clientId: process.env.GOOGLE_ID,
60
+ clientSecret: process.env.GOOGLE_SECRET,
61
+ }),
62
+ ],
63
+
64
+ adapter: PrismaAdapter(prisma),
65
+
66
+ pages: {
67
+ signIn: '/login',
68
+ error: '/login',
69
+ },
70
+
71
+ callbacks: {
72
+ // Add custom logic when user signs in
73
+ async signIn({ user, account, profile, email, credentials }) {
74
+ // Check if user is allowed, verify email domain, etc.
75
+ return true
76
+ },
77
+
78
+ // Add user role/permissions to session
79
+ async session({ session, user }) {
80
+ return {
81
+ ...session,
82
+ user: {
83
+ ...session.user,
84
+ id: user.id,
85
+ role: (user as any).role || 'user', // Adjust based on your schema
86
+ },
87
+ }
88
+ },
89
+
90
+ // Add user info to JWT token
91
+ async jwt({ token, user, account }) {
92
+ if (user) {
93
+ token.id = user.id
94
+ token.role = (user as any).role || 'user'
95
+ }
96
+ return token
97
+ },
98
+ },
99
+
100
+ events: {
101
+ // Log important events
102
+ async signIn({ user, account, profile, isNewUser }) {
103
+ console.log(`User signed in: ${user.email}`)
104
+ },
105
+
106
+ async signOut({ token }) {
107
+ console.log(`User signed out`)
108
+ },
109
+ },
110
+ } satisfies NextAuthConfig
@@ -0,0 +1,11 @@
1
+ /**
2
+ * NextAuth v6 Instance
3
+ *
4
+ * Copy to: ./auth.ts
5
+ * Wraps auth.config.ts with handlers for API routes, middleware, client hooks
6
+ */
7
+
8
+ import NextAuth from 'next-auth'
9
+ import { authConfig } from './auth.config'
10
+
11
+ export const { handlers, auth, signIn, signOut } = NextAuth(authConfig)
@@ -0,0 +1,10 @@
1
+ /**
2
+ * NextAuth API Route Handler
3
+ *
4
+ * Copy to: ./app/api/auth/[auth]/route.ts
5
+ * This handles all NextAuth routes: signIn, signOut, callback, etc.
6
+ */
7
+
8
+ import { handlers } from '@/auth'
9
+
10
+ export const { GET, POST } = handlers