create-ecom-app 1.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.
Files changed (69) hide show
  1. package/index.js +101 -0
  2. package/package.json +25 -0
  3. package/templates/ecommerce/app/about/page.tsx +152 -0
  4. package/templates/ecommerce/app/cart/page.tsx +103 -0
  5. package/templates/ecommerce/app/checkout/page.tsx +126 -0
  6. package/templates/ecommerce/app/contact/page.tsx +124 -0
  7. package/templates/ecommerce/app/globals.css +137 -0
  8. package/templates/ecommerce/app/layout.tsx +31 -0
  9. package/templates/ecommerce/app/login/page.tsx +224 -0
  10. package/templates/ecommerce/app/not-found.tsx +14 -0
  11. package/templates/ecommerce/app/orders/[id]/page.tsx +138 -0
  12. package/templates/ecommerce/app/page.tsx +215 -0
  13. package/templates/ecommerce/app/product/[id]/page.tsx +417 -0
  14. package/templates/ecommerce/app/products/page.tsx +244 -0
  15. package/templates/ecommerce/app/profile/page.tsx +273 -0
  16. package/templates/ecommerce/app/register/page.tsx +221 -0
  17. package/templates/ecommerce/app/wishlist/page.tsx +89 -0
  18. package/templates/ecommerce/components/CartItem.tsx +74 -0
  19. package/templates/ecommerce/components/CheckoutForm.tsx +109 -0
  20. package/templates/ecommerce/components/Filters.tsx +142 -0
  21. package/templates/ecommerce/components/Footer.tsx +100 -0
  22. package/templates/ecommerce/components/Header.tsx +194 -0
  23. package/templates/ecommerce/components/ProductCard.tsx +158 -0
  24. package/templates/ecommerce/components/SearchBar.tsx +41 -0
  25. package/templates/ecommerce/data/products.json +427 -0
  26. package/templates/ecommerce/lib/localStorage.ts +27 -0
  27. package/templates/ecommerce/lib/utils.ts +25 -0
  28. package/templates/ecommerce/next-env.d.ts +5 -0
  29. package/templates/ecommerce/next.config.js +13 -0
  30. package/templates/ecommerce/next.config.ts +14 -0
  31. package/templates/ecommerce/package.json +30 -0
  32. package/templates/ecommerce/postcss.config.js +7 -0
  33. package/templates/ecommerce/public/images/about-2.jpg +0 -0
  34. package/templates/ecommerce/public/images/contact-bg.jpg +0 -0
  35. package/templates/ecommerce/public/images/hero-bg.jpg +0 -0
  36. package/templates/ecommerce/public/images/products/product-1.jpg +0 -0
  37. package/templates/ecommerce/public/images/products/product-10.jpg +0 -0
  38. package/templates/ecommerce/public/images/products/product-11.jpg +0 -0
  39. package/templates/ecommerce/public/images/products/product-12.jpg +0 -0
  40. package/templates/ecommerce/public/images/products/product-13.jpg +0 -0
  41. package/templates/ecommerce/public/images/products/product-14.jpg +0 -0
  42. package/templates/ecommerce/public/images/products/product-15.jpg +0 -0
  43. package/templates/ecommerce/public/images/products/product-16.jpg +0 -0
  44. package/templates/ecommerce/public/images/products/product-17.jpg +0 -0
  45. package/templates/ecommerce/public/images/products/product-18.jpg +0 -0
  46. package/templates/ecommerce/public/images/products/product-19.jpg +0 -0
  47. package/templates/ecommerce/public/images/products/product-2.jpg +0 -0
  48. package/templates/ecommerce/public/images/products/product-20.jpg +0 -0
  49. package/templates/ecommerce/public/images/products/product-21.jpg +0 -0
  50. package/templates/ecommerce/public/images/products/product-22.jpg +0 -0
  51. package/templates/ecommerce/public/images/products/product-23.jpg +0 -0
  52. package/templates/ecommerce/public/images/products/product-24.jpg +0 -0
  53. package/templates/ecommerce/public/images/products/product-25.jpg +0 -0
  54. package/templates/ecommerce/public/images/products/product-3.jpg +0 -0
  55. package/templates/ecommerce/public/images/products/product-4.jpg +0 -0
  56. package/templates/ecommerce/public/images/products/product-5.jpg +0 -0
  57. package/templates/ecommerce/public/images/products/product-6.jpg +0 -0
  58. package/templates/ecommerce/public/images/products/product-7.jpg +0 -0
  59. package/templates/ecommerce/public/images/products/product-8.jpg +0 -0
  60. package/templates/ecommerce/public/images/products/product-9.jpg +0 -0
  61. package/templates/ecommerce/public/service-worker.js +1 -0
  62. package/templates/ecommerce/store/useAuthStore.ts +56 -0
  63. package/templates/ecommerce/store/useCartStore.ts +63 -0
  64. package/templates/ecommerce/store/useOrderStore.ts +43 -0
  65. package/templates/ecommerce/store/useReviewStore.ts +60 -0
  66. package/templates/ecommerce/store/useWishlistStore.ts +43 -0
  67. package/templates/ecommerce/tailwind.config.ts +44 -0
  68. package/templates/ecommerce/tsconfig.json +22 -0
  69. package/templates/ecommerce/types/index.ts +69 -0
