@promakeai/cli 0.0.5 → 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.
Files changed (161) hide show
  1. package/dist/index.js +214 -135
  2. package/dist/registry/about-page.json +5 -3
  3. package/dist/registry/about-section.json +2 -2
  4. package/dist/registry/announcement-bar.json +43 -0
  5. package/dist/registry/api.json +55 -0
  6. package/dist/registry/auth-core.json +43 -0
  7. package/dist/registry/auth.json +70 -0
  8. package/dist/registry/bento-grid-section.json +1 -1
  9. package/dist/registry/blog-list-page.json +3 -2
  10. package/dist/registry/blog-section.json +2 -2
  11. package/dist/registry/cart-drawer.json +1 -1
  12. package/dist/registry/cart-page.json +5 -4
  13. package/dist/registry/case-study-page.json +48 -0
  14. package/dist/registry/category-section.json +1 -1
  15. package/dist/registry/checkout-page.json +7 -5
  16. package/dist/registry/coming-soon-page-minimal.json +45 -0
  17. package/dist/registry/coming-soon-page.json +47 -0
  18. package/dist/registry/contact-info-grid.json +2 -2
  19. package/dist/registry/contact-page-centered.json +2 -2
  20. package/dist/registry/contact-page-map-overlay.json +4 -3
  21. package/dist/registry/contact-page-map-split.json +4 -3
  22. package/dist/registry/contact-page-split.json +3 -3
  23. package/dist/registry/contact-page.json +5 -3
  24. package/dist/registry/cookie-consent.json +43 -0
  25. package/dist/registry/cookies-page.json +4 -2
  26. package/dist/registry/cta-section.json +2 -2
  27. package/dist/registry/db.json +129 -0
  28. package/dist/registry/docs/about-page.md +5 -0
  29. package/dist/registry/docs/announcement-bar.md +38 -0
  30. package/dist/registry/docs/auth-core.md +64 -0
  31. package/dist/registry/docs/blog-list-page.md +1 -0
  32. package/dist/registry/docs/cart-page.md +1 -0
  33. package/dist/registry/docs/case-study-page.md +39 -0
  34. package/dist/registry/docs/checkout-page.md +3 -1
  35. package/dist/registry/docs/coming-soon-page-minimal.md +32 -0
  36. package/dist/registry/docs/coming-soon-page.md +37 -0
  37. package/dist/registry/docs/contact-page-map-overlay.md +2 -2
  38. package/dist/registry/docs/contact-page-map-split.md +2 -2
  39. package/dist/registry/docs/contact-page.md +5 -0
  40. package/dist/registry/docs/cookie-consent.md +37 -0
  41. package/dist/registry/docs/cookies-page.md +5 -0
  42. package/dist/registry/docs/ecommerce-core.md +4 -3
  43. package/dist/registry/docs/forgot-password-page-split.md +45 -0
  44. package/dist/registry/docs/forgot-password-page.md +46 -0
  45. package/dist/registry/docs/header-ecommerce.md +2 -0
  46. package/dist/registry/docs/hero-carousel.md +37 -0
  47. package/dist/registry/docs/landing-page-app.md +43 -0
  48. package/dist/registry/docs/landing-page-saas.md +41 -0
  49. package/dist/registry/docs/login-page-split.md +13 -4
  50. package/dist/registry/docs/login-page.md +17 -4
  51. package/dist/registry/docs/logo-cloud.md +33 -0
  52. package/dist/registry/docs/masonry-grid.md +37 -0
  53. package/dist/registry/docs/my-orders-page.md +44 -0
  54. package/dist/registry/docs/order-confirmation-page.md +41 -0
  55. package/dist/registry/docs/portfolio-page.md +38 -0
  56. package/dist/registry/docs/pricing-page.md +38 -0
  57. package/dist/registry/docs/privacy-page.md +5 -0
  58. package/dist/registry/docs/product-quick-view.md +37 -0
  59. package/dist/registry/docs/products-page.md +1 -0
  60. package/dist/registry/docs/reading-progress.md +31 -0
  61. package/dist/registry/docs/register-page-split.md +45 -0
  62. package/dist/registry/docs/register-page.md +46 -0
  63. package/dist/registry/docs/reset-password-page-split.md +45 -0
  64. package/dist/registry/docs/reset-password-page.md +36 -0
  65. package/dist/registry/docs/share-buttons.md +37 -0
  66. package/dist/registry/docs/team-page.md +38 -0
  67. package/dist/registry/docs/terms-page.md +5 -0
  68. package/dist/registry/docs/timeline-section.md +37 -0
  69. package/dist/registry/docs/video-hero.md +41 -0
  70. package/dist/registry/ecommerce-core.json +18 -2
  71. package/dist/registry/empty-page.json +1 -1
  72. package/dist/registry/faq-categorized.json +2 -2
  73. package/dist/registry/faq-simple.json +2 -2
  74. package/dist/registry/favorites-blog-block.json +1 -1
  75. package/dist/registry/favorites-ecommerce-block.json +1 -1
  76. package/dist/registry/feature-section.json +2 -2
  77. package/dist/registry/featured-products.json +1 -1
  78. package/dist/registry/footer-detailed.json +1 -1
  79. package/dist/registry/footer-minimal.json +3 -3
  80. package/dist/registry/footer.json +2 -2
  81. package/dist/registry/forgot-password-page-split.json +50 -0
  82. package/dist/registry/forgot-password-page.json +51 -0
  83. package/dist/registry/header-ecommerce.json +4 -2
  84. package/dist/registry/header-mega.json +2 -2
  85. package/dist/registry/header-minimal.json +1 -1
  86. package/dist/registry/header-simple.json +1 -1
  87. package/dist/registry/hero-carousel.json +45 -0
  88. package/dist/registry/hero-cta.json +2 -2
  89. package/dist/registry/hero-gradient.json +2 -2
  90. package/dist/registry/hero-profile.json +1 -1
  91. package/dist/registry/hero.json +2 -2
  92. package/dist/registry/index.json +24 -1
  93. package/dist/registry/landing-page-app.json +47 -0
  94. package/dist/registry/landing-page-saas.json +47 -0
  95. package/dist/registry/login-page-split.json +11 -7
  96. package/dist/registry/login-page.json +4 -4
  97. package/dist/registry/logo-cloud.json +41 -0
  98. package/dist/registry/masonry-grid.json +43 -0
  99. package/dist/registry/my-orders-page.json +52 -0
  100. package/dist/registry/order-confirmation-page.json +49 -0
  101. package/dist/registry/orders-list-block.json +1 -1
  102. package/dist/registry/payment-success-block.json +1 -1
  103. package/dist/registry/portfolio-page.json +45 -0
  104. package/dist/registry/post-detail-block.json +1 -1
  105. package/dist/registry/pricing-page.json +47 -0
  106. package/dist/registry/pricing-section.json +2 -2
  107. package/dist/registry/privacy-page.json +4 -2
  108. package/dist/registry/product-detail-block.json +1 -1
  109. package/dist/registry/product-quick-view.json +46 -0
  110. package/dist/registry/products-page.json +5 -4
  111. package/dist/registry/reading-progress.json +43 -0
  112. package/dist/registry/register-page-split.json +50 -0
  113. package/dist/registry/register-page.json +51 -0
  114. package/dist/registry/related-posts-block.json +1 -1
  115. package/dist/registry/reset-password-page-split.json +50 -0
  116. package/dist/registry/reset-password-page.json +39 -0
  117. package/dist/registry/share-buttons.json +46 -0
  118. package/dist/registry/team-page.json +47 -0
  119. package/dist/registry/terms-page.json +4 -2
  120. package/dist/registry/testimonials-carousel.json +2 -2
  121. package/dist/registry/testimonials-grid.json +2 -2
  122. package/dist/registry/timeline-section.json +43 -0
  123. package/dist/registry/video-hero.json +42 -0
  124. package/package.json +1 -1
  125. package/template/index.html +5 -5
  126. package/template/src/App.tsx +7 -24
  127. package/template/src/components/GoogleAnalytics.tsx +34 -0
  128. package/template/src/components/Layout.tsx +1 -5
  129. package/template/src/components/ScriptInjector.tsx +62 -0
  130. package/template/src/constants/constants.json +8 -2
  131. package/template/src/index.css +1 -0
  132. package/template/src/lang/en/index.json +1 -28
  133. package/template/src/lang/tr/index.json +1 -28
  134. package/template/src/pages/Index.tsx +1 -98
  135. package/template/src/components/Footer.tsx +0 -100
  136. package/template/src/components/Header.tsx +0 -79
  137. package/template/src/components/Hero.tsx +0 -69
  138. package/template/src/modules/api/USAGE.md +0 -515
  139. package/template/src/modules/api/customer-client.ts +0 -20
  140. package/template/src/modules/api/get-error-message.ts +0 -18
  141. package/template/src/modules/api/validation/en.json +0 -29
  142. package/template/src/modules/api/validation/tr.json +0 -29
  143. package/template/src/modules/auth/USAGE.md +0 -248
  144. package/template/src/modules/auth/auth-header-menu.tsx +0 -123
  145. package/template/src/modules/auth/auth-store.ts +0 -57
  146. package/template/src/modules/auth/forgot-password-page.tsx +0 -371
  147. package/template/src/modules/auth/login-page.tsx +0 -183
  148. package/template/src/modules/auth/register-page.tsx +0 -252
  149. package/template/src/modules/auth/use-auth.ts +0 -273
  150. package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
  151. package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
  152. package/template/src/modules/db/adapters/index.ts +0 -2
  153. package/template/src/modules/db/config.ts +0 -59
  154. package/template/src/modules/db/core/DataManager.ts +0 -125
  155. package/template/src/modules/db/core/types.ts +0 -101
  156. package/template/src/modules/db/index.ts +0 -42
  157. package/template/src/modules/db/react/QueryProvider.tsx +0 -16
  158. package/template/src/modules/db/react/index.ts +0 -23
  159. package/template/src/modules/db/react/queryClient.ts +0 -64
  160. package/template/src/modules/db/react/useRepository.ts +0 -400
  161. package/template/src/modules/db/utils/parsers.ts +0 -96
