create-brainerce-store 1.3.3 → 1.4.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/dist/index.js +62 -5
- package/messages/en.json +258 -0
- package/messages/he.json +258 -0
- package/package.json +3 -2
- package/templates/nextjs/base/src/app/account/page.tsx +108 -105
- package/templates/nextjs/base/src/app/auth/callback/page.tsx +90 -88
- package/templates/nextjs/base/src/app/cart/page.tsx +110 -109
- package/templates/nextjs/base/src/app/checkout/page.tsx +46 -43
- package/templates/nextjs/base/src/app/layout.tsx.ejs +8 -5
- package/templates/nextjs/base/src/app/login/page.tsx +58 -56
- package/templates/nextjs/base/src/app/order-confirmation/page.tsx +18 -23
- package/templates/nextjs/base/src/app/page.tsx +98 -95
- package/templates/nextjs/base/src/app/products/[slug]/page.tsx +16 -12
- package/templates/nextjs/base/src/app/products/page.tsx +246 -243
- package/templates/nextjs/base/src/app/register/page.tsx +68 -66
- package/templates/nextjs/base/src/app/verify-email/page.tsx +293 -291
- package/templates/nextjs/base/src/components/account/order-history.tsx +198 -184
- package/templates/nextjs/base/src/components/account/profile-section.tsx +75 -73
- package/templates/nextjs/base/src/components/auth/login-form.tsx +94 -92
- package/templates/nextjs/base/src/components/auth/oauth-buttons.tsx +137 -134
- package/templates/nextjs/base/src/components/auth/register-form.tsx +184 -177
- package/templates/nextjs/base/src/components/cart/cart-item.tsx +153 -150
- package/templates/nextjs/base/src/components/cart/cart-summary.tsx +70 -67
- package/templates/nextjs/base/src/components/cart/coupon-input.tsx +134 -131
- package/templates/nextjs/base/src/components/cart/reservation-countdown.tsx +103 -100
- package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +28 -25
- package/templates/nextjs/base/src/components/checkout/delivery-method-step.tsx +6 -4
- package/templates/nextjs/base/src/components/checkout/payment-step.tsx +39 -20
- package/templates/nextjs/base/src/components/checkout/pickup-step.tsx +15 -11
- package/templates/nextjs/base/src/components/checkout/shipping-step.tsx +110 -111
- package/templates/nextjs/base/src/components/checkout/tax-display.tsx +7 -4
- package/templates/nextjs/base/src/components/layout/footer.tsx +38 -35
- package/templates/nextjs/base/src/components/layout/header.tsx +332 -329
- package/templates/nextjs/base/src/components/products/product-card.tsx +3 -1
- package/templates/nextjs/base/src/components/products/product-grid.tsx +35 -33
- package/templates/nextjs/base/src/components/shared/loading-spinner.tsx +32 -30
- package/templates/nextjs/base/src/i18n.ts.ejs +5 -0
- package/templates/nextjs/base/src/lib/translations.ts +11 -0
|
@@ -4,6 +4,7 @@ import Link from 'next/link';
|
|
|
4
4
|
import Image from 'next/image';
|
|
5
5
|
import type { Product } from 'brainerce';
|
|
6
6
|
import { getProductPriceInfo } from 'brainerce';
|
|
7
|
+
import { useTranslations } from '@/lib/translations';
|
|
7
8
|
import { PriceDisplay } from '@/components/shared/price-display';
|
|
8
9
|
import { StockBadge } from '@/components/products/stock-badge';
|
|
9
10
|
import { DiscountBadge } from '@/components/products/discount-badge';
|
|
@@ -15,6 +16,7 @@ interface ProductCardProps {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function ProductCard({ product, className }: ProductCardProps) {
|
|
19
|
+
const t = useTranslations('common');
|
|
18
20
|
const { price, originalPrice, isOnSale } = getProductPriceInfo(product);
|
|
19
21
|
const mainImage = product.images?.[0];
|
|
20
22
|
const imageUrl = mainImage?.url || null;
|
|
@@ -55,7 +57,7 @@ export function ProductCard({ product, className }: ProductCardProps) {
|
|
|
55
57
|
<div className="absolute start-2 top-2 flex flex-col gap-1">
|
|
56
58
|
{isOnSale && (
|
|
57
59
|
<span className="bg-destructive text-destructive-foreground rounded px-2 py-1 text-xs font-bold">
|
|
58
|
-
|
|
60
|
+
{t('sale')}
|
|
59
61
|
</span>
|
|
60
62
|
)}
|
|
61
63
|
<DiscountBadge discount={product.discount} />
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { Product } from 'brainerce';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { Product } from 'brainerce';
|
|
4
|
+
import { useTranslations } from '@/lib/translations';
|
|
5
|
+
import { ProductCard } from '@/components/products/product-card';
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
interface ProductGridProps {
|
|
9
|
+
products: Product[];
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ProductGrid({ products, className }: ProductGridProps) {
|
|
14
|
+
const t = useTranslations('products');
|
|
15
|
+
if (products.length === 0) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="py-16 text-center">
|
|
18
|
+
<p className="text-muted-foreground text-lg">{t('noProducts')}</p>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
className={cn(
|
|
26
|
+
'grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
|
27
|
+
className
|
|
28
|
+
)}
|
|
29
|
+
>
|
|
30
|
+
{products.map((product) => (
|
|
31
|
+
<ProductCard key={product.id} product={product} />
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from '@/lib/translations';
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
|
+
|
|
6
|
+
interface LoadingSpinnerProps {
|
|
7
|
+
size?: 'sm' | 'md' | 'lg';
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: 'h-4 w-4 border-2',
|
|
13
|
+
md: 'h-8 w-8 border-2',
|
|
14
|
+
lg: 'h-12 w-12 border-3',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function LoadingSpinner({ size = 'md', className }: LoadingSpinnerProps) {
|
|
18
|
+
const t = useTranslations('common');
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
'border-muted-foreground/30 border-t-primary animate-spin rounded-full',
|
|
23
|
+
sizeClasses[size],
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
role="status"
|
|
27
|
+
aria-label={t('loading')}
|
|
28
|
+
>
|
|
29
|
+
<span className="sr-only">{t('loading')}</span>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { messages } from '@/i18n';
|
|
2
|
+
|
|
3
|
+
type Messages = typeof messages;
|
|
4
|
+
type Namespace = keyof Messages;
|
|
5
|
+
|
|
6
|
+
export function useTranslations<N extends Namespace>(namespace: N) {
|
|
7
|
+
const ns = messages[namespace] as Record<string, string>;
|
|
8
|
+
return function t(key: keyof Messages[N]): string {
|
|
9
|
+
return ns[key as string] || `${String(namespace)}.${key as string}`;
|
|
10
|
+
};
|
|
11
|
+
}
|