@@ -0,0 +1,56 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ import type { User } from '@/types';
4
+ import { generateId } from '@/lib/utils';
5
+
6
+ interface AuthStore {
7
+ user: User | null;
8
+ isAuthenticated: boolean;
9
+ register: (name: string, email: string, password: string) => void;
10
+ login: (email: string, password: string) => void;
11
+ logout: () => void;
12
+ }
13
+
14
+ export const useAuthStore = create<AuthStore>()(
15
+ persist(
16
+ (set) => ({
17
+ user: null,
18
+ isAuthenticated: false,
19
+
20
+ register: (name, email, password) => {
21
+ const usersRaw =
22
+ typeof window !== 'undefined'
23
+ ? localStorage.getItem('ecom-users')
24
+ : null;
25
+ const users: User[] = usersRaw ? JSON.parse(usersRaw) : [];
26
+
27
+ const exists = users.find((u) => u.email === email);
28
+ if (exists) throw new Error('Email already registered');
29
+
30
+ const newUser: User = { id: generateId(), name, email, password };
31
+ const updated = [...users, newUser];
32
+
33
+ localStorage.setItem('ecom-users', JSON.stringify(updated));
34
+ set({ user: newUser, isAuthenticated: true });
35
+ },
36
+
37
+ login: (email, password) => {
38
+ const usersRaw =
39
+ typeof window !== 'undefined'
40
+ ? localStorage.getItem('ecom-users')
41
+ : null;
42
+ const users: User[] = usersRaw ? JSON.parse(usersRaw) : [];
43
+
44
+ const user = users.find(
45
+ (u) => u.email === email && u.password === password
46
+ );
47
+ if (!user) throw new Error('Invalid email or password');
48
+
49
+ set({ user, isAuthenticated: true });
50
+ },
51
+
52
+ logout: () => set({ user: null, isAuthenticated: false }),
53
+ }),
54
+ { name: 'auth-storage' }
55
+ )
56
+ );
@@ -0,0 +1,63 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ import type { CartItem, Product } from '@/types';
4
+
5
+ interface CartStore {
6
+ items: CartItem[];
7
+ addItem: (product: Product) => void;
8
+ removeItem: (id: string) => void;
9
+ updateQuantity: (id: string, quantity: number) => void;
10
+ clearCart: () => void;
11
+ itemCount: () => number;
12
+ total: () => number;
13
+ }
14
+
15
+ export const useCartStore = create<CartStore>()(
16
+ persist(
17
+ (set, get) => ({
18
+ items: [],
19
+
20
+ addItem: (product) => {
21
+ const existing = get().items.find((i) => i.product.id === product.id);
22
+ if (existing) {
23
+ set({
24
+ items: get().items.map((i) =>
25
+ i.product.id === product.id
26
+ ? { ...i, quantity: i.quantity + 1 }
27
+ : i
28
+ ),
29
+ });
30
+ } else {
31
+ set({ items: [...get().items, { product, quantity: 1 }] });
32
+ }
33
+ },
34
+
35
+ removeItem: (id) => {
36
+ set({ items: get().items.filter((i) => i.product.id !== id) });
37
+ },
38
+
39
+ updateQuantity: (id, quantity) => {
40
+ if (quantity <= 0) {
41
+ get().removeItem(id);
42
+ return;
43
+ }
44
+ set({
45
+ items: get().items.map((i) =>
46
+ i.product.id === id ? { ...i, quantity } : i
47
+ ),
48
+ });
49
+ },
50
+
51
+ clearCart: () => set({ items: [] }),
52
+
53
+ itemCount: () => get().items.reduce((sum, i) => sum + i.quantity, 0),
54
+
55
+ total: () =>
56
+ get().items.reduce(
57
+ (sum, i) => sum + i.product.price * i.quantity,
58
+ 0
59
+ ),
60
+ }),
61
+ { name: 'cart-storage' }
62
+ )
63
+ );
@@ -0,0 +1,43 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ import type { CartItem, Address, Order, OrderStatus } from '@/types';
4
+ import { generateId } from '@/lib/utils';
5
+
6
+ interface OrderStore {
7
+ orders: Order[];
8
+ addOrder: (data: {
9
+ items: CartItem[];
10
+ address: Address;
11
+ subtotal: number;
12
+ shipping: number;
13
+ total: number;
14
+ }) => Order;
15
+ getOrder: (id: string) => Order | undefined;
16
+ getUserOrders: (userEmail: string) => Order[];
17
+ }
18
+
19
+ export const useOrderStore = create<OrderStore>()(
20
+ persist(
21
+ (set, get) => ({
22
+ orders: [],
23
+
24
+ addOrder: (data) => {
25
+ const statuses: OrderStatus[] = ['confirmed', 'processing', 'shipped', 'delivered'];
26
+ const order: Order = {
27
+ ...data,
28
+ id: `ORD-${generateId().toUpperCase().slice(0, 8)}`,
29
+ status: statuses[0],
30
+ createdAt: new Date().toISOString(),
31
+ };
32
+ set({ orders: [order, ...get().orders] });
33
+ return order;
34
+ },
35
+
36
+ getOrder: (id) => get().orders.find((o) => o.id === id),
37
+
38
+ getUserOrders: (userEmail) =>
39
+ get().orders.filter((o) => o.address.email === userEmail),
40
+ }),
41
+ { name: 'orders-storage' }
42
+ )
43
+ );
@@ -0,0 +1,60 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ import type { Review } from '@/types';
4
+ import { generateId } from '@/lib/utils';
5
+
6
+ interface ReviewStore {
7
+ reviews: Review[];
8
+ addReview: (data: Omit<Review, 'id' | 'date' | 'helpful' | 'helpfulBy'>) => void;
9
+ markHelpful: (reviewId: string, userId: string) => void;
10
+ getProductReviews: (productId: string) => Review[];
11
+ hasReviewed: (productId: string, userId: string) => boolean;
12
+ getAverageRating: (productId: string) => number;
13
+ getUserReviews: (userId: string) => Review[];
14
+ }
15
+
16
+ export const useReviewStore = create<ReviewStore>()(
17
+ persist(
18
+ (set, get) => ({
19
+ reviews: [],
20
+
21
+ addReview: (data) => {
22
+ const review: Review = {
23
+ ...data,
24
+ id: generateId(),
25
+ date: new Date().toISOString(),
26
+ helpful: 0,
27
+ helpfulBy: [],
28
+ };
29
+ set({ reviews: [review, ...get().reviews] });
30
+ },
31
+
32
+ markHelpful: (reviewId, userId) => {
33
+ set({
34
+ reviews: get().reviews.map((r) => {
35
+ if (r.id !== reviewId) return r;
36
+ const alreadyMarked = r.helpfulBy.includes(userId);
37
+ return alreadyMarked
38
+ ? { ...r, helpful: r.helpful - 1, helpfulBy: r.helpfulBy.filter((id) => id !== userId) }
39
+ : { ...r, helpful: r.helpful + 1, helpfulBy: [...r.helpfulBy, userId] };
40
+ }),
41
+ });
42
+ },
43
+
44
+ getProductReviews: (productId) =>
45
+ get().reviews.filter((r) => r.productId === productId),
46
+
47
+ hasReviewed: (productId, userId) =>
48
+ get().reviews.some((r) => r.productId === productId && r.userId === userId),
49
+
50
+ getAverageRating: (productId) => {
51
+ const reviews = get().reviews.filter((r) => r.productId === productId);
52
+ if (reviews.length === 0) return 0;
53
+ return reviews.reduce((sum, r) => sum + r.rating, 0) / reviews.length;
54
+ },
55
+
56
+ getUserReviews: (userId) => get().reviews.filter((r) => r.userId === userId),
57
+ }),
58
+ { name: 'reviews-storage' }
59
+ )
60
+ );
@@ -0,0 +1,43 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+ import type { Product } from '@/types';
4
+
5
+ interface WishlistStore {
6
+ items: Product[];
7
+ addItem: (product: Product) => void;
8
+ removeItem: (id: string) => void;
9
+ isWishlisted: (id: string) => boolean;
10
+ toggle: (product: Product) => void;
11
+ itemCount: () => number;
12
+ }
13
+
14
+ export const useWishlistStore = create<WishlistStore>()(
15
+ persist(
16
+ (set, get) => ({
17
+ items: [],
18
+
19
+ addItem: (product) => {
20
+ if (!get().isWishlisted(product.id)) {
21
+ set({ items: [...get().items, product] });
22
+ }
23
+ },
24
+
25
+ removeItem: (id) => {
26
+ set({ items: get().items.filter((p) => p.id !== id) });
27
+ },
28
+
29
+ isWishlisted: (id) => get().items.some((p) => p.id === id),
30
+
31
+ toggle: (product) => {
32
+ if (get().isWishlisted(product.id)) {
33
+ get().removeItem(product.id);
34
+ } else {
35
+ get().addItem(product);
36
+ }
37
+ },
38
+
39
+ itemCount: () => get().items.length,
40
+ }),
41
+ { name: 'wishlist-storage' }
42
+ )
43
+ );
@@ -0,0 +1,44 @@
1
+ import type { Config } from 'tailwindcss';
2
+
3
+ const config: Config = {
4
+ content: [
5
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
6
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
7
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
8
+ ],
9
+ theme: {
10
+ extend: {
11
+ colors: {
12
+ brand: {
13
+ 50: '#fdf4ff',
14
+ 100: '#fae8ff',
15
+ 500: '#a855f7',
16
+ 600: '#9333ea',
17
+ 700: '#7e22ce',
18
+ 900: '#581c87',
19
+ },
20
+ },
21
+ fontFamily: {
22
+ sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
23
+ },
24
+ animation: {
25
+ 'fade-in': 'fadeIn 0.4s ease-out',
26
+ 'slide-up': 'slideUp 0.4s ease-out',
27
+ 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
28
+ },
29
+ keyframes: {
30
+ fadeIn: {
31
+ '0%': { opacity: '0' },
32
+ '100%': { opacity: '1' },
33
+ },
34
+ slideUp: {
35
+ '0%': { opacity: '0', transform: 'translateY(20px)' },
36
+ '100%': { opacity: '1', transform: 'translateY(0)' },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ plugins: [],
42
+ };
43
+
44
+ export default config;
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["dom", "dom.iterable", "esnext"],
4
+ "allowJs": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "module": "esnext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "jsx": "preserve",
14
+ "incremental": true,
15
+ "plugins": [{ "name": "next" }],
16
+ "paths": {
17
+ "@/*": ["./*"]
18
+ }
19
+ },
20
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
21
+ "exclude": ["node_modules"]
22
+ }
@@ -0,0 +1,69 @@
1
+ export type Product = {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ price: number;
6
+ originalPrice?: number;
7
+ category: string;
8
+ image: string;
9
+ rating: number;
10
+ reviewCount: number;
11
+ inStock: boolean;
12
+ tags?: string[];
13
+ };
14
+
15
+ export type User = {
16
+ id: string;
17
+ name: string;
18
+ email: string;
19
+ password: string;
20
+ };
21
+
22
+ export type CartItem = {
23
+ product: Product;
24
+ quantity: number;
25
+ };
26
+
27
+ export type Address = {
28
+ name: string;
29
+ email: string;
30
+ phone: string;
31
+ address: string;
32
+ city: string;
33
+ state: string;
34
+ pincode: string;
35
+ };
36
+
37
+ export type OrderStatus = 'confirmed' | 'processing' | 'shipped' | 'delivered';
38
+
39
+ export type Order = {
40
+ id: string;
41
+ items: CartItem[];
42
+ address: Address;
43
+ subtotal: number;
44
+ shipping: number;
45
+ total: number;
46
+ status: OrderStatus;
47
+ createdAt: string;
48
+ };
49
+
50
+ export type Review = {
51
+ id: string;
52
+ productId: string;
53
+ userId: string;
54
+ userName: string;
55
+ rating: number;
56
+ title: string;
57
+ body: string;
58
+ date: string;
59
+ helpful: number;
60
+ helpfulBy: string[];
61
+ };
62
+
63
+ export type FilterState = {
64
+ search: string;
65
+ category: string;
66
+ minPrice: number;
67
+ maxPrice: number;
68
+ minRating: number;
69
+ };