@@ -0,0 +1,31 @@
1
+ # Reading Progress
2
+
3
+ Fixed progress bar showing scroll/reading progress. Shows a smooth animated bar at the top of the page.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/reading-progress/reading-progress.tsx` | component |
10
+ | `$modules$/reading-progress/index.ts` | index |
11
+ | `$modules$/reading-progress/lang/en.json` | lang |
12
+ | `$modules$/reading-progress/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `ReadingProgress`
17
+
18
+ ```typescript
19
+ import { ReadingProgress } from '@/modules/reading-progress';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { ReadingProgress } from '@/modules/reading-progress';
26
+
27
+ <ReadingProgress />
28
+
29
+ • Installed at: src/modules/reading-progress/
30
+ • Props: color, height, showPercentage
31
+ ```
@@ -0,0 +1,45 @@
1
+ # Register Page Split
2
+
3
+ Split-screen registration page with form on the left and full-height image on the right. Features username, email, password fields with confirmation. Uses customerClient for API calls.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/register-page-split/index.ts` | index |
10
+ | `$modules$/register-page-split/register-page-split.tsx` | page |
11
+ | `$modules$/register-page-split/lang/en.json` | lang |
12
+ | `$modules$/register-page-split/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `RegisterPageSplit`, `default`
17
+
18
+ ```typescript
19
+ import { RegisterPageSplit, default } from '@/modules/register-page-split';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import RegisterPageSplit from '@/modules/register-page-split';
26
+
27
+ <RegisterPageSplit
28
+ image="/images/register-bg.jpg"
29
+ />
30
+
31
+ • Installed at: src/modules/register-page-split/
32
+ • Customize text: src/modules/register-page-split/lang/*.json
33
+ • API Integration:
34
+ - customerClient.auth.register() for registration
35
+ - Shows success state after registration
36
+ • Add to your router as a page component
37
+ ```
38
+
39
+ ## Dependencies
40
+
41
+ This component requires:
42
+ - `button`
43
+ - `input`
44
+ - `auth-core`
45
+ - `api`
@@ -0,0 +1,46 @@
1
+ # Register Page
2
+
3
+ Centered card registration page with Layout wrapper. Features username, email, password fields with confirmation, success state after registration. Uses useAuth hook from auth-core for API calls.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/register-page/index.ts` | index |
10
+ | `$modules$/register-page/register-page.tsx` | page |
11
+ | `$modules$/register-page/lang/en.json` | lang |
12
+ | `$modules$/register-page/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `RegisterPage`, `default`
17
+
18
+ ```typescript
19
+ import { RegisterPage, default } from '@/modules/register-page';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import RegisterPage from '@/modules/register-page';
26
+
27
+ <RegisterPage />
28
+
29
+ • Installed at: src/modules/register-page/
30
+ • Customize text: src/modules/register-page/lang/*.json
31
+ • Uses useAuth() hook from auth-core:
32
+ const { register } = useAuth();
33
+ await register(username, email, password);
34
+ • Shows success state after registration
35
+ • Redirects authenticated users to home
36
+ • Add to your router as a page component
37
+ ```
38
+
39
+ ## Dependencies
40
+
41
+ This component requires:
42
+ - `button`
43
+ - `input`
44
+ - `card`
45
+ - `auth-core`
46
+ - `api`
@@ -0,0 +1,45 @@
1
+ # Reset Password Page Split
2
+
3
+ Split-screen password reset page with form on the left and full-height image on the right. Features new password input with confirmation, validates reset code from URL. Uses customerClient for API calls.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/reset-password-page-split/index.ts` | index |
10
+ | `$modules$/reset-password-page-split/reset-password-page-split.tsx` | page |
11
+ | `$modules$/reset-password-page-split/lang/en.json` | lang |
12
+ | `$modules$/reset-password-page-split/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `ResetPasswordPageSplit`, `default`
17
+
18
+ ```typescript
19
+ import { ResetPasswordPageSplit, default } from '@/modules/reset-password-page-split';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import ResetPasswordPageSplit from '@/modules/reset-password-page-split';
26
+
27
+ <ResetPasswordPageSplit
28
+ image="/images/reset-bg.jpg"
29
+ />
30
+
31
+ • Installed at: src/modules/reset-password-page-split/
32
+ • Customize text: src/modules/reset-password-page-split/lang/*.json
33
+ • API Integration:
34
+ - customerClient.auth.resetPassword() for resetting password
35
+ - Expects ?code= and ?username= URL parameters from email link
36
+ • Add to your router as a page component
37
+ ```
38
+
39
+ ## Dependencies
40
+
41
+ This component requires:
42
+ - `button`
43
+ - `input`
44
+ - `auth-core`
45
+ - `api`
@@ -0,0 +1,36 @@
1
+ # Reset Password Page
2
+
3
+ Split-screen password reset page with form on the left and full-height image on the right. Features new password input with confirmation, validates reset code from URL. Clean minimal design with API integration and responsive layout.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/reset-password-page/index.ts` | index |
10
+ | `$modules$/reset-password-page/reset-password-page.tsx` | page |
11
+ | `$modules$/reset-password-page/lang/en.json` | lang |
12
+ | `$modules$/reset-password-page/lang/tr.json` | lang |
13
+
14
+ ## Usage
15
+
16
+ ```
17
+ import ResetPasswordPage from '@/modules/reset-password-page';
18
+
19
+ <ResetPasswordPage
20
+ image="/images/reset-bg.jpg"
21
+ />
22
+
23
+ • Installed at: src/modules/reset-password-page/
24
+ • Customize text: src/modules/reset-password-page/lang/*.json
25
+ • Uses customerClient.auth.resetPassword() for API
26
+ • Expects ?code= URL parameter from email link
27
+ • Add to your router as a page component
28
+ ```
29
+
30
+ ## Dependencies
31
+
32
+ This component requires:
33
+ - `button`
34
+ - `input`
35
+ - `auth`
36
+ - `api`
@@ -0,0 +1,37 @@
1
+ # Share Buttons
2
+
3
+ Social media share buttons with copy link functionality. Supports Twitter/X, Facebook, LinkedIn, WhatsApp, Email, and copy to clipboard.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/share-buttons/share-buttons.tsx` | component |
10
+ | `$modules$/share-buttons/index.ts` | index |
11
+ | `$modules$/share-buttons/lang/en.json` | lang |
12
+ | `$modules$/share-buttons/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `ShareButtons`
17
+
18
+ ```typescript
19
+ import { ShareButtons } from '@/modules/share-buttons';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { ShareButtons } from '@/modules/share-buttons';
26
+
27
+ <ShareButtons />
28
+
29
+ • Installed at: src/modules/share-buttons/
30
+ • Props: url, title, platforms[], layout, size, showLabels
31
+ ```
32
+
33
+ ## Dependencies
34
+
35
+ This component requires:
36
+ - `button`
37
+ - `tooltip`
@@ -0,0 +1,38 @@
1
+ # Team Page
2
+
3
+ Team members page with responsive grid layout, member cards with photo, name, role, bio, and social links. Includes staggered animations, hover effects, and a join team CTA section.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/team-page/index.ts` | index |
10
+ | `$modules$/team-page/team-page.tsx` | page |
11
+ | `$modules$/team-page/lang/en.json` | lang |
12
+ | `$modules$/team-page/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `TeamPage`, `default`
17
+
18
+ ```typescript
19
+ import { TeamPage, default } from '@/modules/team-page';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { TeamPage } from '@/modules/team-page';
26
+
27
+ <Route path="/team" element={<TeamPage />} />
28
+
29
+ • 6 team member cards with social links
30
+ • StaggerContainer grid animation
31
+ • Join team CTA section
32
+ • Customizable via lang files
33
+ ```
34
+
35
+ ## Dependencies
36
+
37
+ This component requires:
38
+ - `animations`
@@ -30,3 +30,8 @@ import { TermsPage } from '@/modules/terms-page';
30
30
  • Sections: account, use, IP, liability, termination
31
31
  • Edit content in lang/en.json
32
32
  ```
33
+
34
+ ## Dependencies
35
+
36
+ This component requires:
37
+ - `animations`
@@ -0,0 +1,37 @@
1
+ # Timeline Section
2
+
3
+ Vertical timeline with alternating left/right items. Perfect for company history, roadmaps, or process steps.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/timeline-section/timeline-section.tsx` | component |
10
+ | `$modules$/timeline-section/index.ts` | index |
11
+ | `$modules$/timeline-section/lang/en.json` | lang |
12
+ | `$modules$/timeline-section/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `TimelineSection`
17
+
18
+ ```typescript
19
+ import { TimelineSection } from '@/modules/timeline-section';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { TimelineSection } from '@/modules/timeline-section';
26
+
27
+ <TimelineSection />
28
+
29
+ • Installed at: src/modules/timeline-section/
30
+ • Customize content: lang/en/timeline-section.json
31
+ • Props: items[], title, subtitle
32
+ ```
33
+
34
+ ## Dependencies
35
+
36
+ This component requires:
37
+ - `animations`
@@ -0,0 +1,41 @@
1
+ # Video Hero Section
2
+
3
+ Full-width hero section with autoplaying video background, gradient overlay, headline, description, CTA buttons, and animated stats. Includes scroll indicator and FadeIn animations.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/video-hero/index.ts` | index |
10
+ | `$modules$/video-hero/video-hero.tsx` | block |
11
+ | `$modules$/video-hero/lang/en.json` | lang |
12
+ | `$modules$/video-hero/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `VideoHero`
17
+
18
+ ```typescript
19
+ import { VideoHero } from '@/modules/video-hero';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { VideoHero } from '@/modules/video-hero';
26
+
27
+ <VideoHero
28
+ videoSrc="/videos/hero.mp4"
29
+ posterSrc="/images/hero-poster.jpg"
30
+ />
31
+
32
+ • Muted autoplay video
33
+ • Fallback poster image
34
+ • Responsive overlay text
35
+ • Scroll indicator
36
+ ```
37
+
38
+ ## Dependencies
39
+
40
+ This component requires:
41
+ - `animations`
@@ -13,7 +13,7 @@
13
13
  "path": "ecommerce-core/index.ts",
14
14
  "type": "registry:index",
15
15
  "target": "$modules$/ecommerce-core/index.ts",
16
- "content": "// Types\r\nexport * from './types';\r\n\r\n// Stores (Zustand)\r\nexport { useCartStore, useCart } from './stores/cart-store';\r\nexport { useFavoritesStore, useFavorites } from './stores/favorites-store';\r\n\r\n// Hooks\r\nexport { useProducts, useProductBySlug, useFeaturedProducts, useCategories } from './useProducts';\r\nexport { useSearch } from './useSearch';\r\n\r\n// Utilities\r\nexport { formatPrice } from './format-price';\r\n"
16
+ "content": "// Types\r\nexport * from './types';\r\n\r\n// Stores (Zustand)\r\nexport { useCartStore, useCart } from './stores/cart-store';\r\nexport { useFavoritesStore, useFavorites } from './stores/favorites-store';\r\n\r\n// Hooks\r\nexport { useProducts, useProductBySlug, useFeaturedProducts, useCategories } from './useProducts';\r\nexport { useSearch } from './useSearch';\r\n\r\n// Utilities\r\nexport { formatPrice } from './format-price';\r\n\r\n// Payment Config\r\nexport {\r\n type PaymentMethod,\r\n type OnlinePaymentProvider,\r\n type PaymentMethodConfig,\r\n PAYMENT_METHOD_CONFIGS,\r\n ONLINE_PROVIDER_CONFIGS,\r\n getAvailablePaymentMethods,\r\n getOnlinePaymentProviders,\r\n getFilteredPaymentMethodConfigs,\r\n isPaymentMethodAvailable,\r\n isOnlineProviderAvailable,\r\n} from './payment-config';\r\n"
17
17
  },
18
18
  {
19
19
  "path": "ecommerce-core/types.ts",
@@ -25,7 +25,7 @@
25
25
  "path": "ecommerce-core/stores/cart-store.ts",
26
26
  "type": "registry:store",
27
27
  "target": "$modules$/ecommerce-core/stores/cart-store.ts",
28
- "content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Product, CartItem, CartState, CartContextType } from \"../types\";\r\n\r\nconst getProductPrice = (product: Product): number => {\r\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\r\n};\r\n\r\nconst calculateTotal = (items: CartItem[]): number => {\r\n return items.reduce((total, item) => {\r\n const price = getProductPrice(item.product);\r\n return total + price * item.quantity;\r\n }, 0);\r\n};\r\n\r\ninterface CartStore extends CartState {\r\n addItem: (product: Product) => void;\r\n removeItem: (id: string | number) => void;\r\n updateQuantity: (id: string | number, quantity: number) => void;\r\n clearCart: () => void;\r\n itemCount: number;\r\n}\r\n\r\nexport const useCartStore = create<CartStore>()(\r\n persist(\r\n (set, _get) => ({\r\n items: [],\r\n total: 0,\r\n itemCount: 0,\r\n\r\n addItem: (product) =>\r\n set((state) => {\r\n const existingItem = state.items.find(\r\n (item) => item.product.id === product.id\r\n );\r\n\r\n if (existingItem) {\r\n const items = state.items.map((item) =>\r\n item.product.id === product.id\r\n ? { ...item, quantity: item.quantity + 1 }\r\n : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }\r\n\r\n const items = [\r\n ...state.items,\r\n { id: product.id, product, quantity: 1 },\r\n ];\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n removeItem: (id) =>\r\n set((state) => {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n updateQuantity: (id, quantity) =>\r\n set((state) => {\r\n if (quantity <= 0) {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }\r\n\r\n const items = state.items.map((item) =>\r\n item.id === id ? { ...item, quantity } : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n clearCart: () => set({ items: [], total: 0, itemCount: 0 }),\r\n }),\r\n { name: \"ecommerce_cart\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches CartContextType\r\nexport const useCart = (): CartContextType => {\r\n const store = useCartStore();\r\n return {\r\n state: { items: store.items, total: store.total },\r\n addItem: store.addItem,\r\n removeItem: store.removeItem,\r\n updateQuantity: store.updateQuantity,\r\n clearCart: store.clearCart,\r\n itemCount: store.itemCount,\r\n };\r\n};\r\n"
28
+ "content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Product, CartItem, CartState, CartContextType } from \"../types\";\r\n\r\nconst getProductPrice = (product: Product): number => {\r\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\r\n};\r\n\r\nconst calculateTotal = (items: CartItem[]): number => {\r\n return items.reduce((total, item) => {\r\n const price = getProductPrice(item.product);\r\n return total + price * item.quantity;\r\n }, 0);\r\n};\r\n\r\ninterface CartStore extends CartState {\r\n addItem: (product: Product) => void;\r\n removeItem: (id: string | number) => void;\r\n updateQuantity: (id: string | number, quantity: number) => void;\r\n clearCart: () => void;\r\n itemCount: number;\r\n isDrawerOpen: boolean;\r\n setDrawerOpen: (open: boolean) => void;\r\n}\r\n\r\nexport const useCartStore = create<CartStore>()(\r\n persist(\r\n (set, _get) => ({\r\n items: [],\r\n total: 0,\r\n itemCount: 0,\r\n isDrawerOpen: false,\r\n setDrawerOpen: (open: boolean) => set({ isDrawerOpen: open }),\r\n\r\n addItem: (product) =>\r\n set((state) => {\r\n const existingItem = state.items.find(\r\n (item) => item.product.id === product.id\r\n );\r\n\r\n if (existingItem) {\r\n const items = state.items.map((item) =>\r\n item.product.id === product.id\r\n ? { ...item, quantity: item.quantity + 1 }\r\n : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n isDrawerOpen: true,\r\n };\r\n }\r\n\r\n const items = [\r\n ...state.items,\r\n { id: product.id, product, quantity: 1 },\r\n ];\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n isDrawerOpen: true,\r\n };\r\n }),\r\n\r\n removeItem: (id) =>\r\n set((state) => {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n updateQuantity: (id, quantity) =>\r\n set((state) => {\r\n if (quantity <= 0) {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }\r\n\r\n const items = state.items.map((item) =>\r\n item.id === id ? { ...item, quantity } : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n clearCart: () => set({ items: [], total: 0, itemCount: 0 }),\r\n }),\r\n { name: \"ecommerce_cart\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches CartContextType with drawer state\r\nexport const useCart = (): CartContextType & { isDrawerOpen: boolean; setDrawerOpen: (open: boolean) => void } => {\r\n const store = useCartStore();\r\n return {\r\n state: { items: store.items, total: store.total },\r\n addItem: store.addItem,\r\n removeItem: store.removeItem,\r\n updateQuantity: store.updateQuantity,\r\n clearCart: store.clearCart,\r\n itemCount: store.itemCount,\r\n isDrawerOpen: store.isDrawerOpen,\r\n setDrawerOpen: store.setDrawerOpen,\r\n };\r\n};\r\n"
29
29
  },
30
30
  {
31
31
  "path": "ecommerce-core/stores/favorites-store.ts",
@@ -51,6 +51,12 @@
51
51
  "target": "$modules$/ecommerce-core/format-price.ts",
52
52
  "content": "export function formatPrice(price: number, currency: string = \"USD\"): string {\r\n return new Intl.NumberFormat(\"en-US\", {\r\n style: \"currency\",\r\n currency: currency,\r\n }).format(price);\r\n}\r\n"
53
53
  },
54
+ {
55
+ "path": "ecommerce-core/payment-config.ts",
56
+ "type": "registry:lib",
57
+ "target": "$modules$/ecommerce-core/payment-config.ts",
58
+ "content": "// Payment configuration utility\r\n// Parses environment variables and provides payment method configuration\r\n\r\nexport type PaymentMethod = \"card\" | \"transfer\" | \"cash\";\r\nexport type OnlinePaymentProvider = \"stripe\" | \"iyzico\";\r\n\r\nexport interface PaymentMethodConfig {\r\n id: PaymentMethod;\r\n label: string;\r\n description: string;\r\n icon: string; // Icon component name from lucide-react\r\n requiresOnlineProvider?: boolean;\r\n}\r\n\r\n// Parse comma-separated env variable into array\r\nconst parseEnvArray = (envVar: string | undefined): string[] => {\r\n if (!envVar) return [];\r\n return envVar.split(\",\").map((item) => item.trim()).filter(Boolean);\r\n};\r\n\r\n// Get available payment methods from environment\r\nexport const getAvailablePaymentMethods = (): PaymentMethod[] => {\r\n const methods = parseEnvArray(import.meta.env.VITE_AVAILABLE_PAYMENT_METHODS);\r\n if (methods.length === 0) {\r\n // Default to all methods if not configured\r\n return [\"card\", \"transfer\", \"cash\"];\r\n }\r\n return methods as PaymentMethod[];\r\n};\r\n\r\n// Get available online payment providers from environment\r\nexport const getOnlinePaymentProviders = (): OnlinePaymentProvider[] => {\r\n const providers = parseEnvArray(import.meta.env.VITE_ONLINE_PAYMENT_METHODS);\r\n if (providers.length === 0) {\r\n // Default to stripe if not configured\r\n return [\"stripe\"];\r\n }\r\n return providers as OnlinePaymentProvider[];\r\n};\r\n\r\n// Payment method configurations with display information\r\nexport const PAYMENT_METHOD_CONFIGS: Record<PaymentMethod, PaymentMethodConfig> = {\r\n card: {\r\n id: \"card\",\r\n label: \"Credit/Debit Card\",\r\n description: \"Pay securely with your credit or debit card\",\r\n icon: \"CreditCard\",\r\n requiresOnlineProvider: true,\r\n },\r\n transfer: {\r\n id: \"transfer\",\r\n label: \"Bank Transfer\",\r\n description: \"Transfer payment to our bank account\",\r\n icon: \"Banknote\",\r\n },\r\n cash: {\r\n id: \"cash\",\r\n label: \"Cash on Delivery\",\r\n description: \"Pay when your order arrives at your doorstep\",\r\n icon: \"Truck\",\r\n },\r\n};\r\n\r\n// Online payment provider configurations\r\nexport const ONLINE_PROVIDER_CONFIGS: Record<OnlinePaymentProvider, { label: string; description: string }> = {\r\n stripe: {\r\n label: \"Stripe\",\r\n description: \"Secure payment processing by Stripe\",\r\n },\r\n iyzico: {\r\n label: \"iyzico\",\r\n description: \"Secure payment processing by iyzico\",\r\n },\r\n};\r\n\r\n// Check if a payment method is available\r\nexport const isPaymentMethodAvailable = (method: PaymentMethod): boolean => {\r\n const available = getAvailablePaymentMethods();\r\n return available.includes(method);\r\n};\r\n\r\n// Check if an online provider is available\r\nexport const isOnlineProviderAvailable = (provider: OnlinePaymentProvider): boolean => {\r\n const available = getOnlinePaymentProviders();\r\n return available.includes(provider);\r\n};\r\n\r\n// Get filtered payment method configs (only available ones)\r\nexport const getFilteredPaymentMethodConfigs = (): PaymentMethodConfig[] => {\r\n const availableMethods = getAvailablePaymentMethods();\r\n return availableMethods\r\n .map((method) => PAYMENT_METHOD_CONFIGS[method])\r\n .filter(Boolean);\r\n};\r\n"
59
+ },
54
60
  {
55
61
  "path": "ecommerce-core/lang/en.json",
56
62
  "type": "registry:lang",
@@ -72,15 +78,25 @@
72
78
  "CartState",
73
79
  "Category",
74
80
  "FavoritesContextType",
81
+ "OnlinePaymentProvider",
75
82
  "Order",
76
83
  "OrderItem",
84
+ "PaymentMethod",
85
+ "PaymentMethodConfig",
77
86
  "Product",
78
87
  "ProductCategory",
79
88
  "ProductVariant",
80
89
  "User"
81
90
  ],
82
91
  "variables": [
92
+ "ONLINE_PROVIDER_CONFIGS",
93
+ "PAYMENT_METHOD_CONFIGS",
83
94
  "formatPrice",
95
+ "getAvailablePaymentMethods",
96
+ "getFilteredPaymentMethodConfigs",
97
+ "getOnlinePaymentProviders",
98
+ "isOnlineProviderAvailable",
99
+ "isPaymentMethodAvailable",
84
100
  "useCart",
85
101
  "useCartStore",
86
102
  "useCategories",
@@ -20,7 +20,7 @@
20
20
  "path": "empty-page/empty-page.tsx",
21
21
  "type": "registry:page",
22
22
  "target": "$modules$/empty-page/empty-page.tsx",
23
- "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\n\nexport function EmptyPage() {\n const { t } = useTranslation(\"empty-page\");\n usePageTitle({ title: t(\"title\") });\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-3xl font-bold\">{t(\"heading\")}</h1>\n <p className=\"text-muted-foreground mt-2\">{t(\"description\")}</p>\n\n {/* Add your page content here */}\n </div>\n </div>\n </Layout>\n );\n}\n"
23
+ "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\n\nexport function EmptyPage() {\n const { t } = useTranslation(\"empty-page\");\n usePageTitle({ title: t(\"title\") });\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <h1 className=\"text-3xl font-bold\">{t(\"heading\")}</h1>\n <p className=\"text-muted-foreground mt-2\">{t(\"description\")}</p>\n\n {/* Add your page content here */}\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default EmptyPage;\n"
24
24
  },
