create-nextjs-stack 0.1.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/LICENSE +21 -0
- package/README.md +60 -0
- package/bin/cli.js +187 -0
- package/package.json +48 -0
- package/templates/admin/.env.example +11 -0
- package/templates/admin/README.md +82 -0
- package/templates/admin/app/(auth)/login/page.tsx +84 -0
- package/templates/admin/app/(dashboard)/[resource]/[id]/page.tsx +45 -0
- package/templates/admin/app/(dashboard)/[resource]/new/page.tsx +32 -0
- package/templates/admin/app/(dashboard)/[resource]/page.tsx +131 -0
- package/templates/admin/app/(dashboard)/categories/[id]/page.tsx +22 -0
- package/templates/admin/app/(dashboard)/categories/new/page.tsx +5 -0
- package/templates/admin/app/(dashboard)/categories/page.tsx +33 -0
- package/templates/admin/app/(dashboard)/clients/[id]/page.tsx +22 -0
- package/templates/admin/app/(dashboard)/clients/new/page.tsx +5 -0
- package/templates/admin/app/(dashboard)/clients/page.tsx +33 -0
- package/templates/admin/app/(dashboard)/dashboard/page.tsx +45 -0
- package/templates/admin/app/(dashboard)/layout.tsx +13 -0
- package/templates/admin/app/(dashboard)/products/[id]/page.tsx +22 -0
- package/templates/admin/app/(dashboard)/products/new/page.tsx +5 -0
- package/templates/admin/app/(dashboard)/products/page.tsx +33 -0
- package/templates/admin/app/(dashboard)/projects/[id]/page.tsx +22 -0
- package/templates/admin/app/(dashboard)/projects/new/page.tsx +5 -0
- package/templates/admin/app/(dashboard)/projects/page.tsx +33 -0
- package/templates/admin/app/(dashboard)/users/[id]/page.tsx +22 -0
- package/templates/admin/app/(dashboard)/users/new/page.tsx +5 -0
- package/templates/admin/app/(dashboard)/users/page.tsx +33 -0
- package/templates/admin/app/actions/resources.ts +46 -0
- package/templates/admin/app/actions/upload.ts +58 -0
- package/templates/admin/app/favicon.ico +0 -0
- package/templates/admin/app/globals.css +23 -0
- package/templates/admin/app/layout.tsx +23 -0
- package/templates/admin/app/page.tsx +5 -0
- package/templates/admin/components/admin/AdminLayoutClient.tsx +22 -0
- package/templates/admin/components/admin/DeleteModal.tsx +90 -0
- package/templates/admin/components/admin/FormLayout.tsx +113 -0
- package/templates/admin/components/admin/ImageUpload.tsx +137 -0
- package/templates/admin/components/admin/ResourceFormClient.tsx +62 -0
- package/templates/admin/components/admin/Sidebar.tsx +74 -0
- package/templates/admin/components/admin/SubmitButton.tsx +34 -0
- package/templates/admin/components/admin/ToastProvider.tsx +8 -0
- package/templates/admin/components/categories/CategoryForm.tsx +24 -0
- package/templates/admin/components/categories/CategoryList.tsx +113 -0
- package/templates/admin/components/clients/ClientForm.tsx +24 -0
- package/templates/admin/components/clients/ClientList.tsx +113 -0
- package/templates/admin/components/products/ProductForm.tsx +24 -0
- package/templates/admin/components/products/ProductList.tsx +117 -0
- package/templates/admin/components/projects/ProjectForm.tsx +24 -0
- package/templates/admin/components/projects/ProjectList.tsx +121 -0
- package/templates/admin/components/users/UserForm.tsx +39 -0
- package/templates/admin/components/users/UserList.tsx +101 -0
- package/templates/admin/config/resources.ts +123 -0
- package/templates/admin/eslint.config.mjs +18 -0
- package/templates/admin/hooks/useResource.ts +86 -0
- package/templates/admin/lib/services/base.service.ts +106 -0
- package/templates/admin/lib/services/categories.service.ts +7 -0
- package/templates/admin/lib/services/clients.service.ts +7 -0
- package/templates/admin/lib/services/index.ts +27 -0
- package/templates/admin/lib/services/products.service.ts +9 -0
- package/templates/admin/lib/services/projects.service.ts +22 -0
- package/templates/admin/lib/services/resource.service.ts +26 -0
- package/templates/admin/lib/services/users.service.ts +9 -0
- package/templates/admin/lib/supabase/client.ts +9 -0
- package/templates/admin/lib/supabase/middleware.ts +57 -0
- package/templates/admin/lib/supabase/server.ts +29 -0
- package/templates/admin/middleware.ts +15 -0
- package/templates/admin/next.config.ts +10 -0
- package/templates/admin/package-lock.json +6768 -0
- package/templates/admin/package.json +33 -0
- package/templates/admin/postcss.config.mjs +7 -0
- package/templates/admin/public/file.svg +1 -0
- package/templates/admin/public/globe.svg +1 -0
- package/templates/admin/public/next.svg +1 -0
- package/templates/admin/public/vercel.svg +1 -0
- package/templates/admin/public/window.svg +1 -0
- package/templates/admin/supabase_mock_data.sql +57 -0
- package/templates/admin/supabase_schema.sql +93 -0
- package/templates/admin/tsconfig.json +34 -0
- package/templates/web/.env.example +21 -0
- package/templates/web/README.md +129 -0
- package/templates/web/components.json +22 -0
- package/templates/web/eslint.config.mjs +25 -0
- package/templates/web/next.config.ts +25 -0
- package/templates/web/package-lock.json +6778 -0
- package/templates/web/package.json +45 -0
- package/templates/web/postcss.config.mjs +5 -0
- package/templates/web/src/app/api/contact/route.ts +181 -0
- package/templates/web/src/app/api/revalidate/route.ts +95 -0
- package/templates/web/src/app/error.tsx +28 -0
- package/templates/web/src/app/globals.css +838 -0
- package/templates/web/src/app/layout.tsx +126 -0
- package/templates/web/src/app/loading.tsx +60 -0
- package/templates/web/src/app/not-found.tsx +68 -0
- package/templates/web/src/app/page.tsx +106 -0
- package/templates/web/src/app/robots.ts +12 -0
- package/templates/web/src/app/sitemap.ts +66 -0
- package/templates/web/src/components/home/StatsGrid.tsx +89 -0
- package/templates/web/src/hooks/useIntersectionObserver.ts +39 -0
- package/templates/web/src/lib/providers/StoreProvider.tsx +12 -0
- package/templates/web/src/lib/seo/index.ts +4 -0
- package/templates/web/src/lib/seo/metadata.ts +103 -0
- package/templates/web/src/lib/seo/seo.config.ts +161 -0
- package/templates/web/src/lib/seo/seo.types.ts +76 -0
- package/templates/web/src/lib/services/categories.service.ts +38 -0
- package/templates/web/src/lib/services/categoryService.ts +251 -0
- package/templates/web/src/lib/services/clientService.ts +132 -0
- package/templates/web/src/lib/services/clients.service.ts +20 -0
- package/templates/web/src/lib/services/productService.ts +261 -0
- package/templates/web/src/lib/services/products.service.ts +38 -0
- package/templates/web/src/lib/services/projectService.ts +234 -0
- package/templates/web/src/lib/services/projects.service.ts +38 -0
- package/templates/web/src/lib/services/users.service.ts +20 -0
- package/templates/web/src/lib/supabase/client.ts +42 -0
- package/templates/web/src/lib/supabase/constants.ts +25 -0
- package/templates/web/src/lib/supabase/server.ts +29 -0
- package/templates/web/src/lib/supabase/types.ts +112 -0
- package/templates/web/src/lib/utils/cache.ts +98 -0
- package/templates/web/src/lib/utils/rate-limiter.ts +102 -0
- package/templates/web/src/store/actions/index.ts +2 -0
- package/templates/web/src/store/index.ts +13 -0
- package/templates/web/src/store/reducers/index.ts +13 -0
- package/templates/web/src/store/types/index.ts +2 -0
- package/templates/web/tsconfig.json +41 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "supabase-admin-template",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@supabase/ssr": "^0.8.0",
|
|
13
|
+
"@supabase/supabase-js": "^2.94.1",
|
|
14
|
+
"cloudinary": "^2.9.0",
|
|
15
|
+
"lucide-react": "^0.563.0",
|
|
16
|
+
"next": "16.1.6",
|
|
17
|
+
"react": "19.2.3",
|
|
18
|
+
"react-dom": "19.2.3",
|
|
19
|
+
"react-hook-form": "^7.71.1",
|
|
20
|
+
"react-toastify": "^11.0.3"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@tailwindcss/postcss": "^4",
|
|
24
|
+
"@types/node": "^20",
|
|
25
|
+
"@types/react": "^19",
|
|
26
|
+
"@types/react-dom": "^19",
|
|
27
|
+
"babel-plugin-react-compiler": "1.0.0",
|
|
28
|
+
"eslint": "^9",
|
|
29
|
+
"eslint-config-next": "16.1.6",
|
|
30
|
+
"tailwindcss": "^4",
|
|
31
|
+
"typescript": "^5"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- Mock Data Script for Admin Panel
|
|
2
|
+
-- Run this script in the Supabase SQL Editor to populate your tables with test data.
|
|
3
|
+
|
|
4
|
+
-- 1. Insert Users
|
|
5
|
+
-- Note: These users won't have login credentials in auth.users, they are just profile records.
|
|
6
|
+
INSERT INTO public.users (email, full_name, role) VALUES
|
|
7
|
+
('admin@example.com', 'Test Admin User', 'admin'),
|
|
8
|
+
('editor1@example.com', 'Test Editor One', 'editor'),
|
|
9
|
+
('editor2@example.com', 'Test Editor Two', 'editor');
|
|
10
|
+
|
|
11
|
+
-- 2. Insert Categories
|
|
12
|
+
INSERT INTO public.categories (title, slug, description, featured, published) VALUES
|
|
13
|
+
('Test Category 1', 'test-category-1', 'Description for Test Category 1', true, true),
|
|
14
|
+
('Test Category 2', 'test-category-2', 'Description for Test Category 2', false, true),
|
|
15
|
+
('Test Category 3', 'test-category-3', 'Description for Test Category 3', true, true),
|
|
16
|
+
('Test Category 4', 'test-category-4', 'Description for Test Category 4', false, false),
|
|
17
|
+
('Test Category 5', 'test-category-5', 'Description for Test Category 5', false, true);
|
|
18
|
+
|
|
19
|
+
-- 3. Insert Clients
|
|
20
|
+
INSERT INTO public.clients (name, logo_url, website) VALUES
|
|
21
|
+
('Test Client 1', 'https://picsum.photos/200/200?random=1', 'https://example.com/client1'),
|
|
22
|
+
('Test Client 2', 'https://picsum.photos/200/200?random=2', 'https://example.com/client2'),
|
|
23
|
+
('Test Client 3', 'https://picsum.photos/200/200?random=3', 'https://example.com/client3'),
|
|
24
|
+
('Test Client 4', 'https://picsum.photos/200/200?random=4', 'https://example.com/client4'),
|
|
25
|
+
('Test Client 5', 'https://picsum.photos/200/200?random=5', 'https://example.com/client5');
|
|
26
|
+
|
|
27
|
+
-- 4. Insert Products (Assigning to randomly selected Categories)
|
|
28
|
+
WITH cat_ids AS (
|
|
29
|
+
SELECT id, row_number() OVER () as rn FROM public.categories
|
|
30
|
+
)
|
|
31
|
+
INSERT INTO public.products (title, slug, description, featured_image_url, category_id, featured, published) VALUES
|
|
32
|
+
('Test Product 1', 'test-product-1', 'Description for Test Product 1', 'https://picsum.photos/800/600?random=10', (SELECT id FROM cat_ids WHERE rn = 1), true, true),
|
|
33
|
+
('Test Product 2', 'test-product-2', 'Description for Test Product 2', 'https://picsum.photos/800/600?random=11', (SELECT id FROM cat_ids WHERE rn = 2), false, true),
|
|
34
|
+
('Test Product 3', 'test-product-3', 'Description for Test Product 3', 'https://picsum.photos/800/600?random=12', (SELECT id FROM cat_ids WHERE rn = 3), true, true),
|
|
35
|
+
('Test Product 4', 'test-product-4', 'Description for Test Product 4', 'https://picsum.photos/800/600?random=13', (SELECT id FROM cat_ids WHERE rn = 4), false, false),
|
|
36
|
+
('Test Product 5', 'test-product-5', 'Description for Test Product 5', 'https://picsum.photos/800/600?random=14', (SELECT id FROM cat_ids WHERE rn = 5), true, true),
|
|
37
|
+
('Test Product 6', 'test-product-6', 'Description for Test Product 6', 'https://picsum.photos/800/600?random=15', (SELECT id FROM cat_ids WHERE rn = 1), false, true),
|
|
38
|
+
('Test Product 7', 'test-product-7', 'Description for Test Product 7', 'https://picsum.photos/800/600?random=16', (SELECT id FROM cat_ids WHERE rn = 2), true, true),
|
|
39
|
+
('Test Product 8', 'test-product-8', 'Description for Test Product 8', 'https://picsum.photos/800/600?random=17', (SELECT id FROM cat_ids WHERE rn = 3), false, true),
|
|
40
|
+
('Test Product 9', 'test-product-9', 'Description for Test Product 9', 'https://picsum.photos/800/600?random=18', (SELECT id FROM cat_ids WHERE rn = 4), true, true),
|
|
41
|
+
('Test Product 10', 'test-product-10', 'Description for Test Product 10', 'https://picsum.photos/800/600?random=19', (SELECT id FROM cat_ids WHERE rn = 5), false, true);
|
|
42
|
+
|
|
43
|
+
-- 5. Insert Projects (Assigning to randomly selected Clients)
|
|
44
|
+
WITH client_ids AS (
|
|
45
|
+
SELECT id, row_number() OVER () as rn FROM public.clients
|
|
46
|
+
)
|
|
47
|
+
INSERT INTO public.projects (title, slug, description, featured_image_url, client_id, published) VALUES
|
|
48
|
+
('Test Project 1', 'test-project-1', 'Description for Test Project 1', 'https://picsum.photos/800/600?random=30', (SELECT id FROM client_ids WHERE rn = 1), true),
|
|
49
|
+
('Test Project 2', 'test-project-2', 'Description for Test Project 2', 'https://picsum.photos/800/600?random=31', (SELECT id FROM client_ids WHERE rn = 2), true),
|
|
50
|
+
('Test Project 3', 'test-project-3', 'Description for Test Project 3', 'https://picsum.photos/800/600?random=32', (SELECT id FROM client_ids WHERE rn = 3), true),
|
|
51
|
+
('Test Project 4', 'test-project-4', 'Description for Test Project 4', 'https://picsum.photos/800/600?random=33', (SELECT id FROM client_ids WHERE rn = 4), false),
|
|
52
|
+
('Test Project 5', 'test-project-5', 'Description for Test Project 5', 'https://picsum.photos/800/600?random=34', (SELECT id FROM client_ids WHERE rn = 5), true),
|
|
53
|
+
('Test Project 6', 'test-project-6', 'Description for Test Project 6', 'https://picsum.photos/800/600?random=35', (SELECT id FROM client_ids WHERE rn = 1), true),
|
|
54
|
+
('Test Project 7', 'test-project-7', 'Description for Test Project 7', 'https://picsum.photos/800/600?random=36', (SELECT id FROM client_ids WHERE rn = 2), true),
|
|
55
|
+
('Test Project 8', 'test-project-8', 'Description for Test Project 8', 'https://picsum.photos/800/600?random=37', (SELECT id FROM client_ids WHERE rn = 3), true),
|
|
56
|
+
('Test Project 9', 'test-project-9', 'Description for Test Project 9', 'https://picsum.photos/800/600?random=38', (SELECT id FROM client_ids WHERE rn = 4), true),
|
|
57
|
+
('Test Project 10', 'test-project-10', 'Description for Test Project 10', 'https://picsum.photos/800/600?random=39', (SELECT id FROM client_ids WHERE rn = 5), true);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
-- Enable UUID extension
|
|
2
|
+
create extension if not exists "uuid-ossp";
|
|
3
|
+
|
|
4
|
+
-- 1. Categories Table
|
|
5
|
+
create table if not exists public.categories (
|
|
6
|
+
id uuid primary key default uuid_generate_v4(),
|
|
7
|
+
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
8
|
+
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
9
|
+
title text not null,
|
|
10
|
+
slug text not null unique,
|
|
11
|
+
description text,
|
|
12
|
+
featured boolean default false,
|
|
13
|
+
published boolean default true
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- 2. Clients Table
|
|
17
|
+
create table if not exists public.clients (
|
|
18
|
+
id uuid primary key default uuid_generate_v4(),
|
|
19
|
+
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
20
|
+
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
21
|
+
name text not null,
|
|
22
|
+
logo_url text,
|
|
23
|
+
website text
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- 3. Products Table
|
|
27
|
+
create table if not exists public.products (
|
|
28
|
+
id uuid primary key default uuid_generate_v4(),
|
|
29
|
+
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
30
|
+
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
31
|
+
title text not null,
|
|
32
|
+
slug text not null unique,
|
|
33
|
+
description text,
|
|
34
|
+
featured_image_url text,
|
|
35
|
+
category_id uuid references public.categories(id) on delete set null,
|
|
36
|
+
featured boolean default false,
|
|
37
|
+
published boolean default true
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- 4. Projects Table
|
|
41
|
+
create table if not exists public.projects (
|
|
42
|
+
id uuid primary key default uuid_generate_v4(),
|
|
43
|
+
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
44
|
+
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
45
|
+
title text not null,
|
|
46
|
+
slug text not null unique,
|
|
47
|
+
description text,
|
|
48
|
+
client_id uuid references public.clients(id) on delete set null,
|
|
49
|
+
featured_image_url text,
|
|
50
|
+
published boolean default true
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- 5. Users Table (Public Profile / Admin Management)
|
|
54
|
+
-- Note: This is separate from auth.users, used for application logic
|
|
55
|
+
create table if not exists public.users (
|
|
56
|
+
id uuid primary key default uuid_generate_v4(),
|
|
57
|
+
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
58
|
+
updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
|
59
|
+
email text not null unique,
|
|
60
|
+
full_name text,
|
|
61
|
+
role text default 'editor' check (role in ('admin', 'editor'))
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
-- Enable Row Level Security (RLS) on all tables
|
|
65
|
+
alter table public.categories enable row level security;
|
|
66
|
+
alter table public.clients enable row level security;
|
|
67
|
+
alter table public.products enable row level security;
|
|
68
|
+
alter table public.projects enable row level security;
|
|
69
|
+
alter table public.users enable row level security;
|
|
70
|
+
|
|
71
|
+
-- Create policies (modify as needed for your auth model)
|
|
72
|
+
-- For development/admin, we might want full access for authenticated users
|
|
73
|
+
|
|
74
|
+
create policy "Enable all access for authenticated users" on public.categories for all using (auth.role() = 'authenticated');
|
|
75
|
+
create policy "Enable all access for authenticated users" on public.clients for all using (auth.role() = 'authenticated');
|
|
76
|
+
create policy "Enable all access for authenticated users" on public.products for all using (auth.role() = 'authenticated');
|
|
77
|
+
create policy "Enable all access for authenticated users" on public.projects for all using (auth.role() = 'authenticated');
|
|
78
|
+
create policy "Enable all access for authenticated users" on public.users for all using (auth.role() = 'authenticated');
|
|
79
|
+
|
|
80
|
+
-- Function to automatically update updated_at timestamp
|
|
81
|
+
create or replace function public.handle_new_user()
|
|
82
|
+
returns trigger as $$
|
|
83
|
+
begin
|
|
84
|
+
insert into public.users (id, email, full_name, role)
|
|
85
|
+
values (new.id, new.email, new.raw_user_meta_data->>'full_name', 'admin');
|
|
86
|
+
return new;
|
|
87
|
+
end;
|
|
88
|
+
$$ language plpgsql security definer;
|
|
89
|
+
|
|
90
|
+
-- Trigger to call the function on new user creation
|
|
91
|
+
create trigger on_auth_user_created
|
|
92
|
+
after insert on auth.users
|
|
93
|
+
for each row execute procedure public.handle_new_user();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [
|
|
17
|
+
{
|
|
18
|
+
"name": "next"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"paths": {
|
|
22
|
+
"@/*": ["./*"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"include": [
|
|
26
|
+
"next-env.d.ts",
|
|
27
|
+
"**/*.ts",
|
|
28
|
+
"**/*.tsx",
|
|
29
|
+
".next/types/**/*.ts",
|
|
30
|
+
".next/dev/types/**/*.ts",
|
|
31
|
+
"**/*.mts"
|
|
32
|
+
],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Email Service (Resend)
|
|
2
|
+
RESEND_API_KEY=your_resend_api_key_here
|
|
3
|
+
RESEND_FROM_EMAIL=your_resend_from_email_here
|
|
4
|
+
|
|
5
|
+
# Supabase Configuration
|
|
6
|
+
SUPABASE_DATABASE_PASSWORD=your_supabase_database_password_here
|
|
7
|
+
SUPABASE_PROJECT_NAME=your_supabase_project_name_here
|
|
8
|
+
SUPABASE_PROJECT_ID=your_supabase_project_id_here
|
|
9
|
+
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url_here
|
|
10
|
+
SUPABASE_PUBLISHABLE_KEY=your_supabase_publishable_key_here
|
|
11
|
+
SUPABASE_SECRET_KEY=your_supabase_secret_key_here
|
|
12
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here
|
|
13
|
+
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key_here
|
|
14
|
+
REVALIDATION_SECRET=your_revalidation_secret_here
|
|
15
|
+
|
|
16
|
+
# Cloudinary Configuration
|
|
17
|
+
CLOUDINARY_API_KEY=your_cloudinary_api_key_here
|
|
18
|
+
CLOUDINARY_API_SECRET=your_cloudinary_api_secret_here
|
|
19
|
+
CLOUDINARY_ID=your_cloud_name_here
|
|
20
|
+
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=your_cloud_name_here
|
|
21
|
+
CLOUDINARY_URL=cloudinary://your_api_key:your_api_secret@your_cloud_name
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Next.js Project Template
|
|
2
|
+
|
|
3
|
+
A production-ready Next.js template with modern tech stack and best practices for building corporate websites and web applications.
|
|
4
|
+
|
|
5
|
+
## 🚀 Technology Stack
|
|
6
|
+
|
|
7
|
+
- **Framework**: [Next.js 16](https://nextjs.org/) with App Router & Turbopack
|
|
8
|
+
- **Styling**: [Tailwind CSS 4](https://tailwindcss.com/)
|
|
9
|
+
- **State Management**: [Redux Toolkit](https://redux-toolkit.js.org/)
|
|
10
|
+
- **Database**: [Supabase](https://supabase.com/) (PostgreSQL + Auth + Storage)
|
|
11
|
+
- **Media Management**: [Cloudinary](https://cloudinary.com/)
|
|
12
|
+
- **Email**: [Resend](https://resend.com/)
|
|
13
|
+
- **Analytics**: Google Analytics (via `@next/third-parties`)
|
|
14
|
+
- **Forms**: React Hook Form + Zod validation
|
|
15
|
+
- **UI Components**: Lucide React, React Icons, Framer Motion
|
|
16
|
+
- **TypeScript**: Full type safety
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- ⚡️ **Turbopack** for ultra-fast dev & build
|
|
21
|
+
- 🎨 **Modern UI** with Tailwind CSS 4 and premium animations
|
|
22
|
+
- 📱 **Fully Responsive** design
|
|
23
|
+
- 🔐 **Supabase Integration** for database and authentication
|
|
24
|
+
- 🖼️ **Cloudinary Integration** for media optimization
|
|
25
|
+
- 📬 **Email Service** with Resend
|
|
26
|
+
- 🔍 **SEO Optimized** (metadata, sitemap, robots.txt, Schema.org)
|
|
27
|
+
- 📊 **Google Analytics** integration
|
|
28
|
+
- 🗂️ **Service Layer Architecture** for clean code organization
|
|
29
|
+
- 🎯 **Redux Store** with TypeScript support
|
|
30
|
+
- ✅ **Form Validation** with React Hook Form & Zod
|
|
31
|
+
- 🌐 **Multi-language Ready** structure
|
|
32
|
+
|
|
33
|
+
## 🚀 Quick Start
|
|
34
|
+
|
|
35
|
+
### Prerequisites
|
|
36
|
+
|
|
37
|
+
- Node.js 20+ and npm installed
|
|
38
|
+
- Supabase account (free tier available)
|
|
39
|
+
- Cloudinary account (free tier available)
|
|
40
|
+
- Resend account (free tier available)
|
|
41
|
+
|
|
42
|
+
### Installation
|
|
43
|
+
|
|
44
|
+
1. **Copy this template to your new project:**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cp -r /path/to/nextjs-template /path/to/your-new-project
|
|
48
|
+
cd /path/to/your-new-project
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. **Install dependencies:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
3. **Set up environment variables:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cp .env.example .env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then fill in your actual values in `.env`
|
|
64
|
+
|
|
65
|
+
4. **Run development server:**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
5. **Open [http://localhost:3000](http://localhost:3000)**
|
|
72
|
+
|
|
73
|
+
## 📁 Project Structure
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
nextjs-template/
|
|
77
|
+
├── public/ # Static assets
|
|
78
|
+
├── src/
|
|
79
|
+
│ ├── app/ # Next.js App Router pages
|
|
80
|
+
│ │ ├── layout.tsx # Root layout with metadata
|
|
81
|
+
│ │ ├── page.tsx # Home page
|
|
82
|
+
│ │ ├── robots.ts # Robots.txt generation
|
|
83
|
+
│ │ └── sitemap.ts # Sitemap generation
|
|
84
|
+
│ ├── components/ # Reusable UI components
|
|
85
|
+
│ ├── data/ # Static data and types
|
|
86
|
+
│ ├── hooks/ # Custom React hooks
|
|
87
|
+
│ ├── lib/ # Utilities and services
|
|
88
|
+
│ │ ├── providers/ # Context providers (Redux, etc.)
|
|
89
|
+
│ │ ├── seo/ # SEO utilities
|
|
90
|
+
│ │ ├── services/ # Service layer (API calls)
|
|
91
|
+
│ │ ├── supabase/ # Supabase client & utilities
|
|
92
|
+
│ │ └── utils/ # Helper functions
|
|
93
|
+
│ └── store/ # Redux store
|
|
94
|
+
│ ├── actions/ # Redux actions
|
|
95
|
+
│ ├── reducers/ # Redux reducers
|
|
96
|
+
│ └── types/ # Redux types
|
|
97
|
+
├── .env.example # Environment variables template
|
|
98
|
+
├── package.json # Dependencies
|
|
99
|
+
└── tsconfig.json # TypeScript config
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 📜 Available Scripts
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Development with Turbopack
|
|
106
|
+
npm run dev
|
|
107
|
+
|
|
108
|
+
# Production build
|
|
109
|
+
npm run build
|
|
110
|
+
|
|
111
|
+
# Start production server
|
|
112
|
+
npm start
|
|
113
|
+
|
|
114
|
+
# Lint code
|
|
115
|
+
npm run lint
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## 🚀 Deployment
|
|
119
|
+
|
|
120
|
+
### Vercel (Recommended)
|
|
121
|
+
|
|
122
|
+
1. Push code to GitHub
|
|
123
|
+
2. Import project in [Vercel](https://vercel.com)
|
|
124
|
+
3. Add environment variables
|
|
125
|
+
4. Deploy!
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
**Happy coding! 🎉**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/app/globals.css",
|
|
9
|
+
"baseColor": "zinc",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"aliases": {
|
|
15
|
+
"components": "@/components",
|
|
16
|
+
"utils": "@/lib/utils",
|
|
17
|
+
"ui": "@/components/ui",
|
|
18
|
+
"lib": "@/lib",
|
|
19
|
+
"hooks": "@/hooks"
|
|
20
|
+
},
|
|
21
|
+
"registries": {}
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { dirname } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const compat = new FlatCompat({
|
|
9
|
+
baseDirectory: __dirname,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const eslintConfig = [
|
|
13
|
+
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
14
|
+
{
|
|
15
|
+
ignores: [
|
|
16
|
+
"node_modules/**",
|
|
17
|
+
".next/**",
|
|
18
|
+
"out/**",
|
|
19
|
+
"build/**",
|
|
20
|
+
"next-env.d.ts",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
images: {
|
|
5
|
+
formats: ['image/avif', 'image/webp'],
|
|
6
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
7
|
+
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
|
|
8
|
+
remotePatterns: [
|
|
9
|
+
{
|
|
10
|
+
protocol: 'https',
|
|
11
|
+
hostname: 'images.ctfassets.net',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
protocol: 'https',
|
|
15
|
+
hostname: 'assets.ctfassets.net',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
protocol: 'https',
|
|
19
|
+
hostname: 'res.cloudinary.com',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default nextConfig;
|