@akinon/projectzero 1.100.0-rc.72 → 1.100.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 (61) hide show
  1. package/CHANGELOG.md +5 -234
  2. package/app-template/.env.example +0 -1
  3. package/app-template/CHANGELOG.md +333 -4991
  4. package/app-template/README.md +1 -25
  5. package/app-template/package.json +19 -21
  6. package/app-template/public/locales/en/common.json +1 -48
  7. package/app-template/public/locales/tr/common.json +1 -48
  8. package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +82 -9
  9. package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +4 -17
  10. package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +1 -12
  11. package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +11 -29
  12. package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +1 -12
  13. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +10 -28
  14. package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +1 -12
  15. package/app-template/src/app/api/form/[...id]/route.ts +7 -1
  16. package/app-template/src/assets/fonts/pz-icon.css +0 -3
  17. package/app-template/src/components/__tests__/link.test.tsx +0 -2
  18. package/app-template/src/components/accordion.tsx +19 -22
  19. package/app-template/src/components/currency-select.tsx +0 -1
  20. package/app-template/src/components/file-input.tsx +7 -27
  21. package/app-template/src/components/generate-form-fields.tsx +4 -43
  22. package/app-template/src/components/input.tsx +2 -9
  23. package/app-template/src/components/modal.tsx +16 -32
  24. package/app-template/src/components/pagination.tsx +0 -1
  25. package/app-template/src/components/select.tsx +26 -38
  26. package/app-template/src/components/types/index.ts +1 -25
  27. package/app-template/src/hooks/index.ts +0 -2
  28. package/app-template/src/plugins.js +1 -3
  29. package/app-template/src/redux/store.ts +21 -1
  30. package/app-template/src/settings.js +2 -8
  31. package/app-template/src/types/index.ts +0 -17
  32. package/app-template/src/views/account/address-form.tsx +4 -8
  33. package/app-template/src/views/account/contact-form.tsx +1 -1
  34. package/app-template/src/views/account/content-header.tsx +2 -2
  35. package/app-template/src/views/account/faq/faq-tabs.tsx +2 -8
  36. package/app-template/src/views/basket/basket-item.tsx +14 -22
  37. package/app-template/src/views/basket/summary.tsx +7 -10
  38. package/app-template/src/views/breadcrumb.tsx +2 -2
  39. package/app-template/src/views/category/category-info.tsx +0 -1
  40. package/app-template/src/views/category/filters/index.tsx +1 -1
  41. package/app-template/src/views/guest-login/index.tsx +1 -6
  42. package/app-template/src/views/header/action-menu.tsx +1 -1
  43. package/app-template/src/views/header/search/index.tsx +5 -17
  44. package/app-template/src/views/product/product-info.tsx +263 -62
  45. package/app-template/src/views/product/slider.tsx +73 -86
  46. package/app-template/src/widgets/footer-menu.tsx +2 -6
  47. package/commands/plugins.ts +16 -63
  48. package/dist/commands/plugins.js +16 -57
  49. package/package.json +1 -1
  50. package/app-template/.github/instructions/routing.instructions.md +0 -603
  51. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
  52. package/app-template/src/app/api/image-proxy/route.ts +0 -1
  53. package/app-template/src/app/api/similar-product-list/route.ts +0 -1
  54. package/app-template/src/app/api/similar-products/route.ts +0 -1
  55. package/app-template/src/hooks/use-product-cart.ts +0 -77
  56. package/app-template/src/hooks/use-stock-alert.ts +0 -74
  57. package/app-template/src/utils/variant-validation.ts +0 -41
  58. package/app-template/src/views/basket/basket-content.tsx +0 -106
  59. package/app-template/src/views/product/product-actions.tsx +0 -165
  60. package/app-template/src/views/product/product-share.tsx +0 -56
  61. package/app-template/src/views/product/product-variants.tsx +0 -26
