@promakeai/cli 0.0.5

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 (285) hide show
  1. package/dist/index.js +212 -0
  2. package/dist/registry/about-page.json +45 -0
  3. package/dist/registry/about-section.json +40 -0
  4. package/dist/registry/animations.json +69 -0
  5. package/dist/registry/bento-grid-section.json +42 -0
  6. package/dist/registry/blog-core.json +74 -0
  7. package/dist/registry/blog-list-page.json +48 -0
  8. package/dist/registry/blog-section.json +43 -0
  9. package/dist/registry/cards-carousel-section.json +46 -0
  10. package/dist/registry/cart-drawer.json +43 -0
  11. package/dist/registry/cart-page.json +47 -0
  12. package/dist/registry/category-section.json +43 -0
  13. package/dist/registry/checkout-page.json +47 -0
  14. package/dist/registry/contact-info-grid.json +40 -0
  15. package/dist/registry/contact-page-centered.json +50 -0
  16. package/dist/registry/contact-page-map-overlay.json +54 -0
  17. package/dist/registry/contact-page-map-split.json +54 -0
  18. package/dist/registry/contact-page-split.json +49 -0
  19. package/dist/registry/contact-page.json +45 -0
  20. package/dist/registry/content-section.json +40 -0
  21. package/dist/registry/cookies-page.json +45 -0
  22. package/dist/registry/cta-section.json +40 -0
  23. package/dist/registry/docs/about-page.md +32 -0
  24. package/dist/registry/docs/about-section.md +33 -0
  25. package/dist/registry/docs/animations.md +44 -0
  26. package/dist/registry/docs/bento-grid-section.md +40 -0
  27. package/dist/registry/docs/blog-core.md +37 -0
  28. package/dist/registry/docs/blog-list-page.md +38 -0
  29. package/dist/registry/docs/blog-section.md +39 -0
  30. package/dist/registry/docs/cards-carousel-section.md +39 -0
  31. package/dist/registry/docs/cart-drawer.md +42 -0
  32. package/dist/registry/docs/cart-page.md +37 -0
  33. package/dist/registry/docs/category-section.md +34 -0
  34. package/dist/registry/docs/checkout-page.md +38 -0
  35. package/dist/registry/docs/contact-info-grid.md +33 -0
  36. package/dist/registry/docs/contact-page-centered.md +41 -0
  37. package/dist/registry/docs/contact-page-map-overlay.md +44 -0
  38. package/dist/registry/docs/contact-page-map-split.md +44 -0
  39. package/dist/registry/docs/contact-page-split.md +40 -0
  40. package/dist/registry/docs/contact-page.md +33 -0
  41. package/dist/registry/docs/content-section.md +35 -0
  42. package/dist/registry/docs/cookies-page.md +32 -0
  43. package/dist/registry/docs/cta-section.md +32 -0
  44. package/dist/registry/docs/ecommerce-core.md +41 -0
  45. package/dist/registry/docs/empty-page.md +31 -0
  46. package/dist/registry/docs/faq-categorized.md +38 -0
  47. package/dist/registry/docs/faq-simple.md +38 -0
  48. package/dist/registry/docs/favorites-blog-block.md +38 -0
  49. package/dist/registry/docs/favorites-ecommerce-block.md +38 -0
  50. package/dist/registry/docs/feature-section.md +33 -0
  51. package/dist/registry/docs/featured-products.md +38 -0
  52. package/dist/registry/docs/footer-detailed.md +33 -0
  53. package/dist/registry/docs/footer-minimal.md +32 -0
  54. package/dist/registry/docs/footer.md +32 -0
  55. package/dist/registry/docs/google-map.md +36 -0
  56. package/dist/registry/docs/header-centered-pill.md +37 -0
  57. package/dist/registry/docs/header-ecommerce.md +38 -0
  58. package/dist/registry/docs/header-mega.md +40 -0
  59. package/dist/registry/docs/header-minimal.md +38 -0
  60. package/dist/registry/docs/header-simple.md +32 -0
  61. package/dist/registry/docs/hero-cta.md +38 -0
  62. package/dist/registry/docs/hero-gradient.md +33 -0
  63. package/dist/registry/docs/hero-grid.md +40 -0
  64. package/dist/registry/docs/hero-profile.md +33 -0
  65. package/dist/registry/docs/hero.md +32 -0
  66. package/dist/registry/docs/login-page-split.md +40 -0
  67. package/dist/registry/docs/login-page.md +39 -0
  68. package/dist/registry/docs/newsletter-section.md +40 -0
  69. package/dist/registry/docs/order-card-compact.md +37 -0
  70. package/dist/registry/docs/order-detail-block.md +37 -0
  71. package/dist/registry/docs/orders-list-block.md +40 -0
  72. package/dist/registry/docs/payment-success-block.md +32 -0
  73. package/dist/registry/docs/post-card.md +37 -0
  74. package/dist/registry/docs/post-detail-block.md +37 -0
  75. package/dist/registry/docs/pricing-card.md +37 -0
  76. package/dist/registry/docs/pricing-section.md +39 -0
  77. package/dist/registry/docs/privacy-page.md +32 -0
  78. package/dist/registry/docs/product-card-detailed.md +42 -0
  79. package/dist/registry/docs/product-card-hover.md +35 -0
  80. package/dist/registry/docs/product-card.md +37 -0
  81. package/dist/registry/docs/product-detail-block.md +37 -0
  82. package/dist/registry/docs/product-detail-section.md +45 -0
  83. package/dist/registry/docs/products-page.md +39 -0
  84. package/dist/registry/docs/related-posts-block.md +38 -0
  85. package/dist/registry/docs/related-products-block.md +38 -0
  86. package/dist/registry/docs/service-card.md +34 -0
  87. package/dist/registry/docs/skill-card.md +33 -0
  88. package/dist/registry/docs/terms-page.md +32 -0
  89. package/dist/registry/docs/testimonials-carousel.md +40 -0
  90. package/dist/registry/docs/testimonials-grid.md +39 -0
  91. package/dist/registry/ecommerce-core.json +95 -0
  92. package/dist/registry/empty-page.json +45 -0
  93. package/dist/registry/faq-categorized.json +42 -0
  94. package/dist/registry/faq-simple.json +42 -0
  95. package/dist/registry/favorites-blog-block.json +43 -0
  96. package/dist/registry/favorites-ecommerce-block.json +43 -0
  97. package/dist/registry/feature-section.json +40 -0
  98. package/dist/registry/featured-products.json +43 -0
  99. package/dist/registry/footer-detailed.json +43 -0
  100. package/dist/registry/footer-minimal.json +40 -0
  101. package/dist/registry/footer.json +40 -0
  102. package/dist/registry/google-map.json +31 -0
  103. package/dist/registry/header-centered-pill.json +45 -0
  104. package/dist/registry/header-ecommerce.json +42 -0
  105. package/dist/registry/header-mega.json +47 -0
  106. package/dist/registry/header-minimal.json +45 -0
  107. package/dist/registry/header-simple.json +43 -0
  108. package/dist/registry/hero-cta.json +42 -0
  109. package/dist/registry/hero-gradient.json +40 -0
  110. package/dist/registry/hero-grid.json +42 -0
  111. package/dist/registry/hero-profile.json +62 -0
  112. package/dist/registry/hero.json +40 -0
  113. package/dist/registry/index.json +70 -0
  114. package/dist/registry/login-page-split.json +47 -0
  115. package/dist/registry/login-page.json +49 -0
  116. package/dist/registry/newsletter-section.json +44 -0
  117. package/dist/registry/order-card-compact.json +42 -0
  118. package/dist/registry/order-detail-block.json +42 -0
  119. package/dist/registry/orders-list-block.json +45 -0
  120. package/dist/registry/payment-success-block.json +40 -0
  121. package/dist/registry/post-card.json +42 -0
  122. package/dist/registry/post-detail-block.json +42 -0
  123. package/dist/registry/pricing-card.json +40 -0
  124. package/dist/registry/pricing-section.json +43 -0
  125. package/dist/registry/privacy-page.json +45 -0
  126. package/dist/registry/product-card-detailed.json +45 -0
  127. package/dist/registry/product-card-hover.json +40 -0
  128. package/dist/registry/product-card.json +42 -0
  129. package/dist/registry/product-detail-block.json +42 -0
  130. package/dist/registry/product-detail-section.json +46 -0
  131. package/dist/registry/products-page.json +48 -0
  132. package/dist/registry/related-posts-block.json +43 -0
  133. package/dist/registry/related-products-block.json +43 -0
  134. package/dist/registry/service-card.json +28 -0
  135. package/dist/registry/skill-card.json +28 -0
  136. package/dist/registry/terms-page.json +45 -0
  137. package/dist/registry/testimonials-carousel.json +44 -0
  138. package/dist/registry/testimonials-grid.json +43 -0
  139. package/package.json +52 -0
  140. package/template/.env +6 -0
  141. package/template/.prettierignore +3 -0
  142. package/template/.prettierrc +1 -0
  143. package/template/README.md +73 -0
  144. package/template/bun.lock +1007 -0
  145. package/template/components.json +22 -0
  146. package/template/eslint.config.js +32 -0
  147. package/template/index.html +285 -0
  148. package/template/package.json +92 -0
  149. package/template/promake.json +6 -0
  150. package/template/public/_redirects +1 -0
  151. package/template/public/data/database.db +0 -0
  152. package/template/public/favicon.svg +1 -0
  153. package/template/public/images/placeholder.png +0 -0
  154. package/template/public/robots.txt +14 -0
  155. package/template/scripts/init-db.ts +131 -0
  156. package/template/src/App.tsx +33 -0
  157. package/template/src/components/Footer.tsx +100 -0
  158. package/template/src/components/Header.tsx +79 -0
  159. package/template/src/components/Hero.tsx +69 -0
  160. package/template/src/components/LanguageSwitcher.tsx +47 -0
  161. package/template/src/components/Layout.tsx +25 -0
  162. package/template/src/components/Logo.tsx +64 -0
  163. package/template/src/components/ThemeSwitcher.tsx +58 -0
  164. package/template/src/components/ui/accordion.tsx +64 -0
  165. package/template/src/components/ui/alert-dialog.tsx +155 -0
  166. package/template/src/components/ui/alert.tsx +66 -0
  167. package/template/src/components/ui/aspect-ratio.tsx +11 -0
  168. package/template/src/components/ui/avatar.tsx +51 -0
  169. package/template/src/components/ui/badge.tsx +46 -0
  170. package/template/src/components/ui/breadcrumb.tsx +109 -0
  171. package/template/src/components/ui/button-group.tsx +83 -0
  172. package/template/src/components/ui/button.tsx +62 -0
  173. package/template/src/components/ui/calendar.tsx +220 -0
  174. package/template/src/components/ui/card.tsx +92 -0
  175. package/template/src/components/ui/carousel.tsx +239 -0
  176. package/template/src/components/ui/chart.tsx +357 -0
  177. package/template/src/components/ui/checkbox.tsx +32 -0
  178. package/template/src/components/ui/collapsible.tsx +31 -0
  179. package/template/src/components/ui/command.tsx +182 -0
  180. package/template/src/components/ui/context-menu.tsx +252 -0
  181. package/template/src/components/ui/dialog.tsx +141 -0
  182. package/template/src/components/ui/drawer.tsx +135 -0
  183. package/template/src/components/ui/dropdown-menu.tsx +255 -0
  184. package/template/src/components/ui/empty.tsx +104 -0
  185. package/template/src/components/ui/field.tsx +246 -0
  186. package/template/src/components/ui/form.tsx +168 -0
  187. package/template/src/components/ui/hover-card.tsx +44 -0
  188. package/template/src/components/ui/input-group.tsx +170 -0
  189. package/template/src/components/ui/input-otp.tsx +75 -0
  190. package/template/src/components/ui/input.tsx +21 -0
  191. package/template/src/components/ui/item.tsx +193 -0
  192. package/template/src/components/ui/kbd.tsx +28 -0
  193. package/template/src/components/ui/label.tsx +24 -0
  194. package/template/src/components/ui/menubar.tsx +274 -0
  195. package/template/src/components/ui/navigation-menu.tsx +168 -0
  196. package/template/src/components/ui/pagination.tsx +127 -0
  197. package/template/src/components/ui/popover.tsx +48 -0
  198. package/template/src/components/ui/progress.tsx +29 -0
  199. package/template/src/components/ui/radio-group.tsx +45 -0
  200. package/template/src/components/ui/resizable.tsx +54 -0
  201. package/template/src/components/ui/scroll-area.tsx +58 -0
  202. package/template/src/components/ui/select.tsx +188 -0
  203. package/template/src/components/ui/separator.tsx +28 -0
  204. package/template/src/components/ui/sheet.tsx +137 -0
  205. package/template/src/components/ui/sidebar.tsx +726 -0
  206. package/template/src/components/ui/skeleton.tsx +13 -0
  207. package/template/src/components/ui/slider.tsx +63 -0
  208. package/template/src/components/ui/sonner.tsx +38 -0
  209. package/template/src/components/ui/spinner.tsx +16 -0
  210. package/template/src/components/ui/switch.tsx +31 -0
  211. package/template/src/components/ui/table.tsx +114 -0
  212. package/template/src/components/ui/tabs.tsx +66 -0
  213. package/template/src/components/ui/textarea.tsx +18 -0
  214. package/template/src/components/ui/toggle-group.tsx +81 -0
  215. package/template/src/components/ui/toggle.tsx +45 -0
  216. package/template/src/components/ui/tooltip.tsx +61 -0
  217. package/template/src/constants/constants.json +58 -0
  218. package/template/src/hooks/use-is-mobile.ts +21 -0
  219. package/template/src/hooks/use-page-title.ts +49 -0
  220. package/template/src/hooks/use-theme.ts +57 -0
  221. package/template/src/index.css +128 -0
  222. package/template/src/lang/en/about.json +4 -0
  223. package/template/src/lang/en/contact.json +39 -0
  224. package/template/src/lang/en/cookies.json +4 -0
  225. package/template/src/lang/en/footer.json +12 -0
  226. package/template/src/lang/en/forgotPassword.json +37 -0
  227. package/template/src/lang/en/header.json +10 -0
  228. package/template/src/lang/en/hero.json +8 -0
  229. package/template/src/lang/en/index.json +30 -0
  230. package/template/src/lang/en/login.json +18 -0
  231. package/template/src/lang/en/notfound.json +7 -0
  232. package/template/src/lang/en/privacy.json +4 -0
  233. package/template/src/lang/en/register.json +25 -0
  234. package/template/src/lang/en/terms.json +4 -0
  235. package/template/src/lang/index.ts +86 -0
  236. package/template/src/lang/tr/about.json +4 -0
  237. package/template/src/lang/tr/contact.json +39 -0
  238. package/template/src/lang/tr/cookies.json +4 -0
  239. package/template/src/lang/tr/footer.json +12 -0
  240. package/template/src/lang/tr/forgotPassword.json +37 -0
  241. package/template/src/lang/tr/header.json +10 -0
  242. package/template/src/lang/tr/hero.json +8 -0
  243. package/template/src/lang/tr/index.json +30 -0
  244. package/template/src/lang/tr/login.json +18 -0
  245. package/template/src/lang/tr/notfound.json +7 -0
  246. package/template/src/lang/tr/privacy.json +4 -0
  247. package/template/src/lang/tr/register.json +25 -0
  248. package/template/src/lang/tr/terms.json +4 -0
  249. package/template/src/lib/api.ts +237 -0
  250. package/template/src/lib/storage.ts +109 -0
  251. package/template/src/lib/utils.ts +15 -0
  252. package/template/src/main.tsx +13 -0
  253. package/template/src/modules/api/USAGE.md +515 -0
  254. package/template/src/modules/api/customer-client.ts +20 -0
  255. package/template/src/modules/api/get-error-message.ts +18 -0
  256. package/template/src/modules/api/validation/en.json +29 -0
  257. package/template/src/modules/api/validation/tr.json +29 -0
  258. package/template/src/modules/auth/USAGE.md +248 -0
  259. package/template/src/modules/auth/auth-header-menu.tsx +123 -0
  260. package/template/src/modules/auth/auth-store.ts +57 -0
  261. package/template/src/modules/auth/forgot-password-page.tsx +371 -0
  262. package/template/src/modules/auth/login-page.tsx +183 -0
  263. package/template/src/modules/auth/register-page.tsx +252 -0
  264. package/template/src/modules/auth/use-auth.ts +273 -0
  265. package/template/src/modules/db/adapters/IDataAdapter.ts +26 -0
  266. package/template/src/modules/db/adapters/SqliteAdapter.ts +364 -0
  267. package/template/src/modules/db/adapters/index.ts +2 -0
  268. package/template/src/modules/db/config.ts +59 -0
  269. package/template/src/modules/db/core/DataManager.ts +125 -0
  270. package/template/src/modules/db/core/types.ts +101 -0
  271. package/template/src/modules/db/index.ts +42 -0
  272. package/template/src/modules/db/react/QueryProvider.tsx +16 -0
  273. package/template/src/modules/db/react/index.ts +23 -0
  274. package/template/src/modules/db/react/queryClient.ts +64 -0
  275. package/template/src/modules/db/react/useRepository.ts +400 -0
  276. package/template/src/modules/db/utils/parsers.ts +96 -0
  277. package/template/src/pages/Index.tsx +108 -0
  278. package/template/src/pages/NotFound.tsx +35 -0
  279. package/template/src/router.tsx +14 -0
  280. package/template/src/types/index.ts +0 -0
  281. package/template/src/vite-env.d.ts +1 -0
  282. package/template/tsconfig.app.json +32 -0
  283. package/template/tsconfig.json +17 -0
  284. package/template/tsconfig.node.json +26 -0
  285. package/template/vite.config.ts +74 -0
