@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,45 @@
1
+ {
2
+ "name": "about-page",
3
+ "type": "registry:page",
4
+ "title": "About Page",
5
+ "description": "Company/personal about page with hero section, mission statement, team member cards with photos, company timeline/history, values section with icons, and statistics counters. Responsive layout with alternating content sections. Supports both corporate and personal portfolio styles.",
6
+ "registryDependencies": [],
7
+ "usage": "import { AboutPage } from '@/modules/about-page';\n\n<Route path=\"/about\" element={<AboutPage />} />\n\n• Sections: hero, mission, team, timeline, values, stats\n• Edit content in lang/en.json\n• Wrapped with Layout component",
8
+ "route": {
9
+ "path": "/about",
10
+ "componentName": "AboutPage"
11
+ },
12
+ "files": [
13
+ {
14
+ "path": "about-page/index.ts",
15
+ "type": "registry:index",
16
+ "target": "$modules$/about-page/index.ts",
17
+ "content": "export * from './about-page';\r\nexport { AboutPage as default } from './about-page';\r\n"
18
+ },
19
+ {
20
+ "path": "about-page/about-page.tsx",
21
+ "type": "registry:page",
22
+ "target": "$modules$/about-page/about-page.tsx",
23
+ "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Users, Target, Award, Heart } from \"lucide-react\";\nimport { FadeIn, StaggerContainer, StaggerItem, ScaleUp } from \"@/modules/animations\";\n\nexport function AboutPage() {\n const { t } = useTranslation(\"about-page\");\n usePageTitle({ title: t(\"title\") });\n\n const values = [\n { icon: Target, titleKey: \"missionTitle\", descKey: \"missionDesc\" },\n { icon: Heart, titleKey: \"valuesTitle\", descKey: \"valuesDesc\" },\n { icon: Users, titleKey: \"teamTitle\", descKey: \"teamDesc\" },\n { icon: Award, titleKey: \"qualityTitle\", descKey: \"qualityDesc\" },\n ];\n\n const stats = [\n { valueKey: \"customersValue\", labelKey: \"customersLabel\" },\n { valueKey: \"projectsValue\", labelKey: \"projectsLabel\" },\n { valueKey: \"experienceValue\", labelKey: \"experienceLabel\" },\n { valueKey: \"satisfactionValue\", labelKey: \"satisfactionLabel\" },\n ];\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 {/* Hero Section */}\n <FadeIn className=\"text-center mb-16\">\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\n </h1>\n <div className=\"w-16 h-1 bg-primary mx-auto mb-6\"></div>\n <p className=\"text-lg text-muted-foreground max-w-3xl mx-auto\">\n {t(\"subtitle\")}\n </p>\n </FadeIn>\n\n {/* Story Section */}\n <FadeIn delay={0.1} className=\"max-w-4xl mx-auto mb-16\">\n <Card>\n <CardContent className=\"p-8\">\n <h2 className=\"text-2xl font-semibold mb-4\">{t(\"storyTitle\")}</h2>\n <div className=\"space-y-4 text-muted-foreground\">\n <p>{t(\"storyP1\")}</p>\n <p>{t(\"storyP2\")}</p>\n </div>\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Values & Stats */}\n <div className=\"max-w-6xl mx-auto\">\n {/* Values Grid */}\n <StaggerContainer className=\"grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-16\">\n {values.map(({ icon: Icon, titleKey, descKey }) => (\n <StaggerItem key={titleKey}>\n <Card className=\"text-center h-full\">\n <CardContent className=\"p-6\">\n <div className=\"w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <Icon className=\"w-6 h-6 text-primary\" />\n </div>\n <h3 className=\"font-semibold mb-2\">{t(titleKey)}</h3>\n <p className=\"text-sm text-muted-foreground\">{t(descKey)}</p>\n </CardContent>\n </Card>\n </StaggerItem>\n ))}\n </StaggerContainer>\n\n {/* Stats Section */}\n <ScaleUp className=\"bg-primary/5 rounded-2xl p-8 mb-16\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-8 text-center\">\n {stats.map(({ valueKey, labelKey }) => (\n <div key={valueKey}>\n <div className=\"text-3xl font-bold text-primary mb-1\">\n {t(valueKey)}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {t(labelKey)}\n </div>\n </div>\n ))}\n </div>\n </ScaleUp>\n </div>\n\n {/* CTA Section */}\n <FadeIn className=\"text-center\">\n <h2 className=\"text-2xl font-semibold mb-4\">{t(\"ctaTitle\")}</h2>\n <p className=\"text-muted-foreground mb-6 max-w-2xl mx-auto\">\n {t(\"ctaDesc\")}\n </p>\n </FadeIn>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default AboutPage;\n"
24
+ },
25
+ {
26
+ "path": "about-page/lang/en.json",
27
+ "type": "registry:lang",
28
+ "target": "$modules$/about-page/lang/en.json",
29
+ "content": "{\r\n \"title\": \"About Us\",\r\n \"subtitle\": \"AI will customize this about page subtitle based on your site. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyTitle\": \"Our Story\",\r\n \"storyP1\": \"This is placeholder text for your story. AI will replace this with your actual background and journey. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyP2\": \"Placeholder text for the second paragraph of your story. AI will customize this based on your journey and values. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"missionTitle\": \"Our Mission\",\r\n \"missionDesc\": \"AI will customize this mission description based on your goals. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"valuesTitle\": \"Our Values\",\r\n \"valuesDesc\": \"This values description will be replaced by AI with your actual values. Duis aute irure dolor in reprehenderit.\",\r\n \"teamTitle\": \"Our Team\",\r\n \"teamDesc\": \"Placeholder team description. AI will customize this based on your team structure and culture.\",\r\n \"qualityTitle\": \"Quality First\",\r\n \"qualityDesc\": \"AI will replace this quality description with relevant information about your quality standards.\",\r\n \"customersValue\": \"500+\",\r\n \"customersLabel\": \"Happy Customers\",\r\n \"projectsValue\": \"1000+\",\r\n \"projectsLabel\": \"Projects Completed\",\r\n \"experienceValue\": \"10+\",\r\n \"experienceLabel\": \"Years Experience\",\r\n \"satisfactionValue\": \"99%\",\r\n \"satisfactionLabel\": \"Client Satisfaction\",\r\n \"ctaTitle\": \"Ready to Work Together?\",\r\n \"ctaDesc\": \"AI will customize this CTA description based on your site goals. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"\r\n}\r\n"
30
+ },
31
+ {
32
+ "path": "about-page/lang/tr.json",
33
+ "type": "registry:lang",
34
+ "target": "$modules$/about-page/lang/tr.json",
35
+ "content": "{\r\n \"title\": \"Hakkımızda\",\r\n \"subtitle\": \"AI bu hakkımızda sayfası alt başlığını sitenize göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyTitle\": \"Hikayemiz\",\r\n \"storyP1\": \"Bu hikayeniz için placeholder metindir. AI bunu gerçek geçmişiniz ve yolculuğunuzla değiştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"storyP2\": \"Hikayenin ikinci paragrafı için placeholder metin. AI bunu yolculuğunuz ve değerlerinize göre özelleştirecektir. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"missionTitle\": \"Misyonumuz\",\r\n \"missionDesc\": \"AI bu misyon açıklamasını hedeflerinize göre özelleştirecektir. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"valuesTitle\": \"Değerlerimiz\",\r\n \"valuesDesc\": \"Bu değerler açıklaması AI tarafından gerçek değerlerinizle değiştirilecektir. Duis aute irure dolor in reprehenderit.\",\r\n \"teamTitle\": \"Ekibimiz\",\r\n \"teamDesc\": \"Placeholder ekip açıklaması. AI bunu ekip yapınıza ve kültürünüze göre özelleştirecektir.\",\r\n \"qualityTitle\": \"Önce Kalite\",\r\n \"qualityDesc\": \"AI bu kalite açıklamasını kalite standartlarınızla ilgili bilgilerle değiştirecektir.\",\r\n \"customersValue\": \"500+\",\r\n \"customersLabel\": \"Mutlu Müşteri\",\r\n \"projectsValue\": \"1000+\",\r\n \"projectsLabel\": \"Tamamlanan Proje\",\r\n \"experienceValue\": \"10+\",\r\n \"experienceLabel\": \"Yıllık Deneyim\",\r\n \"satisfactionValue\": \"%99\",\r\n \"satisfactionLabel\": \"Müşteri Memnuniyeti\",\r\n \"ctaTitle\": \"Birlikte Çalışmaya Hazır mısınız?\",\r\n \"ctaDesc\": \"AI bu CTA açıklamasını site hedeflerinize göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\"\r\n}\r\n"
36
+ }
37
+ ],
38
+ "exports": {
39
+ "types": [],
40
+ "variables": [
41
+ "AboutPage",
42
+ "default"
43
+ ]
44
+ }
45
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "about-section",
3
+ "type": "registry:component",
4
+ "title": "About Section",
5
+ "description": "Comprehensive about section with image grid, mission card, company logos, and stats grid. Features two-column header, main image with side column (mission card + secondary image), company names ticker, and 4-column achievement stats. Perfect for landing pages and about pages.",
6
+ "registryDependencies": [],
7
+ "usage": "import { AboutSection } from '@/modules/about-section';\n\n<AboutSection />\n\n- Image grid with mission card\n- Company logos section\n- 4-column stats grid\n- Fully translatable via lang files",
8
+ "files": [
9
+ {
10
+ "path": "about-section/index.ts",
11
+ "type": "registry:index",
12
+ "target": "$modules$/about-section/index.ts",
13
+ "content": "export * from './about-section';\r\n"
14
+ },
15
+ {
16
+ "path": "about-section/about-section.tsx",
17
+ "type": "registry:component",
18
+ "target": "$modules$/about-section/about-section.tsx",
19
+ "content": "import { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface AboutSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function AboutSection({ className }: AboutSectionProps) {\r\n const { t } = useTranslation(\"about-section\");\r\n\r\n const stats = [\r\n { value: t(\"stat1Value\", \"500+\"), label: t(\"stat1Label\", \"Happy Clients\") },\r\n { value: t(\"stat2Value\", \"1000+\"), label: t(\"stat2Label\", \"Projects Completed\") },\r\n { value: t(\"stat3Value\", \"99%\"), label: t(\"stat3Label\", \"Satisfaction Rate\") },\r\n { value: t(\"stat4Value\", \"15+\"), label: t(\"stat4Label\", \"Years Experience\") },\r\n ];\r\n\r\n const companies = [\"Google\", \"Microsoft\", \"Amazon\", \"Apple\", \"Meta\"];\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 {/* Header */}\r\n <div className=\"grid md:grid-cols-2 gap-8 mb-12\">\r\n <h2 className=\"text-3xl md:text-4xl lg:text-5xl font-bold\">\r\n {t(\"title\", \"About Our Company\")}\r\n </h2>\r\n <p className=\"text-muted-foreground text-lg\">\r\n {t(\"description\", \"We are a passionate team dedicated to creating innovative solutions that empower businesses to thrive in the digital age. Our mission is to deliver excellence in everything we do.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Images Grid */}\r\n <div className=\"grid lg:grid-cols-3 gap-6 mb-16\">\r\n {/* Main Image */}\r\n <div className=\"lg:col-span-2\">\r\n <div className=\"aspect-[16/10] rounded-2xl bg-muted overflow-hidden\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"mainImageAlt\", \"Our team\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n\r\n {/* Side Column */}\r\n <div className=\"flex flex-col gap-6\">\r\n {/* Info Card */}\r\n <div className=\"bg-muted rounded-2xl p-6 flex flex-col justify-between flex-1\">\r\n <div>\r\n <h3 className=\"text-lg font-semibold mb-2\">\r\n {t(\"cardTitle\", \"Our Mission\")}\r\n </h3>\r\n <p className=\"text-muted-foreground text-sm mb-4\">\r\n {t(\"cardDescription\", \"Providing businesses with effective tools to improve workflows, boost efficiency, and encourage sustainable growth.\")}\r\n </p>\r\n </div>\r\n <Button variant=\"outline\" asChild className=\"w-fit\">\r\n <Link to=\"/about\">{t(\"cardButton\", \"Learn More\")}</Link>\r\n </Button>\r\n </div>\r\n\r\n {/* Secondary Image */}\r\n <div className=\"aspect-square rounded-2xl bg-muted overflow-hidden flex-1\">\r\n <img\r\n src=\"/images/placeholder.png\"\r\n alt={t(\"secondaryImageAlt\", \"Our office\")}\r\n className=\"w-full h-full object-cover\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Companies */}\r\n <div className=\"text-center mb-16\">\r\n <p className=\"text-muted-foreground mb-6\">\r\n {t(\"companiesTitle\", \"Trusted by leading companies worldwide\")}\r\n </p>\r\n <div className=\"flex flex-wrap justify-center items-center gap-8 md:gap-12\">\r\n {companies.map((company) => (\r\n <span\r\n key={company}\r\n className=\"text-xl md:text-2xl font-semibold text-muted-foreground/50\"\r\n >\r\n {company}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Stats */}\r\n <div className=\"bg-muted rounded-2xl p-8 md:p-12\">\r\n <div className=\"text-center md:text-left mb-8\">\r\n <h3 className=\"text-2xl md:text-3xl font-bold mb-2\">\r\n {t(\"statsTitle\", \"Our Achievements\")}\r\n </h3>\r\n <p className=\"text-muted-foreground max-w-xl\">\r\n {t(\"statsDescription\", \"Numbers that reflect our commitment to excellence and client satisfaction.\")}\r\n </p>\r\n </div>\r\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-8\">\r\n {stats.map((stat, index) => (\r\n <div key={index} className=\"text-center\">\r\n <div className=\"text-3xl md:text-4xl lg:text-5xl font-bold text-primary mb-2\">\r\n {stat.value}\r\n </div>\r\n <p className=\"text-sm md:text-base text-muted-foreground\">\r\n {stat.label}\r\n </p>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
20
+ },
21
+ {
22
+ "path": "about-section/lang/en.json",
23
+ "type": "registry:lang",
24
+ "target": "$modules$/about-section/lang/en.json",
25
+ "content": "{\r\n \"title\": \"About Us\",\r\n \"description\": \"AI will customize this description based on your site and audience. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"mainImageAlt\": \"Our team\",\r\n \"secondaryImageAlt\": \"Our office\",\r\n \"cardTitle\": \"Our Mission\",\r\n \"cardDescription\": \"This mission description will be customized by AI based on your goals and values. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"cardButton\": \"Learn More\",\r\n \"companiesTitle\": \"AI will customize this companies title\",\r\n \"statsTitle\": \"Our Achievements\",\r\n \"statsDescription\": \"This stats description will be replaced by AI with relevant information about your milestones.\",\r\n \"stat1Value\": \"500+\",\r\n \"stat1Label\": \"Happy Clients\",\r\n \"stat2Value\": \"1000+\",\r\n \"stat2Label\": \"Projects Completed\",\r\n \"stat3Value\": \"99%\",\r\n \"stat3Label\": \"Satisfaction Rate\",\r\n \"stat4Value\": \"15+\",\r\n \"stat4Label\": \"Years Experience\"\r\n}\r\n"
26
+ },
27
+ {
28
+ "path": "about-section/lang/tr.json",
29
+ "type": "registry:lang",
30
+ "target": "$modules$/about-section/lang/tr.json",
31
+ "content": "{\r\n \"title\": \"Hakkımızda\",\r\n \"description\": \"AI bu açıklamayı sitenize ve hedef kitlenize göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"mainImageAlt\": \"Ekibimiz\",\r\n \"secondaryImageAlt\": \"Ofisimiz\",\r\n \"cardTitle\": \"Misyonumuz\",\r\n \"cardDescription\": \"Bu misyon açıklaması AI tarafından hedeflerinize ve değerlerinize göre özelleştirilecektir. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"cardButton\": \"Daha Fazla\",\r\n \"companiesTitle\": \"AI bu referanslar başlığını özelleştirecektir\",\r\n \"statsTitle\": \"Başarılarımız\",\r\n \"statsDescription\": \"Bu istatistik açıklaması AI tarafından önemli kilometre taşlarınızla ilgili bilgilerle değiştirilecektir.\",\r\n \"stat1Value\": \"500+\",\r\n \"stat1Label\": \"Mutlu Müşteri\",\r\n \"stat2Value\": \"1000+\",\r\n \"stat2Label\": \"Tamamlanan Proje\",\r\n \"stat3Value\": \"%99\",\r\n \"stat3Label\": \"Memnuniyet Oranı\",\r\n \"stat4Value\": \"15+\",\r\n \"stat4Label\": \"Yıllık Deneyim\"\r\n}\r\n"
32
+ }
33
+ ],
34
+ "exports": {
35
+ "types": [],
36
+ "variables": [
37
+ "AboutSection"
38
+ ]
39
+ }
40
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "animations",
3
+ "type": "registry:lib",
4
+ "title": "Animations",
5
+ "description": "Motion animation utilities and wrapper components including FadeIn, SlideIn, ScaleUp, StaggerContainer. Built with motion/react for smooth, performant animations.",
6
+ "dependencies": [
7
+ "motion"
8
+ ],
9
+ "registryDependencies": [],
10
+ "usage": "import { FadeIn, StaggerContainer, StaggerItem, ScaleUp } from '@/modules/animations';\n\n// Simple fade in on scroll\n<FadeIn>\n <Card>Content</Card>\n</FadeIn>\n\n// Staggered grid animation\n<StaggerContainer className=\"grid grid-cols-3 gap-4\">\n {items.map(item => (\n <StaggerItem key={item.id}>\n <Card>{item.content}</Card>\n </StaggerItem>\n ))}\n</StaggerContainer>\n\n// With delay\n<FadeIn delay={0.2}>\n <Section>...</Section>\n</FadeIn>",
11
+ "files": [
12
+ {
13
+ "path": "animations/index.ts",
14
+ "type": "registry:index",
15
+ "target": "$modules$/animations/index.ts",
16
+ "content": "export * from \"./animations\";\r\nexport * from \"./components\";\r\n"
17
+ },
18
+ {
19
+ "path": "animations/animations.ts",
20
+ "type": "registry:lib",
21
+ "target": "$modules$/animations/animations.ts",
22
+ "content": "import type { Variants, Transition } from \"motion/react\";\r\n\r\n/**\r\n * Fade in from bottom animation\r\n * Use for: Hero sections, cards on scroll, page content\r\n */\r\nexport const fadeInUp: Variants = {\r\n initial: { opacity: 0, y: 20 },\r\n animate: { opacity: 1, y: 0 },\r\n exit: { opacity: 0, y: -10 },\r\n};\r\n\r\n/**\r\n * Fade in from top animation\r\n * Use for: Dropdowns, modals, notifications\r\n */\r\nexport const fadeInDown: Variants = {\r\n initial: { opacity: 0, y: -20 },\r\n animate: { opacity: 1, y: 0 },\r\n exit: { opacity: 0, y: -10 },\r\n};\r\n\r\n/**\r\n * Slide in from left animation\r\n * Use for: Sidebars, navigation, list items\r\n */\r\nexport const slideInLeft: Variants = {\r\n initial: { opacity: 0, x: -20 },\r\n animate: { opacity: 1, x: 0 },\r\n exit: { opacity: 0, x: -20 },\r\n};\r\n\r\n/**\r\n * Slide in from right animation\r\n * Use for: Panels, drawers, cart sidebars\r\n */\r\nexport const slideInRight: Variants = {\r\n initial: { opacity: 0, x: 20 },\r\n animate: { opacity: 1, x: 0 },\r\n exit: { opacity: 0, x: 20 },\r\n};\r\n\r\n/**\r\n * Scale up animation\r\n * Use for: Modals, popups, dialogs\r\n */\r\nexport const scaleUp: Variants = {\r\n initial: { opacity: 0, scale: 0.95 },\r\n animate: { opacity: 1, scale: 1 },\r\n exit: { opacity: 0, scale: 0.95 },\r\n};\r\n\r\n/**\r\n * Simple fade animation\r\n * Use for: Overlays, backgrounds, subtle transitions\r\n */\r\nexport const fade: Variants = {\r\n initial: { opacity: 0 },\r\n animate: { opacity: 1 },\r\n exit: { opacity: 0 },\r\n};\r\n\r\n/**\r\n * Stagger container for child animations\r\n * Use with children that have their own variants\r\n *\r\n * @example\r\n * <motion.div variants={staggerContainer} initial=\"initial\" animate=\"animate\">\r\n * {items.map(item => (\r\n * <motion.div key={item.id} variants={fadeInUp}>\r\n * {item.content}\r\n * </motion.div>\r\n * ))}\r\n * </motion.div>\r\n */\r\nexport const staggerContainer: Variants = {\r\n initial: {},\r\n animate: {\r\n transition: {\r\n staggerChildren: 0.1,\r\n delayChildren: 0.1,\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * Fast stagger for many items\r\n */\r\nexport const staggerContainerFast: Variants = {\r\n initial: {},\r\n animate: {\r\n transition: {\r\n staggerChildren: 0.05,\r\n delayChildren: 0.05,\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * Slow stagger for fewer, larger items\r\n */\r\nexport const staggerContainerSlow: Variants = {\r\n initial: {},\r\n animate: {\r\n transition: {\r\n staggerChildren: 0.15,\r\n delayChildren: 0.2,\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * Default spring transition\r\n * Use for: Most animations\r\n */\r\nexport const springTransition: Transition = {\r\n type: \"spring\",\r\n stiffness: 300,\r\n damping: 30,\r\n};\r\n\r\n/**\r\n * Smooth ease transition\r\n * Use for: Subtle, elegant animations\r\n */\r\nexport const easeTransition: Transition = {\r\n type: \"tween\",\r\n ease: \"easeOut\",\r\n duration: 0.3,\r\n};\r\n\r\n/**\r\n * Quick transition\r\n * Use for: Hover effects, quick feedback\r\n */\r\nexport const quickTransition: Transition = {\r\n type: \"tween\",\r\n ease: \"easeOut\",\r\n duration: 0.15,\r\n};\r\n\r\n/**\r\n * Hover scale effect props\r\n * Use with motion components for card hover effects\r\n *\r\n * @example\r\n * <motion.div {...hoverScale}>\r\n * <Card>...</Card>\r\n * </motion.div>\r\n */\r\nexport const hoverScale = {\r\n whileHover: { scale: 1.02 },\r\n whileTap: { scale: 0.98 },\r\n transition: springTransition,\r\n};\r\n\r\n/**\r\n * Subtle hover scale for smaller elements\r\n */\r\nexport const hoverScaleSubtle = {\r\n whileHover: { scale: 1.01 },\r\n whileTap: { scale: 0.99 },\r\n transition: quickTransition,\r\n};\r\n\r\n/**\r\n * Hover lift effect (scale + shadow simulation via y)\r\n */\r\nexport const hoverLift = {\r\n whileHover: { scale: 1.02, y: -4 },\r\n whileTap: { scale: 0.98, y: 0 },\r\n transition: springTransition,\r\n};\r\n\r\n/**\r\n * Button press effect\r\n */\r\nexport const buttonPress = {\r\n whileHover: { scale: 1.02 },\r\n whileTap: { scale: 0.95 },\r\n transition: quickTransition,\r\n};\r\n\r\n/**\r\n * Icon hover rotation\r\n */\r\nexport const iconHoverRotate = {\r\n whileHover: { rotate: 15 },\r\n transition: springTransition,\r\n};\r\n\r\n/**\r\n * Accordion/collapse animation variants\r\n * Use with AnimatePresence for expand/collapse\r\n *\r\n * @example\r\n * <AnimatePresence>\r\n * {isOpen && (\r\n * <motion.div\r\n * variants={collapseVariants}\r\n * initial=\"collapsed\"\r\n * animate=\"expanded\"\r\n * exit=\"collapsed\"\r\n * >\r\n * Content\r\n * </motion.div>\r\n * )}\r\n * </AnimatePresence>\r\n */\r\nexport const collapseVariants: Variants = {\r\n collapsed: {\r\n height: 0,\r\n opacity: 0,\r\n overflow: \"hidden\",\r\n },\r\n expanded: {\r\n height: \"auto\",\r\n opacity: 1,\r\n overflow: \"visible\",\r\n transition: {\r\n height: { duration: 0.3, ease: \"easeOut\" },\r\n opacity: { duration: 0.2, delay: 0.1 },\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * Page transition variants\r\n * Use for route transitions with AnimatePresence\r\n */\r\nexport const pageTransition: Variants = {\r\n initial: { opacity: 0, y: 10 },\r\n animate: {\r\n opacity: 1,\r\n y: 0,\r\n transition: {\r\n duration: 0.3,\r\n ease: \"easeOut\",\r\n },\r\n },\r\n exit: {\r\n opacity: 0,\r\n y: -10,\r\n transition: {\r\n duration: 0.2,\r\n ease: \"easeIn\",\r\n },\r\n },\r\n};\r\n\r\n/**\r\n * Carousel slide variants\r\n * Direction should be passed as custom prop\r\n *\r\n * @example\r\n * <motion.div\r\n * custom={direction}\r\n * variants={carouselSlide}\r\n * initial=\"enter\"\r\n * animate=\"center\"\r\n * exit=\"exit\"\r\n * >\r\n */\r\nexport const carouselSlide: Variants = {\r\n enter: (direction: number) => ({\r\n x: direction > 0 ? 300 : -300,\r\n opacity: 0,\r\n }),\r\n center: {\r\n x: 0,\r\n opacity: 1,\r\n transition: {\r\n x: { type: \"spring\", stiffness: 300, damping: 30 },\r\n opacity: { duration: 0.2 },\r\n },\r\n },\r\n exit: (direction: number) => ({\r\n x: direction < 0 ? 300 : -300,\r\n opacity: 0,\r\n transition: {\r\n x: { type: \"spring\", stiffness: 300, damping: 30 },\r\n opacity: { duration: 0.2 },\r\n },\r\n }),\r\n};\r\n\r\n/**\r\n * Notification/toast animation\r\n */\r\nexport const notificationVariants: Variants = {\r\n initial: { opacity: 0, y: -20, scale: 0.95 },\r\n animate: {\r\n opacity: 1,\r\n y: 0,\r\n scale: 1,\r\n transition: springTransition,\r\n },\r\n exit: {\r\n opacity: 0,\r\n y: -20,\r\n scale: 0.95,\r\n transition: quickTransition,\r\n },\r\n};\r\n\r\n/**\r\n * List item animation for staggered lists\r\n */\r\nexport const listItem: Variants = {\r\n initial: { opacity: 0, x: -10 },\r\n animate: { opacity: 1, x: 0 },\r\n exit: { opacity: 0, x: 10 },\r\n};\r\n\r\n/**\r\n * Grid item animation for staggered grids\r\n */\r\nexport const gridItem: Variants = {\r\n initial: { opacity: 0, scale: 0.9 },\r\n animate: { opacity: 1, scale: 1 },\r\n exit: { opacity: 0, scale: 0.9 },\r\n};\r\n"
23
+ },
24
+ {
25
+ "path": "animations/components.tsx",
26
+ "type": "registry:component",
27
+ "target": "$modules$/animations/components.tsx",
28
+ "content": "import { motion, type HTMLMotionProps } from \"motion/react\";\nimport {\n fadeInUp,\n fadeInDown,\n slideInLeft,\n slideInRight,\n scaleUp,\n fade,\n staggerContainer,\n staggerContainerFast,\n gridItem,\n listItem,\n easeTransition,\n} from \"./animations\";\n\ninterface AnimationProps extends Omit<HTMLMotionProps<\"div\">, \"variants\"> {\n children: React.ReactNode;\n delay?: number;\n once?: boolean;\n amount?: number;\n className?: string;\n}\n\n/**\n * Fade in from bottom animation\n * Use for: Hero sections, cards, page content\n */\nexport function FadeIn({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={fadeInUp}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Fade in from top animation\n * Use for: Dropdowns, modals, notifications\n */\nexport function FadeInDown({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={fadeInDown}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Slide in from left animation\n * Use for: Sidebars, navigation, list items\n */\nexport function SlideInLeft({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={slideInLeft}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Slide in from right animation\n * Use for: Panels, drawers, cart sidebars\n */\nexport function SlideInRight({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={slideInRight}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Scale up animation\n * Use for: Modals, popups, dialogs, highlight sections\n */\nexport function ScaleUp({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={scaleUp}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Simple fade animation\n * Use for: Overlays, backgrounds, subtle transitions\n */\nexport function Fade({\n children,\n delay = 0,\n once = true,\n amount = 0.3,\n className,\n ...props\n}: AnimationProps) {\n return (\n <motion.div\n variants={fade}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n transition={{ ...easeTransition, delay }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\ninterface StaggerContainerProps extends Omit<AnimationProps, \"delay\"> {\n fast?: boolean;\n}\n\n/**\n * Stagger container for animating children sequentially\n * Use with StaggerItem as children\n */\nexport function StaggerContainer({\n children,\n fast = false,\n once = true,\n amount = 0.2,\n className,\n ...props\n}: StaggerContainerProps) {\n return (\n <motion.div\n variants={fast ? staggerContainerFast : staggerContainer}\n initial=\"initial\"\n whileInView=\"animate\"\n viewport={{ once, amount }}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\ninterface StaggerItemProps\n extends Omit<HTMLMotionProps<\"div\">, \"variants\"> {\n children: React.ReactNode;\n className?: string;\n}\n\n/**\n * Stagger item - child of StaggerContainer\n * Animates with fadeInUp when parent triggers\n */\nexport function StaggerItem({\n children,\n className,\n ...props\n}: StaggerItemProps) {\n return (\n <motion.div\n variants={fadeInUp}\n transition={easeTransition}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Grid item for staggered grids\n * Animates with scale effect\n */\nexport function GridItem({\n children,\n className,\n ...props\n}: StaggerItemProps) {\n return (\n <motion.div\n variants={gridItem}\n transition={easeTransition}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * List item for staggered lists\n * Animates with horizontal slide\n */\nexport function ListItem({\n children,\n className,\n ...props\n}: StaggerItemProps) {\n return (\n <motion.div\n variants={listItem}\n transition={easeTransition}\n className={className}\n {...props}\n >\n {children}\n </motion.div>\n );\n}\n"
29
+ }
30
+ ],
31
+ "exports": {
32
+ "types": [],
33
+ "variables": [
34
+ "Fade",
35
+ "FadeIn",
36
+ "FadeInDown",
37
+ "GridItem",
38
+ "ListItem",
39
+ "ScaleUp",
40
+ "SlideInLeft",
41
+ "SlideInRight",
42
+ "StaggerContainer",
43
+ "StaggerItem",
44
+ "buttonPress",
45
+ "carouselSlide",
46
+ "collapseVariants",
47
+ "easeTransition",
48
+ "fade",
49
+ "fadeInDown",
50
+ "fadeInUp",
51
+ "gridItem",
52
+ "hoverLift",
53
+ "hoverScale",
54
+ "hoverScaleSubtle",
55
+ "iconHoverRotate",
56
+ "listItem",
57
+ "notificationVariants",
58
+ "pageTransition",
59
+ "quickTransition",
60
+ "scaleUp",
61
+ "slideInLeft",
62
+ "slideInRight",
63
+ "springTransition",
64
+ "staggerContainer",
65
+ "staggerContainerFast",
66
+ "staggerContainerSlow"
67
+ ]
68
+ }
69
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "bento-grid-section",
3
+ "type": "registry:component",
4
+ "title": "Bento Grid Section",
5
+ "description": "A modern bento-style grid layout for showcasing features or content. Supports custom headers, icons, and descriptions with hover animations. Responsive design with 3-column layout on desktop.",
6
+ "registryDependencies": [],
7
+ "usage": "import { BentoGridSection } from '@/modules/bento-grid-section';\n\n<BentoGridSection\n title=\"Features\"\n items={[\n {\n title: \"Fast Performance\",\n description: \"Lightning fast load times\",\n header: <div className=\"h-full w-full bg-gradient-to-br from-violet-500 to-purple-500\" />,\n icon: <IconBolt className=\"h-4 w-4\" />,\n className: \"md:col-span-2\"\n },\n // more items...\n ]}\n/>",
8
+ "files": [
9
+ {
10
+ "path": "bento-grid-section/index.ts",
11
+ "type": "registry:index",
12
+ "target": "$modules$/bento-grid-section/index.ts",
13
+ "content": "export * from './bento-grid-section';\r\n"
14
+ },
15
+ {
16
+ "path": "bento-grid-section/bento-grid-section.tsx",
17
+ "type": "registry:component",
18
+ "target": "$modules$/bento-grid-section/bento-grid-section.tsx",
19
+ "content": "import type { ReactNode } from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n// Bento Grid Components\r\nexport function BentoGrid({\r\n className,\r\n children,\r\n}: {\r\n className?: string;\r\n children?: ReactNode;\r\n}) {\r\n return (\r\n <div\r\n className={cn(\r\n \"grid md:auto-rows-[18rem] grid-cols-1 md:grid-cols-3 gap-4 max-w-7xl mx-auto\",\r\n className\r\n )}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\nexport function BentoGridItem({\r\n className,\r\n title,\r\n description,\r\n header,\r\n icon,\r\n}: {\r\n className?: string;\r\n title?: string | ReactNode;\r\n description?: string | ReactNode;\r\n header?: ReactNode;\r\n icon?: ReactNode;\r\n}) {\r\n return (\r\n <div\r\n className={cn(\r\n \"row-span-1 rounded-xl group/bento hover:shadow-xl transition duration-200 shadow-input dark:shadow-none p-4 dark:bg-black dark:border-white/[0.2] bg-white border border-transparent justify-between flex flex-col space-y-4\",\r\n className\r\n )}\r\n >\r\n {header}\r\n <div className=\"group-hover/bento:translate-x-2 transition duration-200\">\r\n {icon}\r\n <div className=\"font-sans font-bold text-neutral-600 dark:text-neutral-200 mb-2 mt-2\">\r\n {title}\r\n </div>\r\n <div className=\"font-sans font-normal text-neutral-600 text-xs dark:text-neutral-300\">\r\n {description}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\n// Section Component\r\ninterface BentoGridSectionProps {\r\n title?: string;\r\n items: Array<{\r\n title: string;\r\n description: string;\r\n header?: ReactNode;\r\n icon?: ReactNode;\r\n className?: string;\r\n }>;\r\n className?: string;\r\n}\r\n\r\nexport function BentoGridSection({\r\n title,\r\n items,\r\n className,\r\n}: BentoGridSectionProps) {\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 {title && (\r\n <h2 className=\"text-3xl md:text-4xl font-bold text-center mb-12\">\r\n {title}\r\n </h2>\r\n )}\r\n <BentoGrid>\r\n {items.map((item, i) => (\r\n <BentoGridItem\r\n key={i}\r\n title={item.title}\r\n description={item.description}\r\n header={item.header}\r\n icon={item.icon}\r\n className={item.className}\r\n />\r\n ))}\r\n </BentoGrid>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
20
+ },
21
+ {
22
+ "path": "bento-grid-section/lang/en.json",
23
+ "type": "registry:lang",
24
+ "target": "$modules$/bento-grid-section/lang/en.json",
25
+ "content": "{\r\n \"title\": \"Features\"\r\n}\r\n"
26
+ },
27
+ {
28
+ "path": "bento-grid-section/lang/tr.json",
29
+ "type": "registry:lang",
30
+ "target": "$modules$/bento-grid-section/lang/tr.json",
31
+ "content": "{\r\n \"title\": \"Özellikler\"\r\n}\r\n"
32
+ }
33
+ ],
34
+ "exports": {
35
+ "types": [],
36
+ "variables": [
37
+ "BentoGrid",
38
+ "BentoGridItem",
39
+ "BentoGridSection"
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "blog-core",
3
+ "type": "registry:module",
4
+ "title": "Blog Core",
5
+ "description": "Complete blog state management with Zustand. Includes useBlogStore for saved/favorite posts functionality, usePosts hook for fetching posts with category filtering, search, and pagination. TypeScript types for Post, Category, and Author. No provider wrapping needed.",
6
+ "dependencies": [
7
+ "zustand"
8
+ ],
9
+ "registryDependencies": [],
10
+ "usage": "import { useBlog, usePosts, usePostBySlug } from '@/modules/blog-core';\n\n// No provider needed - just use the hooks:\nconst { favorites, addToFavorites, isFavorite } = useBlog();\nconst { posts, loading } = usePosts();\n\n// Or use store directly with selectors:\nconst favorites = useBlogStore((s) => s.favorites);",
11
+ "files": [
12
+ {
13
+ "path": "blog-core/index.ts",
14
+ "type": "registry:index",
15
+ "target": "$modules$/blog-core/index.ts",
16
+ "content": "// Types\r\nexport * from './types';\r\n\r\n// Store (Zustand)\r\nexport { useBlogStore, useBlog } from './stores/blog-store';\r\n\r\n// Hooks\r\nexport {\r\n usePosts,\r\n usePostBySlug,\r\n useFeaturedPosts,\r\n useRecentPosts,\r\n usePopularPosts,\r\n usePostsByCategory,\r\n usePostsByTag,\r\n usePostSearch,\r\n useBlogCategories,\r\n usePostStats\r\n} from './usePosts';\r\n"
17
+ },
18
+ {
19
+ "path": "blog-core/types.ts",
20
+ "type": "registry:type",
21
+ "target": "$modules$/blog-core/types.ts",
22
+ "content": "export interface PostCategory {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n is_primary: boolean;\r\n}\r\n\r\nexport interface Post {\r\n id: number;\r\n title: string;\r\n slug: string;\r\n content: string;\r\n excerpt: string;\r\n featured_image: string;\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: PostCategory[]; // Multi-category support\r\n author: string;\r\n author_avatar: string;\r\n published_at: string;\r\n updated_at: string;\r\n tags: string[];\r\n read_time: number;\r\n view_count: number;\r\n featured: boolean;\r\n published: boolean;\r\n meta_description: string;\r\n meta_keywords: string;\r\n}\r\n\r\nexport interface Author {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n bio?: string;\r\n avatar?: string;\r\n social?: {\r\n twitter?: string;\r\n linkedin?: string;\r\n github?: string;\r\n website?: string;\r\n };\r\n}\r\n\r\nexport interface Comment {\r\n id: number;\r\n post_id: number;\r\n author_name: string;\r\n author_email: string;\r\n content: string;\r\n created_at: string;\r\n approved: boolean;\r\n}\r\n\r\nexport interface BlogCategory {\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 BlogContextType {\r\n favorites: Post[];\r\n addToFavorites: (post: Post) => void;\r\n removeFromFavorites: (postId: string) => void;\r\n isFavorite: (postId: string) => boolean;\r\n clearFavorites: () => void;\r\n}\r\n\r\nexport interface BlogSettings {\r\n site: {\r\n name: string;\r\n description: string;\r\n url: string;\r\n logo?: string;\r\n };\r\n author: {\r\n name: string;\r\n bio?: string;\r\n avatar?: string;\r\n };\r\n social: {\r\n twitter?: string;\r\n linkedin?: string;\r\n github?: string;\r\n facebook?: string;\r\n };\r\n comments: {\r\n enabled: boolean;\r\n moderation: boolean;\r\n };\r\n newsletter: {\r\n enabled: boolean;\r\n provider?: string;\r\n };\r\n}\r\n"
23
+ },
24
+ {
25
+ "path": "blog-core/stores/blog-store.ts",
26
+ "type": "registry:store",
27
+ "target": "$modules$/blog-core/stores/blog-store.ts",
28
+ "content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Post, BlogContextType } from \"../types\";\r\n\r\ninterface BlogStore {\r\n favorites: Post[];\r\n addToFavorites: (post: Post) => void;\r\n removeFromFavorites: (postId: string) => void;\r\n isFavorite: (postId: string) => boolean;\r\n clearFavorites: () => void;\r\n}\r\n\r\nexport const useBlogStore = create<BlogStore>()(\r\n persist(\r\n (set, get) => ({\r\n favorites: [],\r\n\r\n addToFavorites: (post) =>\r\n set((state) => {\r\n if (state.favorites.some((p) => p.id === post.id)) {\r\n return state;\r\n }\r\n return { favorites: [...state.favorites, post] };\r\n }),\r\n\r\n removeFromFavorites: (postId) =>\r\n set((state) => ({\r\n favorites: state.favorites.filter((p) => String(p.id) !== postId),\r\n })),\r\n\r\n isFavorite: (postId) => {\r\n return get().favorites.some((p) => String(p.id) === postId);\r\n },\r\n\r\n clearFavorites: () => set({ favorites: [] }),\r\n }),\r\n { name: \"blog_favorites\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches BlogContextType\r\nexport const useBlog = (): BlogContextType => {\r\n const store = useBlogStore();\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 clearFavorites: store.clearFavorites,\r\n };\r\n};\r\n"
29
+ },
30
+ {
31
+ "path": "blog-core/usePosts.ts",
32
+ "type": "registry:hook",
33
+ "target": "$modules$/blog-core/usePosts.ts",
34
+ "content": "import { useMemo } from 'react';\nimport type { Post, BlogCategory, PostCategory } from './types';\nimport {\n useRepositoryQuery,\n useRawQuery,\n useRawQueryOne,\n parseStringToArray,\n parseSQLiteBoolean,\n parseNumberSafe\n} from '@/modules/db';\n\nconst transformPost = (row: any): Post => {\n const categoryNames = row.category_names ? row.category_names.split(',') : [];\n const categorySlugs = row.category_slugs ? row.category_slugs.split(',') : [];\n const categoryIds = row.category_ids ? row.category_ids.split(',').map(Number) : [];\n\n const categories: PostCategory[] = categoryIds.map((id: number, index: number) => ({\n id,\n name: categoryNames[index] || '',\n slug: categorySlugs[index] || '',\n is_primary: index === 0\n }));\n\n const primaryCategory = categories.length > 0 ? categories[0] : null;\n\n return {\n id: parseNumberSafe(row.id),\n title: String(row.title || ''),\n slug: String(row.slug || ''),\n excerpt: row.excerpt || '',\n content: row.content || '',\n author: row.author || '',\n author_avatar: row.author_avatar || '',\n published_at: row.published_at || new Date().toISOString(),\n updated_at: row.updated_at || new Date().toISOString(),\n featured_image: row.featured_image || '',\n images: parseStringToArray(row.images),\n tags: parseStringToArray(row.tags),\n read_time: parseNumberSafe(row.read_time),\n view_count: parseNumberSafe(row.view_count),\n featured: parseSQLiteBoolean(row.featured),\n published: parseSQLiteBoolean(row.published),\n meta_description: row.meta_description || '',\n meta_keywords: row.meta_keywords || '',\n category: primaryCategory?.slug || '',\n category_name: primaryCategory?.name || '',\n categories\n };\n};\n\nconst POSTS_WITH_CATEGORIES_SQL = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM posts p\n LEFT JOIN post_categories pc ON p.id = pc.post_id\n LEFT JOIN blog_categories c ON pc.category_id = c.id\n WHERE p.published = 1\n GROUP BY p.id\n`;\n\nexport function usePosts() {\n const sql = `${POSTS_WITH_CATEGORIES_SQL} ORDER BY p.published_at DESC`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'all'],\n sql\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function useFeaturedPosts() {\n const sql = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM posts p\n LEFT JOIN post_categories pc ON p.id = pc.post_id\n LEFT JOIN blog_categories c ON pc.category_id = c.id\n WHERE p.published = 1 AND p.featured = 1\n GROUP BY p.id\n ORDER BY p.published_at DESC\n `;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'featured'],\n sql\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function useRecentPosts(limit: number = 10) {\n const sql = `${POSTS_WITH_CATEGORIES_SQL} ORDER BY p.published_at DESC LIMIT ?`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'recent', limit],\n sql,\n [limit]\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function usePopularPosts(limit: number = 10) {\n const sql = `${POSTS_WITH_CATEGORIES_SQL} ORDER BY p.view_count DESC LIMIT ?`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'popular', limit],\n sql,\n [limit]\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function usePostsByCategory(categorySlug: string) {\n const sql = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM posts p\n LEFT JOIN post_categories pc ON p.id = pc.post_id\n LEFT JOIN blog_categories c ON pc.category_id = c.id\n WHERE p.published = 1 AND p.id IN (\n SELECT pc2.post_id FROM post_categories pc2\n JOIN blog_categories c2 ON pc2.category_id = c2.id\n WHERE c2.slug = ?\n )\n GROUP BY p.id\n ORDER BY p.published_at DESC\n `;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'category', categorySlug],\n sql,\n [categorySlug],\n { enabled: !!categorySlug }\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function usePostsByTag(tag: string) {\n const sql = `${POSTS_WITH_CATEGORIES_SQL} AND (p.tags LIKE ? OR p.tags LIKE ? OR p.tags LIKE ? OR p.tags = ?) ORDER BY p.published_at DESC`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'tag', tag],\n sql,\n [`%\"${tag}\"%`, `%,${tag},%`, `${tag},%`, tag],\n { enabled: !!tag }\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function usePostSearch(query: string) {\n const searchTerm = `%${query}%`;\n const sql = `${POSTS_WITH_CATEGORIES_SQL} AND (p.title LIKE ? OR p.content LIKE ? OR p.excerpt LIKE ?) ORDER BY p.published_at DESC`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['posts', 'search', query],\n sql,\n [searchTerm, searchTerm, searchTerm],\n { enabled: !!query.trim() }\n );\n\n const posts = useMemo(() => {\n if (!data) return [];\n return data.map(transformPost);\n }, [data]);\n\n return { posts, loading, error: error?.message ?? null };\n}\n\nexport function usePostBySlug(slug: string) {\n const sql = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM posts p\n LEFT JOIN post_categories pc ON p.id = pc.post_id\n LEFT JOIN blog_categories c ON pc.category_id = c.id\n WHERE p.slug = ? AND p.published = 1\n GROUP BY p.id\n `;\n\n const { data, isLoading: loading, error } = useRawQueryOne<any>(\n ['posts', 'slug', slug],\n sql,\n [slug],\n { enabled: !!slug }\n );\n\n const post = useMemo(() => {\n if (!data) return null;\n return transformPost(data);\n }, [data]);\n\n return {\n post,\n loading,\n error: !data && !loading && slug ? 'Post not found' : (error?.message ?? null)\n };\n}\n\nexport function useBlogCategories() {\n const { data, isLoading: loading, error } = useRepositoryQuery<BlogCategory>('blog_categories', {\n orderBy: [{ field: 'name', direction: 'ASC' }]\n });\n\n return {\n categories: data ?? [],\n loading,\n error: error?.message ?? null\n };\n}\n\nexport function usePostStats() {\n const { data: statsData, isLoading: loading, error } = useRawQueryOne<{\n totalPosts: number;\n totalViews: number;\n featuredPosts: number;\n }>(\n ['posts', 'stats'],\n `SELECT\n COUNT(*) as totalPosts,\n COALESCE(SUM(view_count), 0) as totalViews,\n SUM(CASE WHEN featured = 1 THEN 1 ELSE 0 END) as featuredPosts\n FROM posts WHERE published = 1`\n );\n\n const { data: categoriesData } = useRawQueryOne<{ count: number }>(\n ['categories', 'count'],\n `SELECT COUNT(*) as count FROM blog_categories`\n );\n\n const stats = useMemo(() => ({\n totalPosts: statsData?.totalPosts ?? 0,\n totalViews: statsData?.totalViews ?? 0,\n featuredPosts: statsData?.featuredPosts ?? 0,\n categoriesCount: categoriesData?.count ?? 0\n }), [statsData, categoriesData]);\n\n return { stats, loading, error: error?.message ?? null };\n}\n"
35
+ },
36
+ {
37
+ "path": "blog-core/lang/en.json",
38
+ "type": "registry:lang",
39
+ "target": "$modules$/blog-core/lang/en.json",
40
+ "content": "{\r\n \"favorites\": \"Favorites\",\r\n \"addedToFavorites\": \"Added to favorites\",\r\n \"removedFromFavorites\": \"Removed from favorites\",\r\n \"noFavorites\": \"No favorites yet\",\r\n \"clearFavorites\": \"Clear all favorites\",\r\n \"posts\": \"Posts\",\r\n \"categories\": \"Categories\",\r\n \"tags\": \"Tags\",\r\n \"readMore\": \"Read more\",\r\n \"minRead\": \"min read\",\r\n \"views\": \"views\",\r\n \"loadingPosts\": \"Loading posts...\",\r\n \"noPosts\": \"No posts found\",\r\n \"errorLoadingPosts\": \"Error loading posts\"\r\n}\r\n"
41
+ },
42
+ {
43
+ "path": "blog-core/lang/tr.json",
44
+ "type": "registry:lang",
45
+ "target": "$modules$/blog-core/lang/tr.json",
46
+ "content": "{\r\n \"favorites\": \"Favoriler\",\r\n \"addedToFavorites\": \"Favorilere eklendi\",\r\n \"removedFromFavorites\": \"Favorilerden kaldırıldı\",\r\n \"noFavorites\": \"Henüz favori yok\",\r\n \"clearFavorites\": \"Tüm favorileri temizle\",\r\n \"posts\": \"Yazılar\",\r\n \"categories\": \"Kategoriler\",\r\n \"tags\": \"Etiketler\",\r\n \"readMore\": \"Devamını oku\",\r\n \"minRead\": \"dk okuma\",\r\n \"views\": \"görüntüleme\",\r\n \"loadingPosts\": \"Yazılar yükleniyor...\",\r\n \"noPosts\": \"Yazı bulunamadı\",\r\n \"errorLoadingPosts\": \"Yazılar yüklenirken hata oluştu\"\r\n}\r\n"
47
+ }
48
+ ],
49
+ "exports": {
50
+ "types": [
51
+ "Author",
52
+ "BlogCategory",
53
+ "BlogContextType",
54
+ "BlogSettings",
55
+ "Comment",
56
+ "Post",
57
+ "PostCategory"
58
+ ],
59
+ "variables": [
60
+ "useBlog",
61
+ "useBlogCategories",
62
+ "useBlogStore",
63
+ "useFeaturedPosts",
64
+ "usePopularPosts",
65
+ "usePostBySlug",
66
+ "usePostSearch",
67
+ "usePostStats",
68
+ "usePosts",
69
+ "usePostsByCategory",
70
+ "usePostsByTag",
71
+ "useRecentPosts"
72
+ ]
73
+ }
74
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "blog-list-page",
3
+ "type": "registry:page",
4
+ "title": "Blog List Page",
5
+ "description": "Blog listing page with category filter tabs, search functionality, and responsive post grid. Uses PostCard component for display with grid/list view toggle. Features loading states, empty state handling, pagination, and featured post highlight at top. Includes sidebar with popular posts, categories, and newsletter signup.",
6
+ "registryDependencies": [
7
+ "blog-core",
8
+ "post-card"
9
+ ],
10
+ "usage": "import { BlogListPage } from '@/modules/blog-list-page';\n\n<Route path=\"/blog\" element={<BlogListPage />} />\n\n• Uses usePosts() from blog-core (Zustand)\n• Features: category tabs, search, grid/list view\n• Sidebar: popular posts, categories, newsletter",
11
+ "route": {
12
+ "path": "/blog",
13
+ "componentName": "BlogListPage"
14
+ },
15
+ "files": [
16
+ {
17
+ "path": "blog-list-page/index.ts",
18
+ "type": "registry:index",
19
+ "target": "$modules$/blog-list-page/index.ts",
20
+ "content": "export * from './blog-list-page';\r\nexport { BlogListPage as default } from './blog-list-page';\r\n"
21
+ },
22
+ {
23
+ "path": "blog-list-page/blog-list-page.tsx",
24
+ "type": "registry:page",
25
+ "target": "$modules$/blog-list-page/blog-list-page.tsx",
26
+ "content": "import { useState, useEffect } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { Search, Filter } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { FadeIn } from \"@/modules/animations\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { usePosts, useBlogCategories } from \"@/modules/blog-core\";\n\nexport function BlogListPage() {\n const { t } = useTranslation(\"blog-list-page\");\n usePageTitle({ title: t(\"title\") });\n\n const [searchParams, setSearchParams] = useSearchParams();\n const [searchTerm, setSearchTerm] = useState(\n searchParams.get(\"search\") || \"\"\n );\n const [selectedCategories, setSelectedCategories] = useState<string[]>(\n searchParams.get(\"categories\")?.split(\",\").filter(Boolean) || []\n );\n const [selectedTags, setSelectedTags] = useState<string[]>(\n searchParams.get(\"tags\")?.split(\",\").filter(Boolean) || []\n );\n const [sortBy, setSortBy] = useState(searchParams.get(\"sort\") || \"newest\");\n const [viewMode, _setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n\n const { posts, loading, error } = usePosts();\n const { categories } = useBlogCategories();\n\n const filteredPosts = posts.filter((post) => {\n if (searchTerm) {\n const searchLower = searchTerm.toLowerCase();\n if (\n !post.title.toLowerCase().includes(searchLower) &&\n !post.excerpt.toLowerCase().includes(searchLower) &&\n !post.content.toLowerCase().includes(searchLower)\n ) {\n return false;\n }\n }\n\n if (selectedCategories.length > 0) {\n const hasMatchingCategory = selectedCategories.some(\n (categorySlug) =>\n post.category === categorySlug ||\n post.categories?.some((cat) => cat.slug === categorySlug)\n );\n if (!hasMatchingCategory) return false;\n }\n\n if (selectedTags.length > 0) {\n const hasMatchingTag = selectedTags.some((tag) =>\n post.tags.includes(tag)\n );\n if (!hasMatchingTag) return false;\n }\n\n return true;\n });\n\n const sortedPosts = [...filteredPosts].sort((a, b) => {\n switch (sortBy) {\n case \"oldest\":\n return (\n new Date(a.published_at).getTime() -\n new Date(b.published_at).getTime()\n );\n case \"popular\":\n return (b.view_count || 0) - (a.view_count || 0);\n case \"reading-time\":\n return (a.read_time || 0) - (b.read_time || 0);\n case \"newest\":\n default:\n return (\n new Date(b.published_at).getTime() -\n new Date(a.published_at).getTime()\n );\n }\n });\n\n useEffect(() => {\n const params = new URLSearchParams();\n if (searchTerm) params.set(\"search\", searchTerm);\n if (selectedCategories.length)\n params.set(\"categories\", selectedCategories.join(\",\"));\n if (selectedTags.length) params.set(\"tags\", selectedTags.join(\",\"));\n if (sortBy !== \"newest\") params.set(\"sort\", sortBy);\n\n setSearchParams(params);\n }, [searchTerm, selectedCategories, selectedTags, sortBy, setSearchParams]);\n\n const handleCategoryChange = (categorySlug: string, checked: boolean) => {\n if (checked) {\n setSelectedCategories([...selectedCategories, categorySlug]);\n } else {\n setSelectedCategories(\n selectedCategories.filter((c) => c !== categorySlug)\n );\n }\n };\n\n const handleTagChange = (tag: string, checked: boolean) => {\n if (checked) {\n setSelectedTags([...selectedTags, tag]);\n } else {\n setSelectedTags(selectedTags.filter((t) => t !== tag));\n }\n };\n\n const allTags = Array.from(new Set(posts.flatMap((post) => post.tags)));\n\n const clearFilters = () => {\n setSearchTerm(\"\");\n setSelectedCategories([]);\n setSelectedTags([]);\n setSortBy(\"newest\");\n };\n\n const FilterSection = () => (\n <div className=\"space-y-6\">\n <div>\n <h3 className=\"font-semibold mb-3 flex items-center gap-2\">\n <Search className=\"h-4 w-4\" />\n {t(\"search\")}\n </h3>\n <Input\n placeholder={t(\"searchPlaceholder\")}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"categories\")}</h3>\n <div className=\"space-y-2\">\n {categories.map((category) => (\n <div key={category.slug} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`category-${category.slug}`}\n checked={selectedCategories.includes(category.slug)}\n onCheckedChange={(checked) =>\n handleCategoryChange(category.slug, checked as boolean)\n }\n />\n <label\n htmlFor={`category-${category.slug}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {category.name}\n </label>\n </div>\n ))}\n </div>\n </div>\n\n {allTags.length > 0 && (\n <div>\n <h3 className=\"font-semibold mb-3\">{t(\"tags\")}</h3>\n <div className=\"space-y-2 max-h-48 overflow-y-auto\">\n {allTags.slice(0, 20).map((tag) => (\n <div key={tag} className=\"flex items-center space-x-2\">\n <Checkbox\n id={`tag-${tag}`}\n checked={selectedTags.includes(tag)}\n onCheckedChange={(checked) =>\n handleTagChange(tag, checked as boolean)\n }\n />\n <label\n htmlFor={`tag-${tag}`}\n className=\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer\"\n >\n {tag}\n </label>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {(searchTerm ||\n selectedCategories.length > 0 ||\n selectedTags.length > 0) && (\n <Button variant=\"outline\" onClick={clearFilters} className=\"w-full\">\n {t(\"clearFilters\")}\n </Button>\n )}\n </div>\n );\n\n if (loading) {\n return (\n <Layout>\n <div className=\"container mx-auto px-4 py-8\">\n <div className=\"animate-pulse space-y-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-48 bg-muted rounded-lg\"></div>\n ))}\n </div>\n </div>\n </Layout>\n );\n }\n\n if (error) {\n return (\n <Layout>\n <div className=\"container mx-auto px-4 py-8 text-center\">\n <p className=\"text-destructive\">{t(\"error\")}</p>\n </div>\n </Layout>\n );\n }\n\n return (\n <Layout>\n <div className=\"container mx-auto px-4 py-8\">\n <FadeIn className=\"flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">{t(\"title\")}</h1>\n <p className=\"text-muted-foreground\">{t(\"subtitle\")}</p>\n </div>\n\n <div className=\"flex items-center gap-4\">\n <Select value={sortBy} onValueChange={setSortBy}>\n <SelectTrigger className=\"w-[180px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"newest\">{t(\"sortNewest\")}</SelectItem>\n <SelectItem value=\"oldest\">{t(\"sortOldest\")}</SelectItem>\n <SelectItem value=\"popular\">{t(\"sortPopular\")}</SelectItem>\n <SelectItem value=\"reading-time\">\n {t(\"sortReadingTime\")}\n </SelectItem>\n </SelectContent>\n </Select>\n\n <Sheet>\n <SheetTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"lg:hidden\">\n <Filter className=\"h-4 w-4 mr-2\" />\n {t(\"filters\")}\n </Button>\n </SheetTrigger>\n <SheetContent>\n <SheetHeader>\n <SheetTitle>{t(\"filters\")}</SheetTitle>\n <SheetDescription>{t(\"filterDescription\")}</SheetDescription>\n </SheetHeader>\n <div className=\"mt-6\">\n <FilterSection />\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </FadeIn>\n\n <div className=\"flex flex-col lg:flex-row gap-8\">\n <div className=\"hidden lg:block w-64 flex-shrink-0\">\n <div className=\"sticky top-4\">\n <FilterSection />\n </div>\n </div>\n\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between mb-6\">\n <p className=\"text-sm text-muted-foreground\">\n {t(\"showing\")} {sortedPosts.length} {t(\"of\")} {posts.length}{\" \"}\n {t(\"posts\")}\n {searchTerm && (\n <span className=\"ml-1\">\n {t(\"for\")} \"<strong>{searchTerm}</strong>\"\n </span>\n )}\n </p>\n </div>\n\n {sortedPosts.length > 0 ? (\n <div\n className={`grid gap-6 ${\n viewMode === \"grid\"\n ? \"grid-cols-1 md:grid-cols-2 xl:grid-cols-3\"\n : \"grid-cols-1\"\n }`}\n >\n {sortedPosts.map((post) => (\n <PostCard key={post.id} post={post} layout={viewMode} />\n ))}\n </div>\n ) : (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground mb-4\">\n {t(\"noPostsFound\")}\n </p>\n <Button onClick={clearFilters} variant=\"outline\">\n {t(\"clearFilters\")}\n </Button>\n </div>\n )}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
27
+ },
28
+ {
29
+ "path": "blog-list-page/lang/en.json",
30
+ "type": "registry:lang",
31
+ "target": "$modules$/blog-list-page/lang/en.json",
32
+ "content": "{\r\n \"title\": \"Blog\",\r\n \"subtitle\": \"Discover our latest articles and insights\",\r\n \"allPosts\": \"All Posts\",\r\n \"featuredPosts\": \"Featured Posts\",\r\n \"recentPosts\": \"Recent Posts\",\r\n \"popularPosts\": \"Popular Posts\",\r\n \"categories\": \"Categories\",\r\n \"tags\": \"Tags\",\r\n \"filters\": \"Filters\",\r\n \"search\": \"Search\",\r\n \"searchPlaceholder\": \"Search posts...\",\r\n \"sortBy\": \"Sort By\",\r\n \"sortNewest\": \"Newest First\",\r\n \"sortOldest\": \"Oldest First\",\r\n \"sortPopular\": \"Most Popular\",\r\n \"sortReadingTime\": \"Reading Time\",\r\n \"showing\": \"Showing\",\r\n \"of\": \"of\",\r\n \"posts\": \"posts\",\r\n \"for\": \"for\",\r\n \"noPostsFound\": \"No posts found matching your criteria.\",\r\n \"clearFilters\": \"Clear Filters\",\r\n \"filterDescription\": \"Filter posts by category, tags, and more\",\r\n \"readMore\": \"Read More\",\r\n \"minRead\": \"min\",\r\n \"views\": \"views\",\r\n \"error\": \"Failed to load posts\"\r\n}\r\n"
33
+ },
34
+ {
35
+ "path": "blog-list-page/lang/tr.json",
36
+ "type": "registry:lang",
37
+ "target": "$modules$/blog-list-page/lang/tr.json",
38
+ "content": "{\r\n \"title\": \"Blog\",\r\n \"subtitle\": \"En son makalelerimizi ve görüşlerimizi keşfedin\",\r\n \"allPosts\": \"Tüm Yazılar\",\r\n \"featuredPosts\": \"Öne Çıkan Yazılar\",\r\n \"recentPosts\": \"Son Yazılar\",\r\n \"popularPosts\": \"Popüler Yazılar\",\r\n \"categories\": \"Kategoriler\",\r\n \"tags\": \"Etiketler\",\r\n \"filters\": \"Filtreler\",\r\n \"search\": \"Arama\",\r\n \"searchPlaceholder\": \"Yazı ara...\",\r\n \"sortBy\": \"Sırala\",\r\n \"sortNewest\": \"En Yeni\",\r\n \"sortOldest\": \"En Eski\",\r\n \"sortPopular\": \"En Popüler\",\r\n \"sortReadingTime\": \"Okuma Süresi\",\r\n \"showing\": \"Gösterilen\",\r\n \"of\": \"/\",\r\n \"posts\": \"yazı\",\r\n \"for\": \"için\",\r\n \"noPostsFound\": \"Kriterlere uygun yazı bulunamadı.\",\r\n \"clearFilters\": \"Filtreleri Temizle\",\r\n \"filterDescription\": \"Yazıları kategori, etiket ve daha fazlasına göre filtreleyin\",\r\n \"readMore\": \"Devamını Oku\",\r\n \"minRead\": \"dk\",\r\n \"views\": \"görüntülenme\",\r\n \"error\": \"Yazılar yüklenemedi\"\r\n}\r\n"
39
+ }
40
+ ],
41
+ "exports": {
42
+ "types": [],
43
+ "variables": [
44
+ "BlogListPage",
45
+ "default"
46
+ ]
47
+ }
48
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "blog-section",
3
+ "type": "registry:component",
4
+ "title": "Blog Section",
5
+ "description": "Blog posts preview section with 3-column card grid. Features tagline badge, heading, subtitle, and 'view all' link. Each card shows image, category badge, date, title, summary, and 'read more' link. Great for homepage blog previews.",
6
+ "registryDependencies": [
7
+ "card",
8
+ "badge"
9
+ ],
10
+ "usage": "import { BlogSection } from '@/modules/blog-section';\n\n<BlogSection />\n\n- 3-column post cards\n- Category badges and dates\n- Hover effects on images\n- Customize posts via lang files",
11
+ "files": [
12
+ {
13
+ "path": "blog-section/index.ts",
14
+ "type": "registry:index",
15
+ "target": "$modules$/blog-section/index.ts",
16
+ "content": "export * from './blog-section';\r\n"
17
+ },
18
+ {
19
+ "path": "blog-section/blog-section.tsx",
20
+ "type": "registry:component",
21
+ "target": "$modules$/blog-section/blog-section.tsx",
22
+ "content": "import { Link } from \"react-router\";\r\nimport { ArrowRight } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport {\r\n Card,\r\n CardContent,\r\n CardFooter,\r\n CardHeader,\r\n} from \"@/components/ui/card\";\r\n\r\ninterface BlogSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function BlogSection({ className }: BlogSectionProps) {\r\n const { t } = useTranslation(\"blog-section\");\r\n\r\n const posts = [\r\n {\r\n id: \"1\",\r\n title: t(\"post1Title\", \"Getting Started with Modern Web Development\"),\r\n summary: t(\r\n \"post1Summary\",\r\n \"Learn the fundamentals of modern web development, from setting up your environment to deploying your first application.\"\r\n ),\r\n category: t(\"post1Category\", \"Tutorial\"),\r\n author: t(\"post1Author\", \"Sarah Chen\"),\r\n date: t(\"post1Date\", \"Jan 15, 2024\"),\r\n image: \"/images/placeholder.png\",\r\n slug: \"getting-started\",\r\n },\r\n {\r\n id: \"2\",\r\n title: t(\"post2Title\", \"Best Practices for Building Scalable Apps\"),\r\n summary: t(\r\n \"post2Summary\",\r\n \"Discover proven strategies and patterns for building applications that scale with your growing user base.\"\r\n ),\r\n category: t(\"post2Category\", \"Best Practices\"),\r\n author: t(\"post2Author\", \"Michael Park\"),\r\n date: t(\"post2Date\", \"Jan 10, 2024\"),\r\n image: \"/images/placeholder.png\",\r\n slug: \"scalable-apps\",\r\n },\r\n {\r\n id: \"3\",\r\n title: t(\"post3Title\", \"The Future of User Interface Design\"),\r\n summary: t(\r\n \"post3Summary\",\r\n \"Explore emerging trends in UI design and how they're shaping the way we build digital experiences.\"\r\n ),\r\n category: t(\"post3Category\", \"Design\"),\r\n author: t(\"post3Author\", \"Emily Davis\"),\r\n date: t(\"post3Date\", \"Jan 5, 2024\"),\r\n image: \"/images/placeholder.png\",\r\n slug: \"future-ui-design\",\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 {/* Header */}\r\n <div className=\"text-center mb-12\">\r\n <Badge variant=\"secondary\" className=\"mb-4\">\r\n {t(\"tagline\", \"Latest Updates\")}\r\n </Badge>\r\n <h2 className=\"text-3xl font-bold md:text-4xl lg:text-5xl mb-4\">\r\n {t(\"title\", \"From Our Blog\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto mb-6\">\r\n {t(\r\n \"subtitle\",\r\n \"Discover the latest trends, tips, and insights from our team of experts.\"\r\n )}\r\n </p>\r\n <Button variant=\"link\" asChild>\r\n <Link to=\"/blog\">\r\n {t(\"viewAll\", \"View all articles\")}\r\n <ArrowRight className=\"ml-2 h-4 w-4\" />\r\n </Link>\r\n </Button>\r\n </div>\r\n\r\n {/* Posts Grid */}\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto\">\r\n {posts.map((post) => (\r\n <Card key={post.id} className=\"overflow-hidden group p-0\">\r\n <div className=\"aspect-video overflow-hidden\">\r\n <Link to={`/blog/${post.slug}`}>\r\n <img\r\n src={post.image}\r\n alt={post.title}\r\n className=\"w-full h-full object-cover transition-transform duration-300 group-hover:scale-105\"\r\n onError={(e) => {\r\n e.currentTarget.style.display = \"none\";\r\n }}\r\n />\r\n </Link>\r\n </div>\r\n <CardHeader className=\"pt-6 pb-2\">\r\n <div className=\"flex items-center gap-2 mb-2\">\r\n <Badge variant=\"outline\" className=\"text-xs\">\r\n {post.category}\r\n </Badge>\r\n <span className=\"text-xs text-muted-foreground\">\r\n {post.date}\r\n </span>\r\n </div>\r\n <Link to={`/blog/${post.slug}`}>\r\n <h3 className=\"text-lg font-semibold hover:text-primary transition-colors line-clamp-2\">\r\n {post.title}\r\n </h3>\r\n </Link>\r\n </CardHeader>\r\n <CardContent className=\"py-0\">\r\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\r\n {post.summary}\r\n </p>\r\n </CardContent>\r\n <CardFooter className=\"pb-2\">\r\n <Link\r\n to={`/blog/${post.slug}`}\r\n className=\"text-sm font-medium text-primary hover:underline inline-flex items-center whitespace-nowrap\"\r\n >\r\n {t(\"readMore\", \"Read more\")}\r\n <ArrowRight className=\"ml-1 h-3 w-3 shrink-0\" />\r\n </Link>\r\n </CardFooter>\r\n </Card>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
23
+ },
24
+ {
25
+ "path": "blog-section/lang/en.json",
26
+ "type": "registry:lang",
27
+ "target": "$modules$/blog-section/lang/en.json",
28
+ "content": "{\r\n \"tagline\": \"Latest Updates\",\r\n \"title\": \"From Our Blog\",\r\n \"subtitle\": \"AI will customize this blog section subtitle based on your content strategy. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"viewAll\": \"View all articles\",\r\n \"readMore\": \"Read more\",\r\n \"post1Title\": \"AI will replace this blog post title\",\r\n \"post1Summary\": \"This is placeholder blog post summary text. AI will customize this based on your actual blog content. Lorem ipsum dolor sit amet.\",\r\n \"post1Category\": \"Tutorial\",\r\n \"post1Author\": \"Sarah Chen\",\r\n \"post1Date\": \"Jan 15, 2024\",\r\n \"post2Title\": \"Replace this with your second blog post title\",\r\n \"post2Summary\": \"Placeholder summary text. AI will generate appropriate blog post summaries based on your content. Sed do eiusmod tempor.\",\r\n \"post2Category\": \"Best Practices\",\r\n \"post2Author\": \"Michael Park\",\r\n \"post2Date\": \"Jan 10, 2024\",\r\n \"post3Title\": \"This blog post title will be customized by AI\",\r\n \"post3Summary\": \"AI will replace this summary with relevant content based on your blog posts. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"post3Category\": \"Design\",\r\n \"post3Author\": \"Emily Davis\",\r\n \"post3Date\": \"Jan 5, 2024\"\r\n}\r\n"
29
+ },
30
+ {
31
+ "path": "blog-section/lang/tr.json",
32
+ "type": "registry:lang",
33
+ "target": "$modules$/blog-section/lang/tr.json",
34
+ "content": "{\r\n \"tagline\": \"Son Güncellemeler\",\r\n \"title\": \"Blogumuzdan\",\r\n \"subtitle\": \"AI bu blog bölümü alt başlığını içerik stratejinize göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"viewAll\": \"Tüm makaleleri görüntüle\",\r\n \"readMore\": \"Devamını oku\",\r\n \"post1Title\": \"AI bu blog yazısı başlığını değiştirecektir\",\r\n \"post1Summary\": \"Bu placeholder blog yazısı özet metnidir. AI bunu gerçek blog içeriğinize göre özelleştirecektir. Lorem ipsum dolor sit amet.\",\r\n \"post1Category\": \"Eğitim\",\r\n \"post1Author\": \"Sarah Chen\",\r\n \"post1Date\": \"15 Ocak 2024\",\r\n \"post2Title\": \"Bunu ikinci blog yazınızın başlığıyla değiştirin\",\r\n \"post2Summary\": \"Placeholder özet metni. AI içeriğinize göre uygun blog yazısı özetleri üretecektir. Sed do eiusmod tempor.\",\r\n \"post2Category\": \"En İyi Uygulamalar\",\r\n \"post2Author\": \"Michael Park\",\r\n \"post2Date\": \"10 Ocak 2024\",\r\n \"post3Title\": \"Bu blog yazısı başlığı AI tarafından özelleştirilecektir\",\r\n \"post3Summary\": \"AI bu özeti blog yazılarınıza göre ilgili içerikle değiştirecektir. Ut enim ad minim veniam, quis nostrud exercitation.\",\r\n \"post3Category\": \"Tasarım\",\r\n \"post3Author\": \"Emily Davis\",\r\n \"post3Date\": \"5 Ocak 2024\"\r\n}\r\n"
35
+ }
36
+ ],
37
+ "exports": {
38
+ "types": [],
39
+ "variables": [
40
+ "BlogSection"
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "cards-carousel-section",
3
+ "type": "registry:component",
4
+ "title": "Cards Carousel Section",
5
+ "description": "Apple-style expandable cards carousel with smooth animations. Cards expand into full-screen modals on click. Features horizontal scrolling, navigation buttons, and responsive design.",
6
+ "dependencies": [
7
+ "framer-motion",
8
+ "lucide-react"
9
+ ],
10
+ "registryDependencies": [],
11
+ "usage": "import { CardsCarouselSection, Card, Carousel } from '@/modules/cards-carousel-section';\n\n<CardsCarouselSection\n title=\"Explore\"\n items={[\n {\n category: \"Technology\",\n title: \"AI Revolution\",\n src: \"/images/ai.jpg\",\n content: <div>Your content here...</div>\n },\n // more items...\n ]}\n/>",
12
+ "files": [
13
+ {
14
+ "path": "cards-carousel-section/index.ts",
15
+ "type": "registry:index",
16
+ "target": "$modules$/cards-carousel-section/index.ts",
17
+ "content": "export * from './cards-carousel-section';\r\n"
18
+ },
19
+ {
20
+ "path": "cards-carousel-section/cards-carousel-section.tsx",
21
+ "type": "registry:component",
22
+ "target": "$modules$/cards-carousel-section/cards-carousel-section.tsx",
23
+ "content": "import { useEffect, useRef, useState, createContext, useContext } from \"react\";\r\nimport type { ReactNode } from \"react\";\r\nimport { motion, AnimatePresence } from \"framer-motion\";\r\nimport { X, ChevronLeft, ChevronRight } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n// Context for outside click detection\r\nconst CarouselContext = createContext<{\r\n onCardClose: (index: number) => void;\r\n currentIndex: number;\r\n}>({\r\n onCardClose: () => {},\r\n currentIndex: 0,\r\n});\r\n\r\n// Card interface\r\ninterface CardData {\r\n category: string;\r\n title: string;\r\n src: string;\r\n content: ReactNode;\r\n}\r\n\r\n// Card Component\r\nexport function Card({\r\n card,\r\n index,\r\n layout = false,\r\n}: {\r\n card: CardData;\r\n index: number;\r\n layout?: boolean;\r\n}) {\r\n const [open, setOpen] = useState(false);\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const { onCardClose } = useContext(CarouselContext);\r\n\r\n useEffect(() => {\r\n function onKeyDown(event: KeyboardEvent) {\r\n if (event.key === \"Escape\") {\r\n handleClose();\r\n }\r\n }\r\n\r\n if (open) {\r\n document.body.style.overflow = \"hidden\";\r\n window.addEventListener(\"keydown\", onKeyDown);\r\n } else {\r\n document.body.style.overflow = \"auto\";\r\n }\r\n\r\n return () => {\r\n window.removeEventListener(\"keydown\", onKeyDown);\r\n document.body.style.overflow = \"auto\";\r\n };\r\n }, [open]);\r\n\r\n const handleOpen = () => setOpen(true);\r\n const handleClose = () => {\r\n setOpen(false);\r\n onCardClose(index);\r\n };\r\n\r\n return (\r\n <>\r\n <AnimatePresence>\r\n {open && (\r\n <div className=\"fixed inset-0 h-screen z-50 overflow-auto\">\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n className=\"bg-black/80 backdrop-blur-lg h-full w-full fixed inset-0\"\r\n onClick={handleClose}\r\n />\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n ref={containerRef}\r\n layoutId={layout ? `card-${card.title}` : undefined}\r\n className=\"max-w-5xl mx-auto bg-white dark:bg-neutral-900 h-fit z-[60] my-10 p-4 md:p-10 rounded-3xl relative\"\r\n >\r\n <button\r\n className=\"sticky top-4 h-8 w-8 right-0 ml-auto bg-black dark:bg-white rounded-full flex items-center justify-center\"\r\n onClick={handleClose}\r\n >\r\n <X className=\"h-6 w-6 text-neutral-100 dark:text-neutral-900\" />\r\n </button>\r\n <motion.p\r\n layoutId={layout ? `category-${card.title}` : undefined}\r\n className=\"text-base font-medium text-black dark:text-white\"\r\n >\r\n {card.category}\r\n </motion.p>\r\n <motion.p\r\n layoutId={layout ? `title-${card.title}` : undefined}\r\n className=\"text-2xl md:text-5xl font-semibold text-neutral-700 mt-4 dark:text-white\"\r\n >\r\n {card.title}\r\n </motion.p>\r\n <div className=\"py-10\">{card.content}</div>\r\n </motion.div>\r\n </div>\r\n )}\r\n </AnimatePresence>\r\n <motion.button\r\n layoutId={layout ? `card-${card.title}` : undefined}\r\n onClick={handleOpen}\r\n className=\"rounded-3xl bg-gray-100 dark:bg-neutral-900 h-80 w-56 md:h-[40rem] md:w-96 overflow-hidden flex flex-col items-start justify-start relative z-10\"\r\n >\r\n <div className=\"absolute h-full top-0 inset-x-0 bg-gradient-to-b from-black/50 via-transparent to-transparent z-30 pointer-events-none\" />\r\n <div className=\"relative z-40 p-8\">\r\n <motion.p\r\n layoutId={layout ? `category-${card.title}` : undefined}\r\n className=\"text-white text-sm md:text-base font-medium text-left\"\r\n >\r\n {card.category}\r\n </motion.p>\r\n <motion.p\r\n layoutId={layout ? `title-${card.title}` : undefined}\r\n className=\"text-white text-xl md:text-3xl font-semibold max-w-xs text-left mt-2\"\r\n >\r\n {card.title}\r\n </motion.p>\r\n </div>\r\n <img\r\n src={card.src}\r\n alt={card.title}\r\n className=\"object-cover absolute z-10 inset-0 w-full h-full\"\r\n />\r\n </motion.button>\r\n </>\r\n );\r\n}\r\n\r\n// Carousel Component\r\nexport function Carousel({ items }: { items: ReactNode[] }) {\r\n const carouselRef = useRef<HTMLDivElement>(null);\r\n const [canScrollLeft, setCanScrollLeft] = useState(false);\r\n const [canScrollRight, setCanScrollRight] = useState(true);\r\n const [currentIndex, setCurrentIndex] = useState(0);\r\n\r\n useEffect(() => {\r\n checkScrollability();\r\n }, []);\r\n\r\n const checkScrollability = () => {\r\n if (carouselRef.current) {\r\n const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;\r\n setCanScrollLeft(scrollLeft > 0);\r\n setCanScrollRight(scrollLeft < scrollWidth - clientWidth);\r\n }\r\n };\r\n\r\n const scrollLeft = () => {\r\n if (carouselRef.current) {\r\n carouselRef.current.scrollBy({ left: -300, behavior: \"smooth\" });\r\n }\r\n };\r\n\r\n const scrollRight = () => {\r\n if (carouselRef.current) {\r\n carouselRef.current.scrollBy({ left: 300, behavior: \"smooth\" });\r\n }\r\n };\r\n\r\n const handleCardClose = (index: number) => {\r\n if (carouselRef.current) {\r\n const cardWidth = isMobile() ? 230 : 384;\r\n const gap = isMobile() ? 4 : 8;\r\n const scrollPosition = (cardWidth + gap) * (index + 1);\r\n carouselRef.current.scrollTo({\r\n left: scrollPosition,\r\n behavior: \"smooth\",\r\n });\r\n setCurrentIndex(index);\r\n }\r\n };\r\n\r\n const isMobile = () => {\r\n return window && window.innerWidth < 768;\r\n };\r\n\r\n return (\r\n <CarouselContext.Provider value={{ onCardClose: handleCardClose, currentIndex }}>\r\n <div className=\"relative w-full\">\r\n <div\r\n className=\"flex w-full overflow-x-scroll overscroll-x-auto py-10 md:py-20 scroll-smooth [scrollbar-width:none]\"\r\n ref={carouselRef}\r\n onScroll={checkScrollability}\r\n >\r\n <div className=\"absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l from-background to-transparent pointer-events-none\" />\r\n <div className=\"flex flex-row justify-start gap-4 pl-4 max-w-7xl mx-auto\">\r\n {items.map((item, index) => (\r\n <motion.div\r\n initial={{ opacity: 0, y: 20 }}\r\n animate={{\r\n opacity: 1,\r\n y: 0,\r\n transition: {\r\n duration: 0.5,\r\n delay: 0.2 * index,\r\n ease: \"easeOut\",\r\n },\r\n }}\r\n key={\"card\" + index}\r\n className=\"last:pr-[5%] md:last:pr-[33%] rounded-3xl\"\r\n >\r\n {item}\r\n </motion.div>\r\n ))}\r\n </div>\r\n </div>\r\n <div className=\"flex justify-end gap-2 mr-10\">\r\n <button\r\n className=\"relative z-40 h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center disabled:opacity-50\"\r\n onClick={scrollLeft}\r\n disabled={!canScrollLeft}\r\n >\r\n <ChevronLeft className=\"h-6 w-6 text-gray-500\" />\r\n </button>\r\n <button\r\n className=\"relative z-40 h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center disabled:opacity-50\"\r\n onClick={scrollRight}\r\n disabled={!canScrollRight}\r\n >\r\n <ChevronRight className=\"h-6 w-6 text-gray-500\" />\r\n </button>\r\n </div>\r\n </div>\r\n </CarouselContext.Provider>\r\n );\r\n}\r\n\r\n// Section Component\r\ninterface CardsCarouselSectionProps {\r\n title?: string;\r\n items: CardData[];\r\n className?: string;\r\n}\r\n\r\nexport function CardsCarouselSection({\r\n title,\r\n items,\r\n className,\r\n}: CardsCarouselSectionProps) {\r\n const cards = items.map((card, index) => (\r\n <Card key={card.src} card={card} index={index} />\r\n ));\r\n\r\n return (\r\n <section className={cn(\"w-full py-16 md:py-20\", className)}>\r\n {title && (\r\n <h2 className=\"max-w-7xl pl-4 mx-auto text-xl md:text-5xl font-bold text-neutral-800 dark:text-neutral-200\">\r\n {title}\r\n </h2>\r\n )}\r\n <Carousel items={cards} />\r\n </section>\r\n );\r\n}\r\n"
24
+ },
25
+ {
26
+ "path": "cards-carousel-section/lang/en.json",
27
+ "type": "registry:lang",
28
+ "target": "$modules$/cards-carousel-section/lang/en.json",
29
+ "content": "{\r\n \"title\": \"Explore\"\r\n}\r\n"
30
+ },
31
+ {
32
+ "path": "cards-carousel-section/lang/tr.json",
33
+ "type": "registry:lang",
34
+ "target": "$modules$/cards-carousel-section/lang/tr.json",
35
+ "content": "{\r\n \"title\": \"Keşfet\"\r\n}\r\n"
36
+ }
37
+ ],
38
+ "exports": {
39
+ "types": [],
40
+ "variables": [
41
+ "Card",
42
+ "CardsCarouselSection",
43
+ "Carousel"
44
+ ]
45
+ }
46
+ }