@@ -1,603 +0,0 @@
1
- ---
2
- applyTo: 'src/app/**'
3
- ---
4
-
5
- # App Router & Routing Guidelines
6
-
7
- ## Route Structure
8
-
9
- Project Zero uses App Router with dynamic segments:
10
-
11
- ```
12
- src/app/[commerce]/[locale]/[currency]/
13
- ```
14
-
15
- **Dynamic Parameters:**
16
-
17
- - `[commerce]` - Commerce instance (configured in settings.js)
18
- - `[locale]` - Language code (e.g., 'tr-TR', 'en-US')
19
- - `[currency]` - Currency code (e.g., 'TRY', 'USD', 'EUR')
20
-
21
- ## File Structure
22
-
23
- ### Required Files
24
-
25
- - `page.tsx` - Page component (required for each route)
26
- - `layout.tsx` - Layout wrapper (optional, inherits from parent)
27
- - `loading.tsx` - Loading UI (optional)
28
- - `error.tsx` - Error boundary (optional)
29
- - `not-found.tsx` - 404 page (optional, custom implementation in `pz-not-found/`)
30
-
31
- ### Route Organization
32
-
33
- ```
34
- app/[commerce]/[locale]/[currency]/
35
- ├── layout.tsx # Root layout with withSegmentDefaults
36
- ├── template.tsx # Root template with client-side logic
37
- ├── client-root.tsx # Client root component
38
- ├── page.tsx # Homepage with widget system
39
- ├── error.tsx # Error boundary with Sentry integration
40
- ├── [...prettyurl]/ # Pretty URL handler for legacy routes
41
- │ └── page.tsx # Dynamic route resolver
42
- ├── category/ # Category routes
43
- │ ├── [pk]/ # Category by ID
44
- │ │ ├── page.tsx # Category page
45
- │ │ └── loading.tsx # Category loading state
46
- │ └── [...slug]/ # Category by slug (empty directory)
47
- ├── product/ # Product routes
48
- │ ├── [pk]/ # Product by ID
49
- │ │ └── page.tsx # Product page with metadata
50
- │ └── [slug]/ # Product by slug (empty directory)
51
- ├── group-product/ # Group product routes
52
- │ └── [pk]/
53
- │ ├── page.tsx # Group product page
54
- │ └── loading.tsx # Group product loading state
55
- ├── special-page/ # Special page routes
56
- │ └── [pk]/
57
- │ ├── page.tsx # Special page with widgets
58
- │ └── loading.tsx # Special page loading state
59
- ├── flat-page/ # Flat/CMS page routes
60
- │ └── [pk]/
61
- │ ├── page.tsx # Flat page with HTML content
62
- │ └── loading.tsx # Flat page loading state
63
- ├── landing-page/ # Landing page routes
64
- │ └── [pk]/
65
- │ ├── page.tsx # Landing page
66
- │ └── loading.tsx # Landing page loading state
67
- ├── account/ # Account section
68
- │ ├── layout.tsx # Account layout with authentication
69
- │ ├── page.tsx # Account dashboard
70
- │ ├── orders/ # Order management
71
- │ │ └── [id]/ # Order detail
72
- │ │ ├── layout.tsx # Order layout
73
- │ │ ├── page.tsx # Order details
74
- │ │ └── cancellation/ # Order cancellation
75
- │ ├── profile/page.tsx # User profile
76
- │ ├── address/page.tsx # Address management
77
- │ ├── change-email/page.tsx # Email change
78
- │ ├── change-password/page.tsx # Password change
79
- │ ├── contact/page.tsx # Contact form
80
- │ ├── coupons/page.tsx # Coupon management
81
- │ ├── email-verification/page.tsx # Email verification
82
- │ ├── faq/page.tsx # FAQ page
83
- │ ├── favourite-products/page.tsx # Wishlist
84
- │ └── my-quotations/page.tsx # Quotations
85
- ├── basket/ # Shopping basket
86
- │ └── page.tsx # Basket page
87
- ├── basket-b2b/ # B2B basket
88
- │ └── page.tsx # B2B basket page
89
- ├── orders/ # Order flow
90
- │ ├── checkout/page.tsx # Checkout process
91
- │ └── completed/[token]/ # Order completion
92
- │ ├── layout.tsx # Completion layout
93
- │ └── page.tsx # Completion page
94
- ├── auth/ # Authentication
95
- │ ├── page.tsx # Login page
96
- │ └── oauth-login/page.tsx # OAuth login
97
- ├── users/ # User management
98
- │ ├── password/reset/page.tsx # Password reset
99
- │ ├── email-set-primary/[[...id]]/page.tsx # Primary email
100
- │ ├── registration/account-confirm-email/[[...id]]/page.tsx # Email confirmation
101
- │ └── reset/[[...id]]/page.tsx # Account reset
102
- ├── list/page.tsx # Product listing/search
103
- ├── contact-us/page.tsx # Contact page
104
- ├── anonymous-tracking/page.tsx # Anonymous order tracking
105
- ├── address/stores/page.tsx # Store locator
106
- ├── forms/[pk]/generate/page.tsx # Form generation
107
- ├── pz-not-found/page.tsx # Custom 404 page
108
- └── xml-sitemap/ # Sitemap generation
109
- ├── route.ts # Main sitemap
110
- └── [node]/route.ts # Node-specific sitemap
111
- ```
112
-
113
- ## Route Parameters
114
-
115
- ### Server Components
116
-
117
- ```tsx
118
- import { PageProps } from '@akinon/next/types';
119
-
120
- // For pages with additional parameters (e.g., pk for product/category)
121
- export default function Page({
122
- params,
123
- searchParams
124
- }: PageProps<{ pk: number }>) {
125
- const { commerce, locale, currency, pk } = params;
126
- // Component logic
127
- }
128
-
129
- // For pages with slug parameters
130
- export default function Page({
131
- params,
132
- searchParams
133
- }: PageProps<{ slug: string }>) {
134
- const { commerce, locale, currency, slug } = params;
135
- // Component logic
136
- }
137
-
138
- // For pages with prettyurl parameters
139
- export default function Page({ params }: PageProps) {
140
- const { commerce, locale, currency, prettyurl } = params;
141
- // Component logic
142
- }
143
- ```
144
-
145
- **Note:** The `PageProps` interface from `@akinon/next/types` automatically includes:
146
-
147
- - `params: { locale: string; currency: string } & T` (where T is your custom params)
148
- - `searchParams: URLSearchParams`
149
-
150
- ### Client Components
151
-
152
- ```tsx
153
- 'use client';
154
- import { useParams, useSearchParams } from 'next/navigation';
155
-
156
- export default function ClientComponent() {
157
- const params = useParams();
158
- const searchParams = useSearchParams();
159
-
160
- const { commerce, locale, currency } = params;
161
- // Component logic
162
- }
163
- ```
164
-
165
- ## Route Constants
166
-
167
- Route patterns are defined in `src/routes/index.ts`. Use these constants instead of hardcoding URLs:
168
-
169
- ```tsx
170
- import { ROUTES } from '@theme/routes';
171
-
172
- // General routes
173
- ROUTES.HOME; // '/'
174
- ROUTES.BASKET; // '/baskets/basket'
175
- ROUTES.LIST; // '/list'
176
-
177
- // Authentication routes
178
- ROUTES.AUTH; // '/users/auth'
179
- ROUTES.FORGOT_PASSWORD; // '/users/password/reset'
180
- ROUTES.EMAIL_SET_PRIMARY; // '/users/email-set-primary/.+'
181
- ROUTES.CONFIRM_EMAIL; // '/users/registration/account-confirm-email/.+'
182
-
183
- // Account routes
184
- ROUTES.ACCOUNT; // '/account'
185
- ROUTES.ACCOUNT_ADDRESS; // '/account/address'
186
- ROUTES.ACCOUNT_CHANGE_EMAIL; // '/account/change-email'
187
- ROUTES.ACCOUNT_CHANGE_PASSWORD; // '/account/change-password'
188
- ROUTES.ACCOUNT_CONTACT; // '/account/contact'
189
- ROUTES.ACCOUNT_COUPONS; // '/account/coupons'
190
- ROUTES.ACCOUNT_FAQ; // '/account/faq'
191
- ROUTES.ACCOUNT_ORDERS; // '/users/orders'
192
- ROUTES.ACCOUNT_PROFILE; // '/account/profile'
193
- ROUTES.ACCOUNT_WISHLIST; // '/account/favourite-products/'
194
- ROUTES.ANONYMOUS_TRACKING; // '/anonymous-tracking'
195
-
196
- // Order routes
197
- ROUTES.CHECKOUT; // '/orders/checkout'
198
- ROUTES.CHECKOUT_COMPLETED; // '/orders/completed'
199
-
200
- // Flat page routes
201
- ROUTES.CONTACT_US; // '/contact-us'
202
- ```
203
-
204
- ## Middleware Integration
205
-
206
- The `middleware.ts` file with `withPzDefault` wrapper handles:
207
-
208
- - Parameter validation
209
- - Commerce/locale/currency resolution
210
- - Authentication checks
211
- - Redirects and rewrites
212
-
213
- **Critical:** Never remove `withPzDefault` - it's essential for routing functionality.
214
-
215
- ```tsx
216
- // middleware.ts
217
- import { withPzDefault } from '@akinon/next/middlewares';
218
- import { NextMiddleware, NextResponse } from 'next/server';
219
-
220
- export const config = {
221
- matcher: [
222
- '/((?!api|_next|[\\w-\\/*]+\\.\\w+).*)',
223
- '/(.*sitemap\\.xml)',
224
- '/(.+\\.)(html|htm|aspx|asp|php)',
225
- '/(.*orders\\/checkout-with-token.*)'
226
- ]
227
- };
228
-
229
- const middleware: NextMiddleware = () => {
230
- return NextResponse.next();
231
- };
232
-
233
- export default withPzDefault(middleware);
234
- ```
235
-
236
- ## Pretty URL System
237
-
238
- The `[...prettyurl]` route handles legacy URL patterns and dynamic routing:
239
-
240
- ```tsx
241
- // [...prettyurl]/page.tsx
242
- export default async function Page({ params }: PageProps) {
243
- const { prettyurl } = params;
244
- const pageSlug = prettyurl
245
- .filter((x) => !x.startsWith('searchparams'))
246
- .join('/');
247
-
248
- // Resolves legacy URLs to current route structure
249
- // Handles: /urun/product-name -> /product/[pk]
250
- // Handles: /kategori/category-name -> /category/[pk]
251
- // etc.
252
- }
253
- ```
254
-
255
- ## Dynamic Routes
256
-
257
- ### Product Pages by ID
258
-
259
- ```tsx
260
- // product/[pk]/page.tsx
261
- export async function generateMetadata({
262
- params,
263
- searchParams
264
- }: PageProps<{ pk: number }>) {
265
- // Generate dynamic metadata for SEO
266
- }
267
-
268
- export default function ProductPage({
269
- params,
270
- searchParams
271
- }: PageProps<{ pk: number }>) {
272
- const { pk } = params;
273
- // Product logic
274
- }
275
- ```
276
-
277
- ### Category Pages by ID
278
-
279
- ```tsx
280
- // category/[pk]/page.tsx
281
- export default function CategoryPage({
282
- params,
283
- searchParams
284
- }: PageProps<{ pk: number }>) {
285
- const { pk } = params;
286
- // Category logic
287
- }
288
- ```
289
-
290
- ### Special Pages
291
-
292
- ```tsx
293
- // special-page/[pk]/page.tsx
294
- export default function SpecialPage({
295
- params,
296
- searchParams
297
- }: PageProps<{ pk: number }>) {
298
- const { pk } = params;
299
- // Special page with widgets
300
- }
301
- ```
302
-
303
- ### Flat/CMS Pages
304
-
305
- ```tsx
306
- // flat-page/[pk]/page.tsx
307
- export default function FlatPage({ params }: PageProps<{ pk: number }>) {
308
- const { pk } = params;
309
- // CMS content rendering
310
- }
311
- ```
312
-
313
- ## Navigation
314
-
315
- ### Link Components
316
-
317
- ```tsx
318
- import Link from 'next/link';
319
-
320
- export default function Navigation() {
321
- return <Link href="/account">Account</Link>;
322
- }
323
- ```
324
-
325
- ### Programmatic Navigation
326
-
327
- ```tsx
328
- 'use client';
329
- import { useRouter } from 'next/navigation';
330
-
331
- export default function Component() {
332
- const router = useRouter();
333
-
334
- const handleNavigate = () => {
335
- router.push('/account');
336
- };
337
-
338
- return <button onClick={handleNavigate}>Go to Account</button>;
339
- }
340
- ```
341
-
342
- ## Error Handling
343
-
344
- ### Error Boundaries
345
-
346
- ```tsx
347
- // error.tsx
348
- 'use client';
349
-
350
- import { useSentryUncaughtErrors } from '@akinon/next/hooks';
351
- import PzErrorPage from '@akinon/next/views/error-page';
352
-
353
- export default function ErrorPage({
354
- error,
355
- reset
356
- }: {
357
- error: Error & { digest?: string; isServerError?: boolean };
358
- reset: () => void;
359
- }) {
360
- // DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
361
- useSentryUncaughtErrors(error);
362
-
363
- return <PzErrorPage error={error} reset={reset} />;
364
- }
365
- ```
366
-
367
- ### Not Found Pages
368
-
369
- ```tsx
370
- // pz-not-found/page.tsx
371
- 'use client';
372
-
373
- import React from 'react';
374
- import { Button, Link } from '@theme/components';
375
- import { useLocalization } from '@akinon/next/hooks';
376
-
377
- const NotFound = () => {
378
- const { t } = useLocalization();
379
-
380
- return (
381
- <div className="py-6 flex flex-col items-center justify-center">
382
- <div className="text-8xl font-bold">404</div>
383
- <h1 className="text-4xl font-bold mb-4">{t('not_found.title')}</h1>
384
- <p className="text-lg mb-6">{t('not_found.sub_title')}</p>
385
- <Link href={'/'}>
386
- <Button className="h-auto mt-4 text-base py-3 px-6">
387
- {t('not_found.button')}
388
- </Button>
389
- </Link>
390
- </div>
391
- );
392
- };
393
-
394
- export default NotFound;
395
- ```
396
-
397
- ## Loading States
398
-
399
- ```tsx
400
- // loading.tsx
401
- import { Skeleton, SkeletonWrapper } from 'components';
402
-
403
- export default function Loading() {
404
- return (
405
- <div className="container p-4 mx-auto lg:px-0 lg:my-4">
406
- <SkeletonWrapper className="md:mb-7">
407
- <Skeleton className="w-[17.25rem] h-4 lg:w-64" />
408
- </SkeletonWrapper>
409
-
410
- <div className="w-full flex gap-8">
411
- <div className="hidden lg:block">
412
- <SkeletonWrapper className="w-[17.25rem] h-[650px] shrink-0">
413
- <Skeleton className="w-full h-full" />
414
- </SkeletonWrapper>
415
- </div>
416
-
417
- <div className="flex-1">
418
- <div className="grid gap-x-4 gap-y-7 grid-cols-2 md:grid-cols-3 lg:grid-cols-3">
419
- {Array(6)
420
- .fill(null)
421
- .map((_, index) => (
422
- <Skeleton
423
- key={index}
424
- className="w-full h-80 md:h-[26.813rem] lg:h-[35.875rem]"
425
- />
426
- ))}
427
- </div>
428
- </div>
429
- </div>
430
- </div>
431
- );
432
- }
433
- ```
434
-
435
- ## Metadata Generation
436
-
437
- ### Static Metadata
438
-
439
- ```tsx
440
- import type { Metadata } from 'next';
441
-
442
- export const metadata: Metadata = {
443
- title: 'Page Title',
444
- description: 'Page description'
445
- };
446
- ```
447
-
448
- ### Dynamic Metadata
449
-
450
- ```tsx
451
- import { PageProps, Metadata } from '@akinon/next/types';
452
-
453
- export async function generateMetadata({
454
- params,
455
- searchParams
456
- }: PageProps<{ pk: number }>): Promise<Metadata> {
457
- const { pk } = params;
458
-
459
- try {
460
- const data = await getProductData({ pk, searchParams });
461
-
462
- return {
463
- title: data.product.name,
464
- description: String(data.product.attributes.description),
465
- twitter: {
466
- title: data.product.name,
467
- description: String(data.product.attributes.description)
468
- },
469
- openGraph: {
470
- title: data.product.name,
471
- description: String(data.product.attributes.description),
472
- images: data.product.productimage_set?.map((item) => ({
473
- url: item.image
474
- }))
475
- }
476
- };
477
- } catch (error) {
478
- return {};
479
- }
480
- }
481
- ```
482
-
483
- ## Component Patterns
484
-
485
- ### withSegmentDefaults Wrapper
486
-
487
- All page components should use the `withSegmentDefaults` HOC:
488
-
489
- ```tsx
490
- import { withSegmentDefaults } from '@akinon/next/hocs/server';
491
-
492
- async function Page({ params, searchParams }: PageProps<{ pk: number }>) {
493
- // Page logic
494
- }
495
-
496
- export default withSegmentDefaults(Page, { segmentType: 'page' });
497
- ```
498
-
499
- ### Layout Components
500
-
501
- ```tsx
502
- import { withSegmentDefaults } from '@akinon/next/hocs/server';
503
- import { RootLayoutProps } from '@akinon/next/types';
504
-
505
- async function RootLayout({
506
- params,
507
- locale,
508
- translations,
509
- children
510
- }: RootLayoutProps) {
511
- return (
512
- <html lang={locale.isoCode}>
513
- <head />
514
- <body>{children}</body>
515
- </html>
516
- );
517
- }
518
-
519
- export default withSegmentDefaults(RootLayout, {
520
- segmentType: 'root-layout'
521
- });
522
- ```
523
-
524
- ## API Routes
525
-
526
- The application includes several API routes in the `app/api/` directory:
527
-
528
- ```tsx
529
- // API route structure
530
- app/api/
531
- ├── cache/route.ts # Cache management
532
- ├── client/[...slug]/route.ts # Client API proxy
533
- ├── form/[...id]/route.ts # Form handling
534
- ├── logout/route.ts # Logout endpoint
535
- ├── sentry/route.ts # Sentry integration
536
- ├── web-vitals/route.ts # Web vitals tracking
537
- └── widgets/[slug]/ # Widget API
538
- ```
539
-
540
- ### API Route Example
541
-
542
- ```tsx
543
- // app/api/logout/route.ts
544
- import { NextRequest, NextResponse } from 'next/server';
545
-
546
- export async function POST(request: NextRequest) {
547
- // Logout logic
548
- return NextResponse.json({ success: true });
549
- }
550
- ```
551
-
552
- ## Route Testing
553
-
554
- ### Test Route Parameters
555
-
556
- ```tsx
557
- import { render } from '@testing-library/react';
558
- import ProductPage from '@/app/[commerce]/[locale]/[currency]/product/[pk]/page';
559
-
560
- describe('Product Page', () => {
561
- it('handles route parameters correctly', () => {
562
- const mockParams = {
563
- commerce: 'tr',
564
- locale: 'tr-TR',
565
- currency: 'TRY',
566
- pk: 123
567
- };
568
-
569
- const mockSearchParams = new URLSearchParams();
570
-
571
- render(<ProductPage params={mockParams} searchParams={mockSearchParams} />);
572
- // Test assertions
573
- });
574
- });
575
- ```
576
-
577
- ### Test Middleware
578
-
579
- ```tsx
580
- import { withPzDefault } from '@akinon/next/middlewares';
581
-
582
- describe('Middleware', () => {
583
- it('should wrap middleware with withPzDefault', () => {
584
- const middleware = () => NextResponse.next();
585
- const wrappedMiddleware = withPzDefault(middleware);
586
-
587
- expect(wrappedMiddleware).toBeDefined();
588
- });
589
- });
590
- ```
591
-
592
- ## Best Practices
593
-
594
- 1. **Always use `withSegmentDefaults`** for page and layout components
595
- 2. **Use `PageProps<T>` interface** from `@akinon/next/types` for type safety
596
- 3. **Implement proper error boundaries** with Sentry integration
597
- 4. **Use route constants** from `@theme/routes` instead of hardcoded URLs
598
- 5. **Generate dynamic metadata** for SEO optimization
599
- 6. **Implement loading states** with skeleton components
600
- 7. **Use the pretty URL system** for legacy URL support
601
- 8. **Follow the middleware pattern** with `withPzDefault` wrapper
602
-
603
- This routing structure supports the multi-commerce, internationalized architecture of Project Zero applications with comprehensive error handling, SEO optimization, and legacy URL support.
@@ -1,67 +0,0 @@
1
- import { Skeleton, SkeletonWrapper } from 'components';
2
-
3
- export default function Loading() {
4
- return (
5
- <div className="container mx-auto">
6
- <div className="max-w-5xl mx-auto my-5 px-7">
7
- <SkeletonWrapper className="md:mb-7">
8
- <Skeleton className="w-[17.25rem] h-4 lg:w-64" />
9
- </SkeletonWrapper>
10
- </div>
11
- <div className="grid max-w-5xl grid-cols-2 lg:gap-8 mx-auto px-7">
12
- <div className="col-span-2 mb-7 md:mb-0 lg:col-span-1">
13
- <div className="flex gap-1">
14
- <SkeletonWrapper className="hidden md:block md:mb-7">
15
- {Array(5)
16
- .fill(null)
17
- .map((_, index) => (
18
- <Skeleton key={index} className="w-20 h-24 mb-2" />
19
- ))}
20
- </SkeletonWrapper>
21
-
22
- <div className="flex-1">
23
- <SkeletonWrapper className="md:mb-7">
24
- <Skeleton className="w-full h-[30.375rem] md:h-[36.375rem]" />
25
- </SkeletonWrapper>
26
- </div>
27
- </div>
28
- </div>
29
- <div className="flex flex-col items-center col-span-2 lg:col-span-1">
30
- <div className="w-full">
31
- <SkeletonWrapper className="w-full md:mb-7 flex justify-center items-center">
32
- <Skeleton className="w-96 h-16 mb-9" />
33
- <Skeleton className="hidden w-36 h-14 mb-9 md:block" />
34
-
35
- <div className="flex flex-col justify-center items-center mb-9">
36
- <Skeleton className="w-36 h-4 mb-2" />
37
- <div className="flex items-center gap-2">
38
- {Array(3)
39
- .fill(null)
40
- .map((_, index) => (
41
- <Skeleton key={index} className="w-24 h-10" />
42
- ))}
43
- </div>
44
- </div>
45
-
46
- <div className="flex flex-col justify-center items-center mb-4">
47
- <Skeleton className="w-36 h-4 mb-2" />
48
- <div className="flex items-center gap-2">
49
- {Array(5)
50
- .fill(null)
51
- .map((_, index) => (
52
- <Skeleton key={index} className="w-11 h-11" />
53
- ))}
54
- </div>
55
- </div>
56
-
57
- <Skeleton className="hidden w-full h-14 mb-6 md:block" />
58
- <Skeleton className="w-40 h-10 mb-9 md:w-72" />
59
- <Skeleton className="w-24 h-10 mb-7" />
60
- <Skeleton className="w-full h-36" />
61
- </SkeletonWrapper>
62
- </div>
63
- </div>
64
- </div>
65
- </div>
66
- );
67
- }
@@ -1 +0,0 @@
1
- export * from '@akinon/next/api/image-proxy';
@@ -1 +0,0 @@
1
- export * from '@akinon/next/api/similar-product-list';
@@ -1 +0,0 @@
1
- export * from '@akinon/next/api/similar-products';