25
25
  {
26
26
  "path": "empty-page/lang/en.json",
@@ -18,13 +18,13 @@
18
18
  "path": "faq-categorized/faq-categorized.tsx",
19
19
  "type": "registry:component",
20
20
  "target": "$modules$/faq-categorized/faq-categorized.tsx",
21
- "content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqCategorizedProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqCategorized({ className }: FaqCategorizedProps) {\r\n const { t } = useTranslation(\"faq-categorized\");\r\n\r\n const categories = [\r\n {\r\n title: t(\"generalTitle\", \"General\"),\r\n items: [\r\n {\r\n question: t(\"general1Q\", \"What is this platform?\"),\r\n answer: t(\"general1A\", \"Our platform is a comprehensive solution for building modern web applications. It provides tools, components, and infrastructure to help you ship faster.\"),\r\n },\r\n {\r\n question: t(\"general2Q\", \"How do I get started?\"),\r\n answer: t(\"general2A\", \"Getting started is easy! Sign up for a free account, follow our quick start guide, and you'll be up and running in minutes.\"),\r\n },\r\n {\r\n question: t(\"general3Q\", \"Is there a free trial?\"),\r\n answer: t(\"general3A\", \"Yes, we offer a 14-day free trial with full access to all features. No credit card required to start.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"billingTitle\", \"Billing\"),\r\n items: [\r\n {\r\n question: t(\"billing1Q\", \"What payment methods do you accept?\"),\r\n answer: t(\"billing1A\", \"We accept all major credit cards (Visa, MasterCard, American Express), PayPal, and bank transfers for annual plans.\"),\r\n },\r\n {\r\n question: t(\"billing2Q\", \"Can I change my plan later?\"),\r\n answer: t(\"billing2A\", \"Absolutely! You can upgrade or downgrade your plan at any time. Changes take effect immediately, and we'll prorate the difference.\"),\r\n },\r\n {\r\n question: t(\"billing3Q\", \"Do you offer refunds?\"),\r\n answer: t(\"billing3A\", \"Yes, we offer a 30-day money-back guarantee. If you're not satisfied within the first 30 days, we'll issue a full refund.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"technicalTitle\", \"Technical\"),\r\n items: [\r\n {\r\n question: t(\"technical1Q\", \"What technologies do you support?\"),\r\n answer: t(\"technical1A\", \"We support React, Vue, Angular, and vanilla JavaScript. Our APIs are RESTful and work with any backend technology.\"),\r\n },\r\n {\r\n question: t(\"technical2Q\", \"Is there an API available?\"),\r\n answer: t(\"technical2A\", \"Yes, we provide a comprehensive REST API with detailed documentation, SDKs for popular languages, and webhook support.\"),\r\n },\r\n {\r\n question: t(\"technical3Q\", \"How is my data protected?\"),\r\n answer: t(\"technical3A\", \"We use industry-standard encryption (AES-256 at rest, TLS 1.3 in transit), regular security audits, and comply with SOC 2 and GDPR requirements.\"),\r\n },\r\n ],\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"Browse through our categorized FAQ to find answers to your questions.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\r\n {categories.map((category, categoryIndex) => (\r\n <div key={categoryIndex}>\r\n <h3 className=\"font-semibold text-lg mb-4 text-primary\">\r\n {category.title}\r\n </h3>\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {category.items.map((item, itemIndex) => (\r\n <AccordionItem\r\n key={itemIndex}\r\n value={`${categoryIndex}-${itemIndex}`}\r\n >\r\n <AccordionTrigger className=\"text-left text-sm font-medium hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-sm text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
21
+ "content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqCategorizedProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqCategorized({ className }: FaqCategorizedProps) {\r\n const { t } = useTranslation(\"faq-categorized\");\r\n\r\n const categories = [\r\n {\r\n title: t(\"generalTitle\", \"General\"),\r\n items: [\r\n {\r\n question: t(\"general1Q\", \"What is this platform?\"),\r\n answer: t(\"general1A\", \"Our platform is a comprehensive solution for building modern web applications. It provides tools, components, and infrastructure to help you ship faster.\"),\r\n },\r\n {\r\n question: t(\"general2Q\", \"How do I get started?\"),\r\n answer: t(\"general2A\", \"Getting started is easy! Sign up for a free account, follow our quick start guide, and you'll be up and running in minutes.\"),\r\n },\r\n {\r\n question: t(\"general3Q\", \"Is there a free trial?\"),\r\n answer: t(\"general3A\", \"Yes, we offer a 14-day free trial with full access to all features. No credit card required to start.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"billingTitle\", \"Billing\"),\r\n items: [\r\n {\r\n question: t(\"billing1Q\", \"What payment methods do you accept?\"),\r\n answer: t(\"billing1A\", \"We accept all major credit cards (Visa, MasterCard, American Express), PayPal, and bank transfers for annual plans.\"),\r\n },\r\n {\r\n question: t(\"billing2Q\", \"Can I change my plan later?\"),\r\n answer: t(\"billing2A\", \"Absolutely! You can upgrade or downgrade your plan at any time. Changes take effect immediately, and we'll prorate the difference.\"),\r\n },\r\n {\r\n question: t(\"billing3Q\", \"Do you offer refunds?\"),\r\n answer: t(\"billing3A\", \"Yes, we offer a 30-day money-back guarantee. If you're not satisfied within the first 30 days, we'll issue a full refund.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"technicalTitle\", \"Technical\"),\r\n items: [\r\n {\r\n question: t(\"technical1Q\", \"What technologies do you support?\"),\r\n answer: t(\"technical1A\", \"We support React, Vue, Angular, and vanilla JavaScript. Our APIs are RESTful and work with any backend technology.\"),\r\n },\r\n {\r\n question: t(\"technical2Q\", \"Is there an API available?\"),\r\n answer: t(\"technical2A\", \"Yes, we provide a comprehensive REST API with detailed documentation, SDKs for popular languages, and webhook support.\"),\r\n },\r\n {\r\n question: t(\"technical3Q\", \"How is my data protected?\"),\r\n answer: t(\"technical3A\", \"We use industry-standard encryption (AES-256 at rest, TLS 1.3 in transit), regular security audits, and comply with SOC 2 and GDPR requirements.\"),\r\n },\r\n ],\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"Browse through our categorized FAQ to find answers to your questions.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\r\n {categories.map((category, categoryIndex) => (\r\n <div key={categoryIndex}>\r\n <h3 className=\"font-semibold text-lg mb-4 text-primary\">\r\n {category.title}\r\n </h3>\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {category.items.map((item, itemIndex) => (\r\n <AccordionItem\r\n key={itemIndex}\r\n value={`${categoryIndex}-${itemIndex}`}\r\n >\r\n <AccordionTrigger className=\"text-left text-sm font-medium hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-sm text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
22
22
  },
23
23
  {
24
24
  "path": "faq-categorized/lang/en.json",
25
25
  "type": "registry:lang",
26
26
  "target": "$modules$/faq-categorized/lang/en.json",
27
- "content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"AI will customize this subtitle to match your FAQ section purpose.\",\r\n \"generalTitle\": \"General\",\r\n \"general1Q\": \"Replace this with your first general question\",\r\n \"general1A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI will replace this answer with relevant information based on your project context.\",\r\n \"general2Q\": \"This is a placeholder question that needs customization\",\r\n \"general2A\": \"This answer will be automatically generated by AI based on your industry and service offerings. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"general3Q\": \"Customize this question for your FAQ section\",\r\n \"general3A\": \"AI will generate appropriate answers based on your site needs. Ut enim ad minim veniam, quis nostrud exercitation ullamco.\",\r\n \"billingTitle\": \"Billing\",\r\n \"billing1Q\": \"Replace with your billing-related question\",\r\n \"billing1A\": \"Placeholder answer text. AI will customize this content to provide accurate billing information for your plan.\",\r\n \"billing2Q\": \"This question should be replaced with real billing FAQ\",\r\n \"billing2A\": \"Lorem ipsum dolor sit amet. AI will replace this with contextually appropriate billing information for your service.\",\r\n \"billing3Q\": \"Customize this billing question\",\r\n \"billing3A\": \"This is placeholder text that will be replaced by AI with relevant billing answers based on your payment structure.\",\r\n \"technicalTitle\": \"Technical\",\r\n \"technical1Q\": \"Replace with your technical question\",\r\n \"technical1A\": \"AI will customize this technical answer based on your product or service specifications. Duis aute irure dolor in reprehenderit.\",\r\n \"technical2Q\": \"This is a placeholder technical question\",\r\n \"technical2A\": \"Placeholder answer. AI will generate appropriate technical information based on your platform capabilities.\",\r\n \"technical3Q\": \"Customize this final technical question\",\r\n \"technical3A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI will replace this with relevant technical details for your offering.\"\r\n}\r\n"
27
+ "content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"Ask Promake to customize this subtitle to match your FAQ section purpose.\",\r\n \"generalTitle\": \"General\",\r\n \"general1Q\": \"Replace this with your first general question\",\r\n \"general1A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ask Promake to replace this answer with relevant information based on your project context.\",\r\n \"general2Q\": \"This is a placeholder question that needs customization\",\r\n \"general2A\": \"This answer will be automatically generated by Promake based on your industry and service offerings. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"general3Q\": \"Customize this question for your FAQ section\",\r\n \"general3A\": \"Ask Promake to generate appropriate answers based on your site needs. Ut enim ad minim veniam, quis nostrud exercitation ullamco.\",\r\n \"billingTitle\": \"Billing\",\r\n \"billing1Q\": \"Replace with your billing-related question\",\r\n \"billing1A\": \"Placeholder answer text. Ask Promake to customize this content to provide accurate billing information for your plan.\",\r\n \"billing2Q\": \"This question should be replaced with real billing FAQ\",\r\n \"billing2A\": \"Lorem ipsum dolor sit amet. Ask Promake to replace this with contextually appropriate billing information for your service.\",\r\n \"billing3Q\": \"Customize this billing question\",\r\n \"billing3A\": \"This is placeholder text that will be replaced by Promake with relevant billing answers based on your payment structure.\",\r\n \"technicalTitle\": \"Technical\",\r\n \"technical1Q\": \"Replace with your technical question\",\r\n \"technical1A\": \"Ask Promake to customize this technical answer based on your product or service specifications. Duis aute irure dolor in reprehenderit.\",\r\n \"technical2Q\": \"This is a placeholder technical question\",\r\n \"technical2A\": \"Placeholder answer. Ask Promake to generate appropriate technical information based on your platform capabilities.\",\r\n \"technical3Q\": \"Customize this final technical question\",\r\n \"technical3A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ask Promake to replace this with relevant technical details for your offering.\"\r\n}\r\n"
28
28
  },
29
29
  {
30
30
  "path": "faq-categorized/lang/tr.json",
@@ -18,13 +18,13 @@
18
18
  "path": "faq-simple/faq-simple.tsx",
19
19
  "type": "registry:component",
20
20
  "target": "$modules$/faq-simple/faq-simple.tsx",
21
- "content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqSimpleProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqSimple({ className }: FaqSimpleProps) {\r\n const { t } = useTranslation(\"faq-simple\");\r\n\r\n const faqItems = [\r\n {\r\n id: \"faq-1\",\r\n question: t(\"q1\", \"What is included in the free plan?\"),\r\n answer: t(\"a1\", \"The free plan includes basic features such as up to 3 projects, 1GB storage, and email support. Perfect for individuals and small teams getting started.\"),\r\n },\r\n {\r\n id: \"faq-2\",\r\n question: t(\"q2\", \"Can I upgrade or downgrade my plan?\"),\r\n answer: t(\"a2\", \"Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle. No penalties for switching plans.\"),\r\n },\r\n {\r\n id: \"faq-3\",\r\n question: t(\"q3\", \"How do I cancel my subscription?\"),\r\n answer: t(\"a3\", \"You can cancel your subscription from your account settings. Your access will continue until the end of your current billing period.\"),\r\n },\r\n {\r\n id: \"faq-4\",\r\n question: t(\"q4\", \"Is my data secure?\"),\r\n answer: t(\"a4\", \"Yes, we take security seriously. All data is encrypted at rest and in transit. We comply with industry standards and regularly undergo security audits.\"),\r\n },\r\n {\r\n id: \"faq-5\",\r\n question: t(\"q5\", \"Do you offer customer support?\"),\r\n answer: t(\"a5\", \"We offer 24/7 customer support via email and live chat. Premium plans also include phone support and dedicated account managers.\"),\r\n },\r\n {\r\n id: \"faq-6\",\r\n question: t(\"q6\", \"Can I get a refund?\"),\r\n answer: t(\"a6\", \"We offer a 30-day money-back guarantee for all paid plans. If you're not satisfied, contact our support team for a full refund.\"),\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4 max-w-3xl\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground\">\r\n {t(\"subtitle\", \"Find answers to common questions about our service.\")}\r\n </p>\r\n </div>\r\n\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {faqItems.map((item, index) => (\r\n <AccordionItem key={item.id} value={`item-${index}`}>\r\n <AccordionTrigger className=\"text-left font-semibold hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
21
+ "content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqSimpleProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqSimple({ className }: FaqSimpleProps) {\r\n const { t } = useTranslation(\"faq-simple\");\r\n\r\n const faqItems = [\r\n {\r\n id: \"faq-1\",\r\n question: t(\"q1\", \"What is included in the free plan?\"),\r\n answer: t(\"a1\", \"The free plan includes basic features such as up to 3 projects, 1GB storage, and email support. Perfect for individuals and small teams getting started.\"),\r\n },\r\n {\r\n id: \"faq-2\",\r\n question: t(\"q2\", \"Can I upgrade or downgrade my plan?\"),\r\n answer: t(\"a2\", \"Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle. No penalties for switching plans.\"),\r\n },\r\n {\r\n id: \"faq-3\",\r\n question: t(\"q3\", \"How do I cancel my subscription?\"),\r\n answer: t(\"a3\", \"You can cancel your subscription from your account settings. Your access will continue until the end of your current billing period.\"),\r\n },\r\n {\r\n id: \"faq-4\",\r\n question: t(\"q4\", \"Is my data secure?\"),\r\n answer: t(\"a4\", \"Yes, we take security seriously. All data is encrypted at rest and in transit. We comply with industry standards and regularly undergo security audits.\"),\r\n },\r\n {\r\n id: \"faq-5\",\r\n question: t(\"q5\", \"Do you offer customer support?\"),\r\n answer: t(\"a5\", \"We offer 24/7 customer support via email and live chat. Premium plans also include phone support and dedicated account managers.\"),\r\n },\r\n {\r\n id: \"faq-6\",\r\n question: t(\"q6\", \"Can I get a refund?\"),\r\n answer: t(\"a6\", \"We offer a 30-day money-back guarantee for all paid plans. If you're not satisfied, contact our support team for a full refund.\"),\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 max-w-3xl\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground\">\r\n {t(\"subtitle\", \"Find answers to common questions about our service.\")}\r\n </p>\r\n </div>\r\n\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {faqItems.map((item, index) => (\r\n <AccordionItem key={item.id} value={`item-${index}`}>\r\n <AccordionTrigger className=\"text-left font-semibold hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
22
22
  },
23
23
  {
24
24
  "path": "faq-simple/lang/en.json",
25
25
  "type": "registry:lang",
26
26
  "target": "$modules$/faq-simple/lang/en.json",
27
- "content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"AI will customize this subtitle based on your service or product offering.\",\r\n \"q1\": \"Replace this question with your first FAQ\",\r\n \"a1\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. AI will replace this answer with relevant information.\",\r\n \"q2\": \"This is a placeholder question that needs to be customized\",\r\n \"a2\": \"This answer will be automatically replaced by AI based on your project context and the question above. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"q3\": \"Customize this question to match your service\",\r\n \"a3\": \"AI will generate appropriate answers based on your site goals and audience. Duis aute irure dolor in reprehenderit in voluptate velit esse.\",\r\n \"q4\": \"Replace with your actual frequently asked question\",\r\n \"a4\": \"Placeholder answer text. AI will customize this content to provide accurate information about your product or service offerings.\",\r\n \"q5\": \"This question should be replaced with real FAQ content\",\r\n \"a5\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI will replace this with contextually appropriate information for your site.\",\r\n \"q6\": \"Customize this final question for your FAQ section\",\r\n \"a6\": \"This is placeholder text that will be replaced by AI with relevant answers based on your specific needs and audience questions.\"\r\n}\r\n"
27
+ "content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"Ask Promake to customize this subtitle based on your service or product offering.\",\r\n \"q1\": \"Replace this question with your first FAQ\",\r\n \"a1\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ask Promake to replace this answer with relevant information.\",\r\n \"q2\": \"This is a placeholder question that needs to be customized\",\r\n \"a2\": \"This answer will be automatically replaced by Promake based on your project context and the question above. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"q3\": \"Customize this question to match your service\",\r\n \"a3\": \"Ask Promake to generate appropriate answers based on your site goals and audience. Duis aute irure dolor in reprehenderit in voluptate velit esse.\",\r\n \"q4\": \"Replace with your actual frequently asked question\",\r\n \"a4\": \"Placeholder answer text. Ask Promake to customize this content to provide accurate information about your product or service offerings.\",\r\n \"q5\": \"This question should be replaced with real FAQ content\",\r\n \"a5\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ask Promake to replace this with contextually appropriate information for your site.\",\r\n \"q6\": \"Customize this final question for your FAQ section\",\r\n \"a6\": \"This is placeholder text that will be replaced by Promake with relevant answers based on your specific needs and audience questions.\"\r\n}\r\n"
28
28
  },
29
29
  {
30
30
  "path": "faq-simple/lang/tr.json",
@@ -19,7 +19,7 @@
19
19
  "path": "favorites-blog-block/favorites-blog-block.tsx",
20
20
  "type": "registry:block",
21
21
  "target": "$modules$/favorites-blog-block/favorites-blog-block.tsx",
22
- "content": "import { Link } from \"react-router\";\nimport { Heart, BookOpen } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface FavoritesBlogBlockProps {\n favorites: Post[];\n onClearAll?: () => void;\n}\n\nexport function FavoritesBlogBlock({\n favorites,\n onClearAll,\n}: FavoritesBlogBlockProps) {\n const { t } = useTranslation(\"favorites-blog-block\");\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/blog\">\n <BookOpen className=\"w-5 h-5 mr-2\" />\n {t(\"browseBlog\", \"Browse Blog\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n // Favorites Grid\n return (\n <div className=\"min-h-screen py-12\">\n <div className=\"container mx-auto px-4\">\n {/* Header */}\n <div className=\"flex justify-between items-center mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"favoritesCount\",\n `You have ${favorites.length} favorite posts`\n )}\n </p>\n </div>\n {onClearAll && favorites.length > 0 && (\n <Button variant=\"outline\" onClick={onClearAll}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n )}\n </div>\n\n {/* Posts Grid */}\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\n {favorites.map((post) => (\n <PostCard key={post.id} post={post} layout=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
22
+ "content": "import { Link } from \"react-router\";\nimport { Heart, BookOpen } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface FavoritesBlogBlockProps {\n favorites: Post[];\n onClearAll?: () => void;\n}\n\nexport function FavoritesBlogBlock({\n favorites,\n onClearAll,\n}: FavoritesBlogBlockProps) {\n const { t } = useTranslation(\"favorites-blog-block\");\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/blog\">\n <BookOpen className=\"w-5 h-5 mr-2\" />\n {t(\"browseBlog\", \"Browse Blog\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n // Favorites Grid\n return (\n <div className=\"min-h-screen py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <div className=\"flex justify-between items-center mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"favoritesCount\",\n `You have ${favorites.length} favorite posts`\n )}\n </p>\n </div>\n {onClearAll && favorites.length > 0 && (\n <Button variant=\"outline\" onClick={onClearAll}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n )}\n </div>\n\n {/* Posts Grid */}\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\n {favorites.map((post) => (\n <PostCard key={post.id} post={post} layout=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
23
23
  },
24
24
  {
25
25
  "path": "favorites-blog-block/lang/en.json",
@@ -19,7 +19,7 @@
19
19
  "path": "favorites-ecommerce-block/favorites-ecommerce-block.tsx",
20
20
  "type": "registry:block",
21
21
  "target": "$modules$/favorites-ecommerce-block/favorites-ecommerce-block.tsx",
22
- "content": "import { Link } from \"react-router\";\nimport { Heart, ShoppingBag } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\ninterface FavoritesEcommerceBlockProps {\n favorites: Product[];\n onClearAll?: () => void;\n}\n\nexport function FavoritesEcommerceBlock({\n favorites,\n onClearAll,\n}: FavoritesEcommerceBlockProps) {\n const { t } = useTranslation(\"favorites-ecommerce-block\");\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our products and add items to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t(\"browseProducts\", \"Browse Products\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n // Favorites Grid\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold text-foreground mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {favorites.length}{\" \"}\n {t(\n \"itemsInFavorites\",\n `item${favorites.length !== 1 ? \"s\" : \"\"} in your favorites`\n )}\n </p>\n </div>\n {onClearAll && (\n <Button variant=\"outline\" onClick={onClearAll}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n )}\n </div>\n\n {/* Products Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {favorites.map((product) => (\n <ProductCard key={product.id} product={product} variant=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
22
+ "content": "import { Link } from \"react-router\";\nimport { Heart, ShoppingBag } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\ninterface FavoritesEcommerceBlockProps {\n favorites: Product[];\n onClearAll?: () => void;\n}\n\nexport function FavoritesEcommerceBlock({\n favorites = [],\n onClearAll,\n}: FavoritesEcommerceBlockProps) {\n const { t } = useTranslation(\"favorites-ecommerce-block\");\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our products and add items to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t(\"browseProducts\", \"Browse Products\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n // Favorites Grid\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold text-foreground mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {favorites.length}{\" \"}\n {t(\n \"itemsInFavorites\",\n `item${favorites.length !== 1 ? \"s\" : \"\"} in your favorites`\n )}\n </p>\n </div>\n {onClearAll && (\n <Button variant=\"outline\" onClick={onClearAll}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n )}\n </div>\n\n {/* Products Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {favorites.map((product) => (\n <ProductCard key={product.id} product={product} variant=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
23
23
  },
24
24
  {
25
25
  "path": "favorites-ecommerce-block/lang/en.json",