@@ -0,0 +1,38 @@
1
+ # Related Posts Block
2
+
3
+ Related posts section with heading and 3-column grid. Shows posts by same category or tags. Use at bottom of post detail pages.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/related-posts-block/index.ts` | index |
10
+ | `$modules$/related-posts-block/related-posts-block.tsx` | block |
11
+ | `$modules$/related-posts-block/lang/en.json` | lang |
12
+ | `$modules$/related-posts-block/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `RelatedPostsBlock`
17
+
18
+ ```typescript
19
+ import { RelatedPostsBlock } from '@/modules/related-posts-block';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { RelatedPostsBlock } from '@/modules/related-posts-block';
26
+
27
+ <RelatedPostsBlock posts={relatedPosts} />
28
+
29
+ • Props: posts (Post[] from blog-core)
30
+ • Uses PostCard component
31
+ • Responsive: 3 cols desktop, 1 col mobile
32
+ ```
33
+
34
+ ## Dependencies
35
+
36
+ This component requires:
37
+ - `blog-core`
38
+ - `post-card`
@@ -0,0 +1,38 @@
1
+ # Related Products Block
2
+
3
+ Related/recommended products section with heading and horizontal scrollable grid. Shows 4 products on desktop, swipeable on mobile. Use on product detail pages.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/related-products-block/index.ts` | index |
10
+ | `$modules$/related-products-block/related-products-block.tsx` | block |
11
+ | `$modules$/related-products-block/lang/en.json` | lang |
12
+ | `$modules$/related-products-block/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `RelatedProductsBlock`
17
+
18
+ ```typescript
19
+ import { RelatedProductsBlock } from '@/modules/related-products-block';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { RelatedProductsBlock } from '@/modules/related-products-block';
26
+
27
+ <RelatedProductsBlock products={relatedProducts} />
28
+
29
+ • Props: products (Product[] from ecommerce-core)
30
+ • Uses ProductCard component
31
+ • Responsive: 4 cols desktop, scrollable mobile
32
+ ```
33
+
34
+ ## Dependencies
35
+
36
+ This component requires:
37
+ - `ecommerce-core`
38
+ - `product-card`
@@ -0,0 +1,34 @@
1
+ # Service Card
2
+
3
+ Service/offering display card with large icon, service title, description text, and optional 'Learn More' link. Features hover lift effect, subtle border, and icon background circle. Great for displaying services, features, or benefits in a grid layout.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/service-card/index.ts` | index |
10
+ | `$modules$/service-card/service-card.tsx` | component |
11
+
12
+ ## Exports
13
+
14
+ **Components/Functions:** `ServiceCard`
15
+
16
+ ```typescript
17
+ import { ServiceCard } from '@/modules/service-card';
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```
23
+ import { ServiceCard } from '@/modules/service-card';
24
+
25
+ <ServiceCard
26
+ icon={<CodeIcon />}
27
+ title="Web Development"
28
+ description="Custom web applications..."
29
+ link="/services/web"
30
+ />
31
+
32
+ • Installed at: src/modules/service-card/
33
+ • Use in grid: grid grid-cols-1 md:grid-cols-3 gap-6
34
+ ```
@@ -0,0 +1,33 @@
1
+ # Skill Card
2
+
3
+ Portfolio skill display card with icon/logo, skill name, proficiency level (percentage or text), and animated progress bar. Progress bar animates on scroll into view. Supports different sizes (sm, md, lg) and color variants. Perfect for showcasing technical skills in portfolios.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/skill-card/index.ts` | index |
10
+ | `$modules$/skill-card/skill-card.tsx` | component |
11
+
12
+ ## Exports
13
+
14
+ **Components/Functions:** `SkillCard`
15
+
16
+ ```typescript
17
+ import { SkillCard } from '@/modules/skill-card';
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```
23
+ import { SkillCard } from '@/modules/skill-card';
24
+
25
+ <SkillCard
26
+ name="React"
27
+ icon={<ReactIcon />}
28
+ level={90}
29
+ />
30
+
31
+ • Installed at: src/modules/skill-card/
32
+ • Use in grid: grid grid-cols-2 md:grid-cols-4 gap-4
33
+ ```
@@ -0,0 +1,32 @@
1
+ # Terms of Service Page
2
+
3
+ Terms of service/use page with numbered sections covering account terms, acceptable use, intellectual property, disclaimers, limitation of liability, and termination. Includes table of contents, collapsible sections, and professional legal formatting. Last updated date and acceptance notice included.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/terms-page/index.ts` | index |
10
+ | `$modules$/terms-page/terms-page.tsx` | page |
11
+ | `$modules$/terms-page/lang/en.json` | lang |
12
+ | `$modules$/terms-page/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `TermsPage`, `default`
17
+
18
+ ```typescript
19
+ import { TermsPage, default } from '@/modules/terms-page';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { TermsPage } from '@/modules/terms-page';
26
+
27
+ <Route path="/terms" element={<TermsPage />} />
28
+
29
+ • Legal terms structure with numbered sections
30
+ • Sections: account, use, IP, liability, termination
31
+ • Edit content in lang/en.json
32
+ ```
@@ -0,0 +1,40 @@
1
+ # Testimonials Carousel
2
+
3
+ Carousel-based testimonials section using Embla Carousel. Features customer cards with avatar, name, role, review text, and star ratings. Includes dot navigation and arrow controls. Responsive design shows 1-3 cards based on screen size.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/testimonials-carousel/index.ts` | index |
10
+ | `$modules$/testimonials-carousel/testimonials-carousel.tsx` | component |
11
+ | `$modules$/testimonials-carousel/lang/en.json` | lang |
12
+ | `$modules$/testimonials-carousel/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `TestimonialsCarousel`
17
+
18
+ ```typescript
19
+ import { TestimonialsCarousel } from '@/modules/testimonials-carousel';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { TestimonialsCarousel } from '@/modules/testimonials-carousel';
26
+
27
+ <TestimonialsCarousel />
28
+
29
+ - Embla carousel with loop
30
+ - Dot pagination controls
31
+ - Star ratings
32
+ - Responsive 1-3 column layout
33
+ ```
34
+
35
+ ## Dependencies
36
+
37
+ This component requires:
38
+ - `carousel`
39
+ - `card`
40
+ - `avatar`
@@ -0,0 +1,39 @@
1
+ # Testimonials Grid
2
+
3
+ Static 3-column grid testimonials section without carousel. Features quote icon, review text, star ratings, and author info with avatar. Simple, clean design with subtle background. Great for landing pages and about pages.
4
+
5
+ ## Files
6
+
7
+ | Target | Type |
8
+ |--------|------|
9
+ | `$modules$/testimonials-grid/index.ts` | index |
10
+ | `$modules$/testimonials-grid/testimonials-grid.tsx` | component |
11
+ | `$modules$/testimonials-grid/lang/en.json` | lang |
12
+ | `$modules$/testimonials-grid/lang/tr.json` | lang |
13
+
14
+ ## Exports
15
+
16
+ **Components/Functions:** `TestimonialsGrid`
17
+
18
+ ```typescript
19
+ import { TestimonialsGrid } from '@/modules/testimonials-grid';
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ import { TestimonialsGrid } from '@/modules/testimonials-grid';
26
+
27
+ <TestimonialsGrid />
28
+
29
+ - Static 3-column grid
30
+ - Quote icon decoration
31
+ - Star ratings
32
+ - No JavaScript carousel needed
33
+ ```
34
+
35
+ ## Dependencies
36
+
37
+ This component requires:
38
+ - `card`
39
+ - `avatar`
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "ecommerce-core",
3
+ "type": "registry:module",
4
+ "title": "E-commerce Core",
5
+ "description": "Complete e-commerce state management with Zustand. Includes useCartStore for shopping cart operations (add/remove/update items, totals), useFavoritesStore for wishlist, useProducts hook for product fetching with filtering/sorting/pagination, and useSearch hook. No provider wrapping needed.",
6
+ "dependencies": [
7
+ "zustand"
8
+ ],
9
+ "registryDependencies": [],
10
+ "usage": "import { useCart, useFavorites, useProducts } from '@/modules/ecommerce-core';\n\n// No provider needed - just use the hooks:\nconst { addItem, removeItem, state, itemCount } = useCart();\nconst { addToFavorites, isFavorite } = useFavorites();\nconst { products, loading } = useProducts();\n\n// Or use stores directly with selectors:\nconst itemCount = useCartStore((s) => s.itemCount);",
11
+ "files": [
12
+ {
13
+ "path": "ecommerce-core/index.ts",
14
+ "type": "registry:index",
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"
17
+ },
18
+ {
19
+ "path": "ecommerce-core/types.ts",
20
+ "type": "registry:type",
21
+ "target": "$modules$/ecommerce-core/types.ts",
22
+ "content": "export interface ProductCategory {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n is_primary: boolean;\r\n}\r\n\r\nexport interface Product {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n description: string;\r\n price: number;\r\n sale_price?: number;\r\n on_sale: boolean;\r\n images: string[];\r\n category: string; // Primary category slug (backward compatibility)\r\n category_name?: string; // Primary category name (backward compatibility)\r\n categories: ProductCategory[]; // NEW: Multi-category support\r\n brand?: string;\r\n sku?: string;\r\n stock: number;\r\n tags: string[];\r\n rating: number;\r\n review_count: number;\r\n featured: boolean;\r\n is_new: boolean;\r\n published: boolean;\r\n specifications?: Record<string, any>;\r\n variants?: any;\r\n created_at?: string;\r\n updated_at?: string;\r\n meta_description?: string;\r\n meta_keywords?: string;\r\n}\r\n\r\nexport interface ProductVariant {\r\n id: string;\r\n name: string;\r\n value: string;\r\n price?: number;\r\n image?: string;\r\n stockQuantity: number;\r\n}\r\n\r\nexport interface CartItem {\r\n id: string | number;\r\n product: Product;\r\n quantity: number;\r\n}\r\n\r\nexport interface CartState {\r\n items: CartItem[];\r\n total: number;\r\n}\r\n\r\nexport interface CartContextType {\r\n state: 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 interface FavoritesContextType {\r\n favorites: Product[];\r\n addToFavorites: (product: Product) => void;\r\n removeFromFavorites: (productId: string | number) => void;\r\n isFavorite: (productId: string | number) => boolean;\r\n favoriteCount: number;\r\n}\r\n\r\nexport interface Category {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n description?: string;\r\n image?: string;\r\n}\r\n\r\nexport interface User {\r\n id: string;\r\n email: string;\r\n name: string;\r\n avatar?: string;\r\n addresses?: Address[];\r\n orders?: Order[];\r\n}\r\n\r\nexport interface Address {\r\n name: string;\r\n line1: string;\r\n line2?: string;\r\n city: string;\r\n state: string;\r\n postalCode: string;\r\n country: string;\r\n}\r\n\r\nexport interface Order {\r\n id: number;\r\n user_id: string;\r\n total_price: number;\r\n status: 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';\r\n payment_method: string;\r\n shipping_address: Address;\r\n notes?: string;\r\n created_at?: string;\r\n}\r\n\r\nexport interface OrderItem {\r\n id: number;\r\n order_id: number;\r\n product_id: number;\r\n quantity: number;\r\n price: number;\r\n product?: {\r\n name: string;\r\n slug: string;\r\n images: string[];\r\n price: number;\r\n };\r\n}\r\n"
23
+ },
24
+ {
25
+ "path": "ecommerce-core/stores/cart-store.ts",
26
+ "type": "registry:store",
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"
29
+ },
30
+ {
31
+ "path": "ecommerce-core/stores/favorites-store.ts",
32
+ "type": "registry:store",
33
+ "target": "$modules$/ecommerce-core/stores/favorites-store.ts",
34
+ "content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Product, FavoritesContextType } from \"../types\";\r\n\r\ninterface FavoritesStore {\r\n favorites: Product[];\r\n favoriteCount: number;\r\n addToFavorites: (product: Product) => void;\r\n removeFromFavorites: (productId: string | number) => void;\r\n isFavorite: (productId: string | number) => boolean;\r\n clearFavorites: () => void;\r\n}\r\n\r\nexport const useFavoritesStore = create<FavoritesStore>()(\r\n persist(\r\n (set, get) => ({\r\n favorites: [],\r\n favoriteCount: 0,\r\n\r\n addToFavorites: (product) =>\r\n set((state) => {\r\n if (state.favorites.some((fav) => fav.id === product.id)) {\r\n return state;\r\n }\r\n const favorites = [...state.favorites, product];\r\n return { favorites, favoriteCount: favorites.length };\r\n }),\r\n\r\n removeFromFavorites: (productId) =>\r\n set((state) => {\r\n const favorites = state.favorites.filter((fav) => fav.id !== productId);\r\n return { favorites, favoriteCount: favorites.length };\r\n }),\r\n\r\n isFavorite: (productId) => {\r\n return get().favorites.some((fav) => fav.id === productId);\r\n },\r\n\r\n clearFavorites: () => set({ favorites: [], favoriteCount: 0 }),\r\n }),\r\n { name: \"ecommerce_favorites\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches FavoritesContextType\r\nexport const useFavorites = (): FavoritesContextType => {\r\n const store = useFavoritesStore();\r\n return {\r\n favorites: store.favorites,\r\n addToFavorites: store.addToFavorites,\r\n removeFromFavorites: store.removeFromFavorites,\r\n isFavorite: store.isFavorite,\r\n favoriteCount: store.favoriteCount,\r\n };\r\n};\r\n"
35
+ },
36
+ {
37
+ "path": "ecommerce-core/useProducts.ts",
38
+ "type": "registry:hook",
39
+ "target": "$modules$/ecommerce-core/useProducts.ts",
40
+ "content": "import { useMemo } from 'react';\r\nimport type { Product, Category, ProductCategory } from './types';\r\nimport {\r\n useRepositoryQuery,\r\n useRawQuery,\r\n useRawQueryOne,\r\n parseStringToArray,\r\n parseJSONString,\r\n parseSQLiteBoolean,\r\n parseNumberSafe\r\n} from '@/modules/db';\r\n\r\nconst transformProduct = (row: any): Product => {\r\n const categoryNames = row.category_names ? row.category_names.split(',') : [];\r\n const categorySlugs = row.category_slugs ? row.category_slugs.split(',') : [];\r\n const categoryIds = row.category_ids ? row.category_ids.split(',').map(Number) : [];\r\n\r\n const categories: ProductCategory[] = categoryIds.map((id: number, index: number) => ({\r\n id,\r\n name: categoryNames[index] || '',\r\n slug: categorySlugs[index] || '',\r\n is_primary: index === 0\r\n }));\r\n\r\n const primaryCategory = categories.length > 0 ? categories[0] : null;\r\n\r\n return {\r\n id: parseNumberSafe(row.id),\r\n name: String(row.name || ''),\r\n slug: String(row.slug || ''),\r\n description: row.description || '',\r\n price: parseNumberSafe(row.price),\r\n sale_price: row.sale_price ? parseNumberSafe(row.sale_price) : undefined,\r\n on_sale: parseSQLiteBoolean(row.on_sale),\r\n images: parseStringToArray(row.images),\r\n brand: row.brand || '',\r\n sku: row.sku || '',\r\n stock: parseNumberSafe(row.stock),\r\n tags: parseJSONString(row.tags, []) || [],\r\n rating: parseNumberSafe(row.rating),\r\n review_count: parseNumberSafe(row.review_count),\r\n featured: parseSQLiteBoolean(row.featured),\r\n is_new: parseSQLiteBoolean(row.is_new),\r\n published: parseSQLiteBoolean(row.published),\r\n specifications: parseJSONString(row.specifications, {}) || {},\r\n variants: parseJSONString(row.variants, []) || [],\r\n created_at: row.created_at || new Date().toISOString(),\r\n updated_at: row.updated_at || new Date().toISOString(),\r\n meta_description: row.meta_description || '',\r\n meta_keywords: row.meta_keywords || '',\r\n category: primaryCategory?.slug || '',\r\n category_name: primaryCategory?.name || '',\r\n categories\r\n };\r\n};\r\n\r\nconst PRODUCTS_WITH_CATEGORIES_SQL = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.published = 1\r\n GROUP BY p.id\r\n`;\r\n\r\nexport function useCategories() {\r\n const { data, isLoading: loading, error } = useRepositoryQuery<Category>('product_categories', {\r\n orderBy: [{ field: 'name', direction: 'ASC' }]\r\n });\r\n\r\n return {\r\n categories: data ?? [],\r\n loading,\r\n error: error?.message ?? null\r\n };\r\n}\r\n\r\nexport function useProducts() {\r\n const sql = `${PRODUCTS_WITH_CATEGORIES_SQL} ORDER BY p.name`;\r\n\r\n const { data, isLoading: loading, error } = useRawQuery<any>(\r\n ['products', 'all'],\r\n sql\r\n );\r\n\r\n const products = useMemo(() => {\r\n if (!data) return [];\r\n return data.map(transformProduct);\r\n }, [data]);\r\n\r\n return { products, loading, error: error?.message ?? null };\r\n}\r\n\r\nexport function useProductBySlug(slug: string) {\r\n const sql = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.slug = ? AND p.published = 1\r\n GROUP BY p.id\r\n `;\r\n\r\n const { data, isLoading: loading, error } = useRawQueryOne<any>(\r\n ['products', 'slug', slug],\r\n sql,\r\n [slug],\r\n { enabled: !!slug }\r\n );\r\n\r\n const product = useMemo(() => {\r\n if (!data) return null;\r\n return transformProduct(data);\r\n }, [data]);\r\n\r\n return {\r\n product,\r\n loading,\r\n error: !data && !loading && slug ? 'Product not found' : (error?.message ?? null)\r\n };\r\n}\r\n\r\nexport function useFeaturedProducts() {\r\n const sql = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.published = 1 AND p.featured = 1\r\n GROUP BY p.id\r\n ORDER BY p.created_at DESC LIMIT 8\r\n `;\r\n\r\n const { data, isLoading: loading, error } = useRawQuery<any>(\r\n ['products', 'featured'],\r\n sql\r\n );\r\n\r\n const products = useMemo(() => {\r\n if (!data) return [];\r\n return data.map(transformProduct);\r\n }, [data]);\r\n\r\n return { products, loading, error: error?.message ?? null };\r\n}\r\n"
41
+ },
42
+ {
43
+ "path": "ecommerce-core/useSearch.ts",
44
+ "type": "registry:hook",
45
+ "target": "$modules$/ecommerce-core/useSearch.ts",
46
+ "content": "import { useState, useEffect, useCallback } from 'react';\r\nimport type { Product } from './types';\r\nimport { useProducts } from './useProducts';\r\n\r\nexport const useSearch = () => {\r\n const [searchTerm, setSearchTerm] = useState('');\r\n const [results, setResults] = useState<Product[]>([]);\r\n const [isSearching, setIsSearching] = useState(false);\r\n\r\n // Load all products via useProducts hook\r\n const { products: allProducts } = useProducts();\r\n\r\n // Perform search when searchTerm changes\r\n useEffect(() => {\r\n if (!searchTerm.trim()) {\r\n setResults([]);\r\n setIsSearching(false);\r\n return;\r\n }\r\n\r\n setIsSearching(true);\r\n\r\n const searchTimeout = setTimeout(() => {\r\n const filtered = allProducts.filter(product => {\r\n const term = searchTerm.toLowerCase();\r\n \r\n // Search in product name\r\n if (product.name.toLowerCase().includes(term)) return true;\r\n \r\n // Search in description\r\n if (product.description.toLowerCase().includes(term)) return true;\r\n \r\n // Search in category\r\n if (product.category_name?.toLowerCase().includes(term)) return true;\r\n \r\n // Search in brand\r\n if (product.brand?.toLowerCase().includes(term)) return true;\r\n \r\n // Search in tags\r\n if (product.tags.some(tag => tag.toLowerCase().includes(term))) return true;\r\n \r\n return false;\r\n });\r\n\r\n setResults(filtered);\r\n setIsSearching(false);\r\n }, 300); // Debounce search\r\n\r\n return () => clearTimeout(searchTimeout);\r\n }, [searchTerm, allProducts]);\r\n\r\n const clearSearch = useCallback(() => {\r\n setSearchTerm('');\r\n setResults([]);\r\n setIsSearching(false);\r\n }, []);\r\n\r\n // search function that takes a term and sets the searchTerm\r\n const search = useCallback((term: string) => {\r\n setSearchTerm(term);\r\n }, []);\r\n\r\n return {\r\n searchTerm,\r\n setSearchTerm,\r\n results,\r\n isSearching,\r\n clearSearch,\r\n clearResults: clearSearch, // alias for header-ecommerce compatibility\r\n search, // function to trigger search\r\n hasResults: results.length > 0\r\n };\r\n};\r\n"
47
+ },
48
+ {
49
+ "path": "ecommerce-core/format-price.ts",
50
+ "type": "registry:lib",
51
+ "target": "$modules$/ecommerce-core/format-price.ts",
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
+ },
54
+ {
55
+ "path": "ecommerce-core/lang/en.json",
56
+ "type": "registry:lang",
57
+ "target": "$modules$/ecommerce-core/lang/en.json",
58
+ "content": "{\r\n \"cart\": \"Cart\",\r\n \"addToCart\": \"Add to Cart\",\r\n \"removeFromCart\": \"Remove\",\r\n \"clearCart\": \"Clear Cart\",\r\n \"emptyCart\": \"Your cart is empty\",\r\n \"cartTotal\": \"Total\",\r\n \"checkout\": \"Checkout\",\r\n \"quantity\": \"Quantity\",\r\n \"favorites\": \"Favorites\",\r\n \"addToFavorites\": \"Add to Favorites\",\r\n \"removeFromFavorites\": \"Remove from Favorites\",\r\n \"noFavorites\": \"No favorites yet\",\r\n \"products\": \"Products\",\r\n \"search\": \"Search\",\r\n \"searchPlaceholder\": \"Search products...\",\r\n \"noResults\": \"No products found\",\r\n \"outOfStock\": \"Out of Stock\",\r\n \"inStock\": \"In Stock\",\r\n \"sale\": \"Sale\",\r\n \"new\": \"New\",\r\n \"featured\": \"Featured\"\r\n}\r\n"
59
+ },
60
+ {
61
+ "path": "ecommerce-core/lang/tr.json",
62
+ "type": "registry:lang",
63
+ "target": "$modules$/ecommerce-core/lang/tr.json",
64
+ "content": "{\r\n \"cart\": \"Sepet\",\r\n \"addToCart\": \"Sepete Ekle\",\r\n \"removeFromCart\": \"Kaldır\",\r\n \"clearCart\": \"Sepeti Temizle\",\r\n \"emptyCart\": \"Sepetiniz boş\",\r\n \"cartTotal\": \"Toplam\",\r\n \"checkout\": \"Ödeme\",\r\n \"quantity\": \"Adet\",\r\n \"favorites\": \"Favoriler\",\r\n \"addToFavorites\": \"Favorilere Ekle\",\r\n \"removeFromFavorites\": \"Favorilerden Kaldır\",\r\n \"noFavorites\": \"Henüz favori yok\",\r\n \"products\": \"Ürünler\",\r\n \"search\": \"Ara\",\r\n \"searchPlaceholder\": \"Ürün ara...\",\r\n \"noResults\": \"Ürün bulunamadı\",\r\n \"outOfStock\": \"Stokta Yok\",\r\n \"inStock\": \"Stokta\",\r\n \"sale\": \"İndirim\",\r\n \"new\": \"Yeni\",\r\n \"featured\": \"Öne Çıkan\"\r\n}\r\n"
65
+ }
66
+ ],
67
+ "exports": {
68
+ "types": [
69
+ "Address",
70
+ "CartContextType",
71
+ "CartItem",
72
+ "CartState",
73
+ "Category",
74
+ "FavoritesContextType",
75
+ "Order",
76
+ "OrderItem",
77
+ "Product",
78
+ "ProductCategory",
79
+ "ProductVariant",
80
+ "User"
81
+ ],
82
+ "variables": [
83
+ "formatPrice",
84
+ "useCart",
85
+ "useCartStore",
86
+ "useCategories",
87
+ "useFavorites",
88
+ "useFavoritesStore",
89
+ "useFeaturedProducts",
90
+ "useProductBySlug",
91
+ "useProducts",
92
+ "useSearch"
93
+ ]
94
+ }
95
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "empty-page",
3
+ "type": "registry:page",
4
+ "title": "Empty Page",
5
+ "description": "Minimal starter page template. Use with --module-name and --route-path flags to create custom pages. Includes Layout wrapper, i18n support, and usePageTitle hook.",
6
+ "registryDependencies": [],
7
+ "usage": "promake add empty-page --module-name faq --route-path /faq\n\n• Creates: src/modules/faq/faq.tsx\n• Adds route: /faq → FaqPage\n• Edit content in lang/en.json\n• Wrapped with Layout component",
8
+ "route": {
9
+ "path": "/page",
10
+ "componentName": "Page"
11
+ },
12
+ "files": [
13
+ {
14
+ "path": "empty-page/index.ts",
15
+ "type": "registry:index",
16
+ "target": "$modules$/empty-page/index.ts",
17
+ "content": "export * from './empty-page';\r\nexport { EmptyPage as default } from './empty-page';\r\n"
18
+ },
19
+ {
20
+ "path": "empty-page/empty-page.tsx",
21
+ "type": "registry:page",
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"
24
+ },
25
+ {
26
+ "path": "empty-page/lang/en.json",
27
+ "type": "registry:lang",
28
+ "target": "$modules$/empty-page/lang/en.json",
29
+ "content": "{\r\n \"title\": \"Page Title\",\r\n \"heading\": \"Page Heading\",\r\n \"description\": \"This is an empty page template. Replace this content with your own.\"\r\n}\r\n"
30
+ },
31
+ {
32
+ "path": "empty-page/lang/tr.json",
33
+ "type": "registry:lang",
34
+ "target": "$modules$/empty-page/lang/tr.json",
35
+ "content": "{\r\n \"title\": \"Sayfa Başlığı\",\r\n \"heading\": \"Sayfa Başlığı\",\r\n \"description\": \"Bu boş bir sayfa şablonudur. Bu içeriği kendi içeriğinizle değiştirin.\"\r\n}\r\n"
36
+ }
37
+ ],
38
+ "exports": {
39
+ "types": [],
40
+ "variables": [
41
+ "EmptyPage",
42
+ "default"
43
+ ]
44
+ }
45
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "faq-categorized",
3
+ "type": "registry:component",
4
+ "title": "FAQ Categorized",
5
+ "description": "Multi-column FAQ section with questions grouped by category. Features 3 category columns (General, Billing, Technical) each with its own accordion. Responsive grid layout that stacks on mobile. Great for comprehensive FAQ pages.",
6
+ "registryDependencies": [
7
+ "accordion"
8
+ ],
9
+ "usage": "import { FaqCategorized } from '@/modules/faq-categorized';\n\n<FaqCategorized />\n\n- 3 category columns\n- Each category has accordion\n- Responsive grid (1-3 columns)\n- Customize categories via lang files",
10
+ "files": [
11
+ {
12
+ "path": "faq-categorized/index.ts",
13
+ "type": "registry:index",
14
+ "target": "$modules$/faq-categorized/index.ts",
15
+ "content": "export * from './faq-categorized';\r\n"
16
+ },
17
+ {
18
+ "path": "faq-categorized/faq-categorized.tsx",
19
+ "type": "registry:component",
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"
22
+ },
23
+ {
24
+ "path": "faq-categorized/lang/en.json",
25
+ "type": "registry:lang",
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"
28
+ },
29
+ {
30
+ "path": "faq-categorized/lang/tr.json",
31
+ "type": "registry:lang",
32
+ "target": "$modules$/faq-categorized/lang/tr.json",
33
+ "content": "{\r\n \"title\": \"Sıkça Sorulan Sorular\",\r\n \"subtitle\": \"AI bu alt başlığı SSS bölümünüzün amacına göre özelleştirecektir.\",\r\n \"generalTitle\": \"Genel\",\r\n \"general1Q\": \"Bunu ilk genel sorunuzla değiştirin\",\r\n \"general1A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI bu cevabı proje bağlamınıza göre ilgili bilgilerle değiştirecektir.\",\r\n \"general2Q\": \"Bu özelleştirilmesi gereken bir placeholder sorudur\",\r\n \"general2A\": \"Bu cevap AI tarafından sektörünüze ve hizmet tekliflerinize göre otomatik olarak üretilecektir. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"general3Q\": \"Bu soruyu SSS bölümünüz için özelleştirin\",\r\n \"general3A\": \"AI site ihtiyaçlarınıza göre uygun cevaplar üretecektir. Ut enim ad minim veniam, quis nostrud exercitation ullamco.\",\r\n \"billingTitle\": \"Faturalama\",\r\n \"billing1Q\": \"Faturalama ile ilgili sorunuzla değiştirin\",\r\n \"billing1A\": \"Placeholder cevap metni. AI bu içeriği planınız için doğru faturalama bilgileri sağlamak üzere özelleştirecektir.\",\r\n \"billing2Q\": \"Bu soru gerçek faturalama SSS'si ile değiştirilmelidir\",\r\n \"billing2A\": \"Lorem ipsum dolor sit amet. AI bunu hizmetiniz için bağlamsal olarak uygun faturalama bilgileriyle değiştirecektir.\",\r\n \"billing3Q\": \"Bu faturalama sorusunu özelleştirin\",\r\n \"billing3A\": \"Bu, AI tarafından ödeme yapınıza göre ilgili faturalama cevaplarıyla değiştirilecek placeholder metindir.\",\r\n \"technicalTitle\": \"Teknik\",\r\n \"technical1Q\": \"Teknik sorunuzla değiştirin\",\r\n \"technical1A\": \"AI bu teknik cevabı ürün veya hizmet özelliklerinize göre özelleştirecektir. Duis aute irure dolor in reprehenderit.\",\r\n \"technical2Q\": \"Bu bir placeholder teknik sorudur\",\r\n \"technical2A\": \"Placeholder cevap. AI platform yeteneklerinize göre uygun teknik bilgiler üretecektir.\",\r\n \"technical3Q\": \"Bu son teknik soruyu özelleştirin\",\r\n \"technical3A\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI bunu teklifiniz için ilgili teknik detaylarla değiştirecektir.\"\r\n}\r\n"
34
+ }
35
+ ],
36
+ "exports": {
37
+ "types": [],
38
+ "variables": [
39
+ "FaqCategorized"
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "faq-simple",
3
+ "type": "registry:component",
4
+ "title": "FAQ Simple",
5
+ "description": "Simple single-column FAQ section with accordion. Features centered heading, subtitle, and collapsible question/answer items. Clean minimal design using shadcn Accordion component. Perfect for landing pages and support sections.",
6
+ "registryDependencies": [
7
+ "accordion"
8
+ ],
9
+ "usage": "import { FaqSimple } from '@/modules/faq-simple';\n\n<FaqSimple />\n\n- Single column accordion\n- Centered header\n- Collapsible Q&A items\n- Customize via lang files",
10
+ "files": [
11
+ {
12
+ "path": "faq-simple/index.ts",
13
+ "type": "registry:index",
14
+ "target": "$modules$/faq-simple/index.ts",
15
+ "content": "export * from './faq-simple';\r\n"
16
+ },
17
+ {
18
+ "path": "faq-simple/faq-simple.tsx",
19
+ "type": "registry:component",
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"
22
+ },
23
+ {
24
+ "path": "faq-simple/lang/en.json",
25
+ "type": "registry:lang",
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"
28
+ },
29
+ {
30
+ "path": "faq-simple/lang/tr.json",
31
+ "type": "registry:lang",
32
+ "target": "$modules$/faq-simple/lang/tr.json",
33
+ "content": "{\r\n \"title\": \"Sıkça Sorulan Sorular\",\r\n \"subtitle\": \"AI bu alt başlığı hizmet veya ürün teklifinize göre özelleştirecektir.\",\r\n \"q1\": \"Bu soruyu ilk SSS'nizle değiştirin\",\r\n \"a1\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore. AI bu cevabı ilgili bilgilerle değiştirecektir.\",\r\n \"q2\": \"Bu özelleştirilmesi gereken bir placeholder sorudur\",\r\n \"a2\": \"Bu cevap AI tarafından proje bağlamınıza ve yukarıdaki soruya göre otomatik olarak değiştirilecektir. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"q3\": \"Bu soruyu hizmetinize uyacak şekilde özelleştirin\",\r\n \"a3\": \"AI hedef kitlenize ve site ihtiyaçlarınıza göre uygun cevaplar üretecektir. Duis aute irure dolor in reprehenderit in voluptate velit esse.\",\r\n \"q4\": \"Gerçek sık sorulan sorunuzla değiştirin\",\r\n \"a4\": \"Placeholder cevap metni. AI bu içeriği ürün veya hizmet teklifleriniz hakkında doğru bilgi sağlamak için özelleştirecektir.\",\r\n \"q5\": \"Bu soru gerçek SSS içeriğiyle değiştirilmelidir\",\r\n \"a5\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI bunu siteniz için bağlamsal olarak uygun bilgilerle değiştirecektir.\",\r\n \"q6\": \"SSS bölümünüz için bu son soruyu özelleştirin\",\r\n \"a6\": \"Bu, AI tarafından özel ihtiyaçlarınıza ve kullanıcı sorularınıza göre ilgili cevaplarla değiştirilecek placeholder metindir.\"\r\n}\r\n"
34
+ }
35
+ ],
36
+ "exports": {
37
+ "types": [],
38
+ "variables": [
39
+ "FaqSimple"
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "favorites-blog-block",
3
+ "type": "registry:block",
4
+ "title": "Favorites Blog Block",
5
+ "description": "Saved posts page block showing bookmarked blog posts in grid. Includes empty state with browse CTA, clear all button, and remove from favorites per item.",
6
+ "registryDependencies": [
7
+ "blog-core",
8
+ "post-card"
9
+ ],
10
+ "usage": "import { FavoritesBlogBlock } from '@/modules/favorites-blog-block';\n\n<FavoritesBlogBlock />\n\n• Uses useBlog() from blog-core (Zustand)\n• Reads favorites directly from store\n• Empty state: \"No saved posts\" with blog link",
11
+ "files": [
12
+ {
13
+ "path": "favorites-blog-block/index.ts",
14
+ "type": "registry:index",
15
+ "target": "$modules$/favorites-blog-block/index.ts",
16
+ "content": "export * from './favorites-blog-block';\r\n"
17
+ },
18
+ {
19
+ "path": "favorites-blog-block/favorites-blog-block.tsx",
20
+ "type": "registry:block",
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"
23
+ },
24
+ {
25
+ "path": "favorites-blog-block/lang/en.json",
26
+ "type": "registry:lang",
27
+ "target": "$modules$/favorites-blog-block/lang/en.json",
28
+ "content": "{\n \"title\": \"My Favorites\",\n \"noFavoritesYet\": \"No Favorites Yet\",\n \"noFavoritesDescription\": \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\",\n \"browseBlog\": \"Browse Blog\",\n \"favoritesCount\": \"You have {{count}} favorite posts\",\n \"clearAll\": \"Clear All\"\n}\n"
29
+ },
30
+ {
31
+ "path": "favorites-blog-block/lang/tr.json",
32
+ "type": "registry:lang",
33
+ "target": "$modules$/favorites-blog-block/lang/tr.json",
34
+ "content": "{\n \"title\": \"Favorilerim\",\n \"noFavoritesYet\": \"Henüz Favori Yok\",\n \"noFavoritesDescription\": \"Blogumuzu incelemeye başlayın ve kalp simgesine tıklayarak yazıları favorilerinize ekleyin.\",\n \"browseBlog\": \"Blog'a Göz At\",\n \"favoritesCount\": \"{{count}} favori yazınız var\",\n \"clearAll\": \"Tümünü Temizle\"\n}\n"
35
+ }
36
+ ],
37
+ "exports": {
38
+ "types": [],
39
+ "variables": [
40
+ "FavoritesBlogBlock"
41
+ ]
42
+ }
43
+ }