@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.
- package/dist/index.js +212 -0
- package/dist/registry/about-page.json +45 -0
- package/dist/registry/about-section.json +40 -0
- package/dist/registry/animations.json +69 -0
- package/dist/registry/bento-grid-section.json +42 -0
- package/dist/registry/blog-core.json +74 -0
- package/dist/registry/blog-list-page.json +48 -0
- package/dist/registry/blog-section.json +43 -0
- package/dist/registry/cards-carousel-section.json +46 -0
- package/dist/registry/cart-drawer.json +43 -0
- package/dist/registry/cart-page.json +47 -0
- package/dist/registry/category-section.json +43 -0
- package/dist/registry/checkout-page.json +47 -0
- package/dist/registry/contact-info-grid.json +40 -0
- package/dist/registry/contact-page-centered.json +50 -0
- package/dist/registry/contact-page-map-overlay.json +54 -0
- package/dist/registry/contact-page-map-split.json +54 -0
- package/dist/registry/contact-page-split.json +49 -0
- package/dist/registry/contact-page.json +45 -0
- package/dist/registry/content-section.json +40 -0
- package/dist/registry/cookies-page.json +45 -0
- package/dist/registry/cta-section.json +40 -0
- package/dist/registry/docs/about-page.md +32 -0
- package/dist/registry/docs/about-section.md +33 -0
- package/dist/registry/docs/animations.md +44 -0
- package/dist/registry/docs/bento-grid-section.md +40 -0
- package/dist/registry/docs/blog-core.md +37 -0
- package/dist/registry/docs/blog-list-page.md +38 -0
- package/dist/registry/docs/blog-section.md +39 -0
- package/dist/registry/docs/cards-carousel-section.md +39 -0
- package/dist/registry/docs/cart-drawer.md +42 -0
- package/dist/registry/docs/cart-page.md +37 -0
- package/dist/registry/docs/category-section.md +34 -0
- package/dist/registry/docs/checkout-page.md +38 -0
- package/dist/registry/docs/contact-info-grid.md +33 -0
- package/dist/registry/docs/contact-page-centered.md +41 -0
- package/dist/registry/docs/contact-page-map-overlay.md +44 -0
- package/dist/registry/docs/contact-page-map-split.md +44 -0
- package/dist/registry/docs/contact-page-split.md +40 -0
- package/dist/registry/docs/contact-page.md +33 -0
- package/dist/registry/docs/content-section.md +35 -0
- package/dist/registry/docs/cookies-page.md +32 -0
- package/dist/registry/docs/cta-section.md +32 -0
- package/dist/registry/docs/ecommerce-core.md +41 -0
- package/dist/registry/docs/empty-page.md +31 -0
- package/dist/registry/docs/faq-categorized.md +38 -0
- package/dist/registry/docs/faq-simple.md +38 -0
- package/dist/registry/docs/favorites-blog-block.md +38 -0
- package/dist/registry/docs/favorites-ecommerce-block.md +38 -0
- package/dist/registry/docs/feature-section.md +33 -0
- package/dist/registry/docs/featured-products.md +38 -0
- package/dist/registry/docs/footer-detailed.md +33 -0
- package/dist/registry/docs/footer-minimal.md +32 -0
- package/dist/registry/docs/footer.md +32 -0
- package/dist/registry/docs/google-map.md +36 -0
- package/dist/registry/docs/header-centered-pill.md +37 -0
- package/dist/registry/docs/header-ecommerce.md +38 -0
- package/dist/registry/docs/header-mega.md +40 -0
- package/dist/registry/docs/header-minimal.md +38 -0
- package/dist/registry/docs/header-simple.md +32 -0
- package/dist/registry/docs/hero-cta.md +38 -0
- package/dist/registry/docs/hero-gradient.md +33 -0
- package/dist/registry/docs/hero-grid.md +40 -0
- package/dist/registry/docs/hero-profile.md +33 -0
- package/dist/registry/docs/hero.md +32 -0
- package/dist/registry/docs/login-page-split.md +40 -0
- package/dist/registry/docs/login-page.md +39 -0
- package/dist/registry/docs/newsletter-section.md +40 -0
- package/dist/registry/docs/order-card-compact.md +37 -0
- package/dist/registry/docs/order-detail-block.md +37 -0
- package/dist/registry/docs/orders-list-block.md +40 -0
- package/dist/registry/docs/payment-success-block.md +32 -0
- package/dist/registry/docs/post-card.md +37 -0
- package/dist/registry/docs/post-detail-block.md +37 -0
- package/dist/registry/docs/pricing-card.md +37 -0
- package/dist/registry/docs/pricing-section.md +39 -0
- package/dist/registry/docs/privacy-page.md +32 -0
- package/dist/registry/docs/product-card-detailed.md +42 -0
- package/dist/registry/docs/product-card-hover.md +35 -0
- package/dist/registry/docs/product-card.md +37 -0
- package/dist/registry/docs/product-detail-block.md +37 -0
- package/dist/registry/docs/product-detail-section.md +45 -0
- package/dist/registry/docs/products-page.md +39 -0
- package/dist/registry/docs/related-posts-block.md +38 -0
- package/dist/registry/docs/related-products-block.md +38 -0
- package/dist/registry/docs/service-card.md +34 -0
- package/dist/registry/docs/skill-card.md +33 -0
- package/dist/registry/docs/terms-page.md +32 -0
- package/dist/registry/docs/testimonials-carousel.md +40 -0
- package/dist/registry/docs/testimonials-grid.md +39 -0
- package/dist/registry/ecommerce-core.json +95 -0
- package/dist/registry/empty-page.json +45 -0
- package/dist/registry/faq-categorized.json +42 -0
- package/dist/registry/faq-simple.json +42 -0
- package/dist/registry/favorites-blog-block.json +43 -0
- package/dist/registry/favorites-ecommerce-block.json +43 -0
- package/dist/registry/feature-section.json +40 -0
- package/dist/registry/featured-products.json +43 -0
- package/dist/registry/footer-detailed.json +43 -0
- package/dist/registry/footer-minimal.json +40 -0
- package/dist/registry/footer.json +40 -0
- package/dist/registry/google-map.json +31 -0
- package/dist/registry/header-centered-pill.json +45 -0
- package/dist/registry/header-ecommerce.json +42 -0
- package/dist/registry/header-mega.json +47 -0
- package/dist/registry/header-minimal.json +45 -0
- package/dist/registry/header-simple.json +43 -0
- package/dist/registry/hero-cta.json +42 -0
- package/dist/registry/hero-gradient.json +40 -0
- package/dist/registry/hero-grid.json +42 -0
- package/dist/registry/hero-profile.json +62 -0
- package/dist/registry/hero.json +40 -0
- package/dist/registry/index.json +70 -0
- package/dist/registry/login-page-split.json +47 -0
- package/dist/registry/login-page.json +49 -0
- package/dist/registry/newsletter-section.json +44 -0
- package/dist/registry/order-card-compact.json +42 -0
- package/dist/registry/order-detail-block.json +42 -0
- package/dist/registry/orders-list-block.json +45 -0
- package/dist/registry/payment-success-block.json +40 -0
- package/dist/registry/post-card.json +42 -0
- package/dist/registry/post-detail-block.json +42 -0
- package/dist/registry/pricing-card.json +40 -0
- package/dist/registry/pricing-section.json +43 -0
- package/dist/registry/privacy-page.json +45 -0
- package/dist/registry/product-card-detailed.json +45 -0
- package/dist/registry/product-card-hover.json +40 -0
- package/dist/registry/product-card.json +42 -0
- package/dist/registry/product-detail-block.json +42 -0
- package/dist/registry/product-detail-section.json +46 -0
- package/dist/registry/products-page.json +48 -0
- package/dist/registry/related-posts-block.json +43 -0
- package/dist/registry/related-products-block.json +43 -0
- package/dist/registry/service-card.json +28 -0
- package/dist/registry/skill-card.json +28 -0
- package/dist/registry/terms-page.json +45 -0
- package/dist/registry/testimonials-carousel.json +44 -0
- package/dist/registry/testimonials-grid.json +43 -0
- package/package.json +52 -0
- package/template/.env +6 -0
- package/template/.prettierignore +3 -0
- package/template/.prettierrc +1 -0
- package/template/README.md +73 -0
- package/template/bun.lock +1007 -0
- package/template/components.json +22 -0
- package/template/eslint.config.js +32 -0
- package/template/index.html +285 -0
- package/template/package.json +92 -0
- package/template/promake.json +6 -0
- package/template/public/_redirects +1 -0
- package/template/public/data/database.db +0 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/images/placeholder.png +0 -0
- package/template/public/robots.txt +14 -0
- package/template/scripts/init-db.ts +131 -0
- package/template/src/App.tsx +33 -0
- package/template/src/components/Footer.tsx +100 -0
- package/template/src/components/Header.tsx +79 -0
- package/template/src/components/Hero.tsx +69 -0
- package/template/src/components/LanguageSwitcher.tsx +47 -0
- package/template/src/components/Layout.tsx +25 -0
- package/template/src/components/Logo.tsx +64 -0
- package/template/src/components/ThemeSwitcher.tsx +58 -0
- package/template/src/components/ui/accordion.tsx +64 -0
- package/template/src/components/ui/alert-dialog.tsx +155 -0
- package/template/src/components/ui/alert.tsx +66 -0
- package/template/src/components/ui/aspect-ratio.tsx +11 -0
- package/template/src/components/ui/avatar.tsx +51 -0
- package/template/src/components/ui/badge.tsx +46 -0
- package/template/src/components/ui/breadcrumb.tsx +109 -0
- package/template/src/components/ui/button-group.tsx +83 -0
- package/template/src/components/ui/button.tsx +62 -0
- package/template/src/components/ui/calendar.tsx +220 -0
- package/template/src/components/ui/card.tsx +92 -0
- package/template/src/components/ui/carousel.tsx +239 -0
- package/template/src/components/ui/chart.tsx +357 -0
- package/template/src/components/ui/checkbox.tsx +32 -0
- package/template/src/components/ui/collapsible.tsx +31 -0
- package/template/src/components/ui/command.tsx +182 -0
- package/template/src/components/ui/context-menu.tsx +252 -0
- package/template/src/components/ui/dialog.tsx +141 -0
- package/template/src/components/ui/drawer.tsx +135 -0
- package/template/src/components/ui/dropdown-menu.tsx +255 -0
- package/template/src/components/ui/empty.tsx +104 -0
- package/template/src/components/ui/field.tsx +246 -0
- package/template/src/components/ui/form.tsx +168 -0
- package/template/src/components/ui/hover-card.tsx +44 -0
- package/template/src/components/ui/input-group.tsx +170 -0
- package/template/src/components/ui/input-otp.tsx +75 -0
- package/template/src/components/ui/input.tsx +21 -0
- package/template/src/components/ui/item.tsx +193 -0
- package/template/src/components/ui/kbd.tsx +28 -0
- package/template/src/components/ui/label.tsx +24 -0
- package/template/src/components/ui/menubar.tsx +274 -0
- package/template/src/components/ui/navigation-menu.tsx +168 -0
- package/template/src/components/ui/pagination.tsx +127 -0
- package/template/src/components/ui/popover.tsx +48 -0
- package/template/src/components/ui/progress.tsx +29 -0
- package/template/src/components/ui/radio-group.tsx +45 -0
- package/template/src/components/ui/resizable.tsx +54 -0
- package/template/src/components/ui/scroll-area.tsx +58 -0
- package/template/src/components/ui/select.tsx +188 -0
- package/template/src/components/ui/separator.tsx +28 -0
- package/template/src/components/ui/sheet.tsx +137 -0
- package/template/src/components/ui/sidebar.tsx +726 -0
- package/template/src/components/ui/skeleton.tsx +13 -0
- package/template/src/components/ui/slider.tsx +63 -0
- package/template/src/components/ui/sonner.tsx +38 -0
- package/template/src/components/ui/spinner.tsx +16 -0
- package/template/src/components/ui/switch.tsx +31 -0
- package/template/src/components/ui/table.tsx +114 -0
- package/template/src/components/ui/tabs.tsx +66 -0
- package/template/src/components/ui/textarea.tsx +18 -0
- package/template/src/components/ui/toggle-group.tsx +81 -0
- package/template/src/components/ui/toggle.tsx +45 -0
- package/template/src/components/ui/tooltip.tsx +61 -0
- package/template/src/constants/constants.json +58 -0
- package/template/src/hooks/use-is-mobile.ts +21 -0
- package/template/src/hooks/use-page-title.ts +49 -0
- package/template/src/hooks/use-theme.ts +57 -0
- package/template/src/index.css +128 -0
- package/template/src/lang/en/about.json +4 -0
- package/template/src/lang/en/contact.json +39 -0
- package/template/src/lang/en/cookies.json +4 -0
- package/template/src/lang/en/footer.json +12 -0
- package/template/src/lang/en/forgotPassword.json +37 -0
- package/template/src/lang/en/header.json +10 -0
- package/template/src/lang/en/hero.json +8 -0
- package/template/src/lang/en/index.json +30 -0
- package/template/src/lang/en/login.json +18 -0
- package/template/src/lang/en/notfound.json +7 -0
- package/template/src/lang/en/privacy.json +4 -0
- package/template/src/lang/en/register.json +25 -0
- package/template/src/lang/en/terms.json +4 -0
- package/template/src/lang/index.ts +86 -0
- package/template/src/lang/tr/about.json +4 -0
- package/template/src/lang/tr/contact.json +39 -0
- package/template/src/lang/tr/cookies.json +4 -0
- package/template/src/lang/tr/footer.json +12 -0
- package/template/src/lang/tr/forgotPassword.json +37 -0
- package/template/src/lang/tr/header.json +10 -0
- package/template/src/lang/tr/hero.json +8 -0
- package/template/src/lang/tr/index.json +30 -0
- package/template/src/lang/tr/login.json +18 -0
- package/template/src/lang/tr/notfound.json +7 -0
- package/template/src/lang/tr/privacy.json +4 -0
- package/template/src/lang/tr/register.json +25 -0
- package/template/src/lang/tr/terms.json +4 -0
- package/template/src/lib/api.ts +237 -0
- package/template/src/lib/storage.ts +109 -0
- package/template/src/lib/utils.ts +15 -0
- package/template/src/main.tsx +13 -0
- package/template/src/modules/api/USAGE.md +515 -0
- package/template/src/modules/api/customer-client.ts +20 -0
- package/template/src/modules/api/get-error-message.ts +18 -0
- package/template/src/modules/api/validation/en.json +29 -0
- package/template/src/modules/api/validation/tr.json +29 -0
- package/template/src/modules/auth/USAGE.md +248 -0
- package/template/src/modules/auth/auth-header-menu.tsx +123 -0
- package/template/src/modules/auth/auth-store.ts +57 -0
- package/template/src/modules/auth/forgot-password-page.tsx +371 -0
- package/template/src/modules/auth/login-page.tsx +183 -0
- package/template/src/modules/auth/register-page.tsx +252 -0
- package/template/src/modules/auth/use-auth.ts +273 -0
- package/template/src/modules/db/adapters/IDataAdapter.ts +26 -0
- package/template/src/modules/db/adapters/SqliteAdapter.ts +364 -0
- package/template/src/modules/db/adapters/index.ts +2 -0
- package/template/src/modules/db/config.ts +59 -0
- package/template/src/modules/db/core/DataManager.ts +125 -0
- package/template/src/modules/db/core/types.ts +101 -0
- package/template/src/modules/db/index.ts +42 -0
- package/template/src/modules/db/react/QueryProvider.tsx +16 -0
- package/template/src/modules/db/react/index.ts +23 -0
- package/template/src/modules/db/react/queryClient.ts +64 -0
- package/template/src/modules/db/react/useRepository.ts +400 -0
- package/template/src/modules/db/utils/parsers.ts +96 -0
- package/template/src/pages/Index.tsx +108 -0
- package/template/src/pages/NotFound.tsx +35 -0
- package/template/src/router.tsx +14 -0
- package/template/src/types/index.ts +0 -0
- package/template/src/vite-env.d.ts +1 -0
- package/template/tsconfig.app.json +32 -0
- package/template/tsconfig.json +17 -0
- package/template/tsconfig.node.json +26 -0
- package/template/vite.config.ts +74 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "favorites-ecommerce-block",
|
|
3
|
+
"type": "registry:block",
|
|
4
|
+
"title": "Favorites E-commerce Block",
|
|
5
|
+
"description": "Wishlist/favorites page block showing saved products in grid. Includes empty state with shop CTA, clear all button, and remove from favorites per item.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core",
|
|
8
|
+
"product-card"
|
|
9
|
+
],
|
|
10
|
+
"usage": "import { FavoritesEcommerceBlock } from '@/modules/favorites-ecommerce-block';\n\n<FavoritesEcommerceBlock />\n\n• Uses useFavorites() from ecommerce-core (Zustand)\n• Reads favorites directly from store\n• Empty state: \"No favorites yet\" with shop link",
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "favorites-ecommerce-block/index.ts",
|
|
14
|
+
"type": "registry:index",
|
|
15
|
+
"target": "$modules$/favorites-ecommerce-block/index.ts",
|
|
16
|
+
"content": "export * from './favorites-ecommerce-block';\r\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "favorites-ecommerce-block/favorites-ecommerce-block.tsx",
|
|
20
|
+
"type": "registry:block",
|
|
21
|
+
"target": "$modules$/favorites-ecommerce-block/favorites-ecommerce-block.tsx",
|
|
22
|
+
"content": "import { Link } from \"react-router\";\nimport { Heart, ShoppingBag } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\ninterface FavoritesEcommerceBlockProps {\n favorites: Product[];\n onClearAll?: () => void;\n}\n\nexport function FavoritesEcommerceBlock({\n favorites,\n onClearAll,\n}: FavoritesEcommerceBlockProps) {\n const { t } = useTranslation(\"favorites-ecommerce-block\");\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our products and add items to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t(\"browseProducts\", \"Browse Products\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n // Favorites Grid\n return (\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold text-foreground mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {favorites.length}{\" \"}\n {t(\n \"itemsInFavorites\",\n `item${favorites.length !== 1 ? \"s\" : \"\"} in your favorites`\n )}\n </p>\n </div>\n {onClearAll && (\n <Button variant=\"outline\" onClick={onClearAll}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n )}\n </div>\n\n {/* Products Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {favorites.map((product) => (\n <ProductCard key={product.id} product={product} variant=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "favorites-ecommerce-block/lang/en.json",
|
|
26
|
+
"type": "registry:lang",
|
|
27
|
+
"target": "$modules$/favorites-ecommerce-block/lang/en.json",
|
|
28
|
+
"content": "{\n \"title\": \"My Favorites\",\n \"noFavoritesYet\": \"No Favorites Yet\",\n \"noFavoritesDescription\": \"Start browsing our products and add items to your favorites by clicking the heart icon.\",\n \"clearAll\": \"Clear All\",\n \"itemsInFavorites\": \"items in your favorites\",\n \"browseProducts\": \"Browse Products\"\n}\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "favorites-ecommerce-block/lang/tr.json",
|
|
32
|
+
"type": "registry:lang",
|
|
33
|
+
"target": "$modules$/favorites-ecommerce-block/lang/tr.json",
|
|
34
|
+
"content": "{\n \"title\": \"Favorilerim\",\n \"noFavoritesYet\": \"Henüz Favori Yok\",\n \"noFavoritesDescription\": \"Ürünlerimizi incelemeye başlayın ve kalp simgesine tıklayarak ürünleri favorilerinize ekleyin.\",\n \"clearAll\": \"Tümünü Temizle\",\n \"itemsInFavorites\": \"favorilerinizdeki ürün\",\n \"browseProducts\": \"Ürünleri İncele\"\n}\n"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
"types": [],
|
|
39
|
+
"variables": [
|
|
40
|
+
"FavoritesEcommerceBlock"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "feature-section",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Feature Section",
|
|
5
|
+
"description": "Two-column feature section with image on one side and content on the other. Content includes icon, headline, description text, bullet points with checkmarks, and CTA button. Supports alternating layout (image left/right). Great for explaining product features, services, or process steps.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { FeatureSection } from '@/modules/feature-section';\n\n<FeatureSection imagePosition=\"left\" />\n<FeatureSection imagePosition=\"right\" />\n\n• Installed at: src/modules/feature-section/\n• Customize content: src/modules/feature-section/lang/*.json\n• Props: imagePosition=\"left\" | \"right\"",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "feature-section/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/feature-section/index.ts",
|
|
13
|
+
"content": "export * from './feature-section';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "feature-section/feature-section.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/feature-section/feature-section.tsx",
|
|
19
|
+
"content": "import { Link } from \"react-router\";\nimport { Button } from \"@/components/ui/button\";\nimport { useTranslation } from \"react-i18next\";\n\nexport function FeatureSection() {\n const { t } = useTranslation(\"feature-section\");\n\n return (\n <section className=\"py-20 bg-muted/30\">\n <div className=\"container mx-auto px-4\">\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-12 items-center\">\n <div>\n <h2 className=\"text-3xl lg:text-4xl font-bold mb-6 text-foreground\">\n {t(\"heading\", \"Your Business Title Here\")}\n </h2>\n <p className=\"text-lg text-muted-foreground mb-6\">\n {t(\n \"description\",\n \"This is where your main business description will appear. AI will customize this content based on your specific industry and services.\"\n )}\n </p>\n <div className=\"space-y-4 mb-8\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-2 h-2 bg-primary rounded-full\"></div>\n <span className=\"text-foreground\">\n {t(\"feature1\", \"Key feature or benefit #1\")}\n </span>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"w-2 h-2 bg-primary rounded-full\"></div>\n <span className=\"text-foreground\">\n {t(\"feature2\", \"Key feature or benefit #2\")}\n </span>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"w-2 h-2 bg-primary rounded-full\"></div>\n <span className=\"text-foreground\">\n {t(\"feature3\", \"Key feature or benefit #3\")}\n </span>\n </div>\n </div>\n <div className=\"flex gap-4\">\n <Button asChild>\n <Link to=\"/about\">{t(\"primaryButton\", \"Learn More\")}</Link>\n </Button>\n <Button variant=\"outline\" asChild>\n <Link to=\"/contact\">{t(\"secondaryButton\", \"Get Started\")}</Link>\n </Button>\n </div>\n </div>\n <div>\n <div className=\"aspect-square bg-gradient-to-br from-primary/10 to-primary/5 rounded-2xl overflow-hidden\">\n <img\n src=\"/images/placeholder.png\"\n alt={t(\"imageAlt\", \"Business Solutions\")}\n className=\"w-full h-full object-cover\"\n />\n </div>\n </div>\n </div>\n </div>\n </section>\n );\n}\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "feature-section/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/feature-section/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"heading\": \"Your Site Title Here\",\r\n \"description\": \"This is where your main site description will appear. AI will customize this content based on your specific goals and audience.\",\r\n \"feature1\": \"Key feature or benefit #1\",\r\n \"feature2\": \"Key feature or benefit #2\",\r\n \"feature3\": \"Key feature or benefit #3\",\r\n \"primaryButton\": \"Learn More\",\r\n \"secondaryButton\": \"Get Started\",\r\n \"imageAlt\": \"Site Preview\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "feature-section/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/feature-section/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"heading\": \"Site Başlığınız\",\r\n \"description\": \"Ana site açıklamanız burada görünecek. AI, içeriklerinizi hedef kitlenize ve içeriğinize göre özelleştirecektir.\",\r\n \"feature1\": \"Anahtar özellik veya avantaj #1\",\r\n \"feature2\": \"Anahtar özellik veya avantaj #2\",\r\n \"feature3\": \"Anahtar özellik veya avantaj #3\",\r\n \"primaryButton\": \"Daha Fazla Öğren\",\r\n \"secondaryButton\": \"Başlayın\",\r\n \"imageAlt\": \"Site Önizlemesi\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"FeatureSection"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "featured-products",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Featured Products",
|
|
5
|
+
"description": "Homepage featured products section displaying 4-8 products in a responsive grid. Uses ProductCard component with 'featured' variant showing larger images and sale badges. Includes section title, 'View All' link, and optional carousel mode for mobile. Perfect for showcasing bestsellers or new arrivals.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core",
|
|
8
|
+
"product-card"
|
|
9
|
+
],
|
|
10
|
+
"usage": "import { FeaturedProducts } from '@/modules/featured-products';\n\n<FeaturedProducts />\n\n• Installed at: src/modules/featured-products/\n• Customize content: src/modules/featured-products/lang/*.json\n• Products auto-loaded via useProducts hook",
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "featured-products/index.ts",
|
|
14
|
+
"type": "registry:index",
|
|
15
|
+
"target": "$modules$/featured-products/index.ts",
|
|
16
|
+
"content": "export * from './featured-products';\r\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "featured-products/featured-products.tsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"target": "$modules$/featured-products/featured-products.tsx",
|
|
22
|
+
"content": "import { Link } from \"react-router\";\nimport { ArrowRight } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\ninterface FeaturedProductsProps {\n products: Product[];\n loading?: boolean;\n}\n\nexport function FeaturedProducts({\n products,\n loading = false,\n}: FeaturedProductsProps) {\n const { t } = useTranslation(\"featured-products\");\n\n return (\n <section className=\"py-8 sm:py-12 md:py-16 lg:py-20 bg-background border-t border-border/20 relative\">\n <div className=\"absolute top-0 left-1/2 transform -translate-x-1/2 w-16 sm:w-24 h-px bg-primary/30\"></div>\n <div className=\"container mx-auto px-3 sm:px-4 lg:px-8\">\n <div className=\"text-center mb-6 sm:mb-8 md:mb-12 lg:mb-16 px-2\">\n <h2 className=\"text-xl sm:text-2xl md:text-3xl lg:text-4xl xl:text-5xl font-bold mb-2 sm:mb-3 md:mb-4 bg-gradient-to-r from-primary to-primary/80 bg-clip-text text-transparent leading-normal pb-1\">\n {t('title', 'Featured Products')}\n </h2>\n <div className=\"w-12 sm:w-16 md:w-20 h-1 bg-gradient-to-r from-primary/50 to-primary/20 mx-auto mb-3 sm:mb-4 md:mb-6 rounded-full\"></div>\n <p className=\"text-xs sm:text-sm md:text-base lg:text-lg xl:text-xl text-muted-foreground max-w-2xl mx-auto leading-relaxed\">\n {t('subtitle', 'Hand-picked favorites from our collection')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 lg:gap-8 xl:gap-10\">\n {loading ? (\n [...Array(3)].map((_, i) => (\n <div key={i} className=\"animate-pulse group\">\n <div className=\"aspect-square bg-gradient-to-br from-muted to-muted/50 rounded-2xl mb-6\"></div>\n <div className=\"space-y-3\">\n <div className=\"h-6 bg-muted rounded-lg w-3/4\"></div>\n <div className=\"h-4 bg-muted rounded w-1/2\"></div>\n <div className=\"h-5 bg-muted rounded w-2/3\"></div>\n </div>\n </div>\n ))\n ) : (\n products.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n variant=\"featured\"\n />\n ))\n )}\n </div>\n\n <div className=\"text-center mt-8 sm:mt-12 lg:mt-16\">\n <Button size=\"lg\" asChild className=\"px-6 sm:px-8 py-3 sm:py-4 text-base sm:text-lg\">\n <Link to=\"/products\">\n {t('viewAll', 'View All Products')}\n <ArrowRight className=\"w-4 h-4 sm:w-5 sm:h-5 ml-2\" />\n </Link>\n </Button>\n </div>\n </div>\n </section>\n );\n}\n"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "featured-products/lang/en.json",
|
|
26
|
+
"type": "registry:lang",
|
|
27
|
+
"target": "$modules$/featured-products/lang/en.json",
|
|
28
|
+
"content": "{\r\n \"title\": \"Featured Products\",\r\n \"subtitle\": \"Hand-picked favorites from our collection\",\r\n \"viewAll\": \"View All Products\"\r\n}\r\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "featured-products/lang/tr.json",
|
|
32
|
+
"type": "registry:lang",
|
|
33
|
+
"target": "$modules$/featured-products/lang/tr.json",
|
|
34
|
+
"content": "{\r\n \"title\": \"Öne Çıkan Ürünler\",\r\n \"subtitle\": \"Koleksiyonumuzdan özenle seçilmiş favoriler\",\r\n \"viewAll\": \"Tüm Ürünleri Gör\"\r\n}\r\n"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
"types": [],
|
|
39
|
+
"variables": [
|
|
40
|
+
"FeaturedProducts"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "footer-detailed",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Detailed Footer",
|
|
5
|
+
"description": "Multi-column footer with logo, tagline, social links, and organized link sections. Features company and legal columns with responsive grid layout. Perfect for corporate and SaaS websites.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"lucide-react"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [],
|
|
10
|
+
"usage": "import { FooterDetailed } from '@/modules/footer-detailed';\n\n<FooterDetailed />\n\n- Logo and tagline area\n- Social media links\n- Company and legal link sections\n- Responsive grid layout",
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "footer-detailed/index.ts",
|
|
14
|
+
"type": "registry:index",
|
|
15
|
+
"target": "$modules$/footer-detailed/index.ts",
|
|
16
|
+
"content": "export * from './footer-detailed';\r\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "footer-detailed/footer-detailed.tsx",
|
|
20
|
+
"type": "registry:component",
|
|
21
|
+
"target": "$modules$/footer-detailed/footer-detailed.tsx",
|
|
22
|
+
"content": "import { useState, useMemo } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport {\r\n Facebook,\r\n Twitter,\r\n Instagram,\r\n Linkedin,\r\n Youtube,\r\n Mail,\r\n Phone,\r\n ArrowUp,\r\n Send,\r\n CreditCard,\r\n} from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\nconst socialIcons: Record<string, React.ElementType> = {\r\n facebook: Facebook,\r\n twitter: Twitter,\r\n instagram: Instagram,\r\n linkedin: Linkedin,\r\n youtube: Youtube,\r\n};\r\n\r\ninterface FooterDetailedProps {\r\n className?: string;\r\n}\r\n\r\nexport function FooterDetailed({ className }: FooterDetailedProps) {\r\n const { t } = useTranslation(\"footer-detailed\");\r\n const [email, setEmail] = useState(\"\");\r\n\r\n const socialLinks = useMemo(() => {\r\n const socialMedia = constants.socialMedia as Record<string, string> | undefined;\r\n if (!socialMedia) return [];\r\n return Object.entries(socialMedia)\r\n .filter(([platform, url]) => url && socialIcons[platform])\r\n .map(([platform, url]) => ({ platform, url, Icon: socialIcons[platform] }));\r\n }, []);\r\n\r\n const handleNewsletterSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n // Newsletter subscription logic\r\n console.log(\"Newsletter subscription:\", email);\r\n setEmail(\"\");\r\n };\r\n\r\n const scrollToTop = () => {\r\n window.scrollTo({ top: 0, behavior: \"smooth\" });\r\n };\r\n\r\n const currentYear = new Date().getFullYear();\r\n\r\n const linkSections = [\r\n {\r\n title: t(\"shop\", \"Shop\"),\r\n links: [\r\n { text: t(\"allProducts\", \"All Products\"), url: \"/products\" },\r\n { text: t(\"featured\", \"Featured\"), url: \"/products?featured=true\" },\r\n { text: t(\"newArrivals\", \"New Arrivals\"), url: \"/products?new=true\" },\r\n { text: t(\"onSale\", \"On Sale\"), url: \"/products?sale=true\" },\r\n ],\r\n },\r\n {\r\n title: t(\"company\", \"Company\"),\r\n links: [\r\n { text: t(\"about\", \"About Us\"), url: \"/about\" },\r\n { text: t(\"careers\", \"Careers\"), url: \"/careers\" },\r\n { text: t(\"blog\", \"Blog\"), url: \"/blog\" },\r\n { text: t(\"press\", \"Press\"), url: \"/press\" },\r\n ],\r\n },\r\n {\r\n title: t(\"support\", \"Support\"),\r\n links: [\r\n { text: t(\"helpCenter\", \"Help Center\"), url: \"/help\" },\r\n { text: t(\"faq\", \"FAQ\"), url: \"/faq\" },\r\n { text: t(\"contact\", \"Contact Us\"), url: \"/contact\" },\r\n { text: t(\"returns\", \"Returns\"), url: \"/returns\" },\r\n ],\r\n },\r\n {\r\n title: t(\"legal\", \"Legal\"),\r\n links: [\r\n { text: t(\"privacy\", \"Privacy Policy\"), url: \"/privacy\" },\r\n { text: t(\"terms\", \"Terms of Service\"), url: \"/terms\" },\r\n { text: t(\"cookies\", \"Cookie Policy\"), url: \"/cookies\" },\r\n { text: t(\"refund\", \"Refund Policy\"), url: \"/refund\" },\r\n ],\r\n },\r\n ];\r\n\r\n return (\r\n <footer className={cn(\"border-t bg-muted/30\", className)}>\r\n {/* Main Footer */}\r\n <div className=\"container mx-auto px-4 py-12 lg:py-16\">\r\n <div className=\"grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-6\">\r\n {/* Brand & Newsletter - spans 2 columns */}\r\n <div className=\"sm:col-span-2 space-y-6\">\r\n <Logo size=\"lg\" />\r\n <p className=\"text-sm text-muted-foreground leading-relaxed max-w-sm\">\r\n {t(\"description\", \"Your trusted destination for quality products. We deliver excellence with every order.\")}\r\n </p>\r\n\r\n {/* Newsletter */}\r\n <div className=\"space-y-3\">\r\n <h4 className=\"font-semibold text-sm\">\r\n {t(\"newsletter\", \"Subscribe to our newsletter\")}\r\n </h4>\r\n <form onSubmit={handleNewsletterSubmit} className=\"flex gap-2\">\r\n <Input\r\n type=\"email\"\r\n placeholder={t(\"emailPlaceholder\", \"Enter your email\")}\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n className=\"flex-1 h-10\"\r\n required\r\n />\r\n <Button type=\"submit\" size=\"sm\" className=\"h-10 px-4\">\r\n <Send className=\"h-4 w-4\" />\r\n </Button>\r\n </form>\r\n <p className=\"text-xs text-muted-foreground\">\r\n {t(\"newsletterNote\", \"Get updates on new products and exclusive offers.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Social Links */}\r\n {socialLinks.length > 0 && (\r\n <div className=\"flex gap-1 pt-2\">\r\n {socialLinks.map(({ platform, url, Icon }) => (\r\n <a\r\n key={platform}\r\n href={url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"h-10 w-10 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\"\r\n >\r\n <Icon className=\"h-5 w-5\" />\r\n </a>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Link Sections */}\r\n {linkSections.map((section, idx) => (\r\n <div key={idx} className=\"space-y-4\">\r\n <h4 className=\"font-semibold\">{section.title}</h4>\r\n <ul className=\"space-y-2.5\">\r\n {section.links.map((link, linkIdx) => (\r\n <li key={linkIdx}>\r\n <Link\r\n to={link.url}\r\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\r\n >\r\n {link.text}\r\n </Link>\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Bottom Bar */}\r\n <div className=\"border-t bg-muted/50\">\r\n <div className=\"container mx-auto px-4 py-6\">\r\n <div className=\"flex flex-col lg:flex-row justify-between items-center gap-6\">\r\n {/* Copyright & Contact */}\r\n <div className=\"flex flex-col sm:flex-row items-center gap-4 sm:gap-6 text-sm text-muted-foreground\">\r\n <span>© {currentYear} {constants.site.name}. {t(\"allRightsReserved\", \"All rights reserved.\")}</span>\r\n <div className=\"hidden sm:block w-px h-4 bg-border\" />\r\n <div className=\"flex items-center gap-4\">\r\n <a href={`tel:${constants.phone}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\r\n <Phone className=\"h-3.5 w-3.5\" />\r\n <span>{constants.phone}</span>\r\n </a>\r\n <a href={`mailto:${constants.email}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\r\n <Mail className=\"h-3.5 w-3.5\" />\r\n <span>{constants.email}</span>\r\n </a>\r\n </div>\r\n </div>\r\n\r\n {/* Payment & Back to Top */}\r\n <div className=\"flex items-center gap-6\">\r\n {/* Payment Methods */}\r\n <div className=\"flex items-center gap-2 text-muted-foreground\">\r\n <CreditCard className=\"h-5 w-5\" />\r\n <span className=\"text-xs\">{t(\"securePayment\", \"Secure Payment\")}</span>\r\n </div>\r\n\r\n {/* Back to Top */}\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={scrollToTop}\r\n className=\"gap-2\"\r\n >\r\n <ArrowUp className=\"h-4 w-4\" />\r\n {t(\"backToTop\", \"Top\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </footer>\r\n );\r\n}\r\n"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "footer-detailed/lang/en.json",
|
|
26
|
+
"type": "registry:lang",
|
|
27
|
+
"target": "$modules$/footer-detailed/lang/en.json",
|
|
28
|
+
"content": "{\r\n \"description\": \"Your trusted destination for quality products. We deliver excellence with every order.\",\r\n \"newsletter\": \"Subscribe to our newsletter\",\r\n \"emailPlaceholder\": \"Enter your email\",\r\n \"newsletterNote\": \"Get updates on new products and exclusive offers.\",\r\n \"shop\": \"Shop\",\r\n \"allProducts\": \"All Products\",\r\n \"featured\": \"Featured\",\r\n \"newArrivals\": \"New Arrivals\",\r\n \"onSale\": \"On Sale\",\r\n \"company\": \"Company\",\r\n \"about\": \"About Us\",\r\n \"careers\": \"Careers\",\r\n \"blog\": \"Blog\",\r\n \"press\": \"Press\",\r\n \"support\": \"Support\",\r\n \"helpCenter\": \"Help Center\",\r\n \"faq\": \"FAQ\",\r\n \"contact\": \"Contact Us\",\r\n \"returns\": \"Returns\",\r\n \"legal\": \"Legal\",\r\n \"privacy\": \"Privacy Policy\",\r\n \"terms\": \"Terms of Service\",\r\n \"cookies\": \"Cookie Policy\",\r\n \"refund\": \"Refund Policy\",\r\n \"securePayment\": \"Secure Payment\",\r\n \"backToTop\": \"Top\",\r\n \"allRightsReserved\": \"All rights reserved.\"\r\n}\r\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "footer-detailed/lang/tr.json",
|
|
32
|
+
"type": "registry:lang",
|
|
33
|
+
"target": "$modules$/footer-detailed/lang/tr.json",
|
|
34
|
+
"content": "{\r\n \"description\": \"Kaliteli ürünler için güvenilir adresiniz. Her siparişte mükemmellik sunuyoruz.\",\r\n \"newsletter\": \"Bültenimize abone olun\",\r\n \"emailPlaceholder\": \"E-posta adresiniz\",\r\n \"newsletterNote\": \"Yeni ürünler ve özel tekliflerden haberdar olun.\",\r\n \"shop\": \"Mağaza\",\r\n \"allProducts\": \"Tüm Ürünler\",\r\n \"featured\": \"Öne Çıkanlar\",\r\n \"newArrivals\": \"Yeni Gelenler\",\r\n \"onSale\": \"İndirimli\",\r\n \"company\": \"Şirket\",\r\n \"about\": \"Hakkımızda\",\r\n \"careers\": \"Kariyer\",\r\n \"blog\": \"Blog\",\r\n \"press\": \"Basın\",\r\n \"support\": \"Destek\",\r\n \"helpCenter\": \"Yardım Merkezi\",\r\n \"faq\": \"SSS\",\r\n \"contact\": \"İletişim\",\r\n \"returns\": \"İade\",\r\n \"legal\": \"Yasal\",\r\n \"privacy\": \"Gizlilik Politikası\",\r\n \"terms\": \"Kullanım Şartları\",\r\n \"cookies\": \"Çerez Politikası\",\r\n \"refund\": \"İade Politikası\",\r\n \"securePayment\": \"Güvenli Ödeme\",\r\n \"backToTop\": \"Yukarı\",\r\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\r\n}\r\n"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
"types": [],
|
|
39
|
+
"variables": [
|
|
40
|
+
"FooterDetailed"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "footer-minimal",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Minimal Footer",
|
|
5
|
+
"description": "Clean, centered minimal footer perfect for blogs, portfolios, and landing pages. Features centered logo, single row navigation links, social media icons, and copyright text. Compact design with subtle separator line. Fully responsive with proper spacing on mobile.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { Footer } from '@/modules/footer-minimal';\n\n<Footer />\n\n• Installed at: src/modules/footer-minimal/\n• Customize content: src/modules/footer-minimal/lang/*.json\n• Minimal design for simple sites",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "footer-minimal/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/footer-minimal/index.ts",
|
|
13
|
+
"content": "export * from './footer-minimal';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "footer-minimal/footer-minimal.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/footer-minimal/footer-minimal.tsx",
|
|
19
|
+
"content": "import { useMemo } from \"react\";\nimport { Link } from \"react-router\";\nimport { Facebook, Twitter, Instagram, Linkedin } from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { useTranslation } from \"react-i18next\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function FooterMinimal() {\n const { t } = useTranslation(\"footer-minimal\");\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as Record<string, string> | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({ platform, url, Icon: socialIcons[platform] }));\n }, []);\n\n const currentYear = new Date().getFullYear();\n\n return (\n <footer className=\"border-t bg-muted/20\">\n <div className=\"container mx-auto px-4 py-6\">\n <div className=\"flex flex-col md:flex-row items-center justify-between gap-4\">\n {/* Copyright */}\n <p className=\"text-sm text-muted-foreground\">\n © {currentYear} {constants.site.name}\n </p>\n\n {/* Legal Links */}\n <div className=\"flex items-center gap-6\">\n <Link to=\"/privacy\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"privacy\", \"Privacy\")}\n </Link>\n <Link to=\"/terms\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"terms\", \"Terms\")}\n </Link>\n </div>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"flex gap-1\">\n {socialLinks.map(({ platform, url, Icon }) => (\n <a\n key={platform}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"h-8 w-8 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n )}\n </div>\n </div>\n </footer>\n );\n}\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "footer-minimal/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/footer-minimal/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"privacy\": \"Privacy\",\r\n \"terms\": \"Terms\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "footer-minimal/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/footer-minimal/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"privacy\": \"Gizlilik\",\r\n \"terms\": \"Şartlar\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"FooterMinimal"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "footer",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Full Footer",
|
|
5
|
+
"description": "Comprehensive 5-column footer for e-commerce and business sites. Includes company info with logo and description, product links, company links (About, Careers, Press), support links (Contact, FAQ, Shipping), and newsletter signup form. Bottom bar with copyright, payment icons, and social media links. Fully responsive with stacked mobile layout.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { Footer } from '@/modules/footer';\n\n<Footer />\n\n• Installed at: src/modules/footer/\n• Customize content: src/modules/footer/lang/*.json\n• Update social links in component",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "footer/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/footer/index.ts",
|
|
13
|
+
"content": "export * from './footer';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "footer/footer.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/footer/footer.tsx",
|
|
19
|
+
"content": "import { useMemo } from \"react\";\nimport { Link } from \"react-router\";\nimport { Mail, Phone, MapPin, Facebook, Twitter, Instagram, Linkedin } from \"lucide-react\";\nimport { Logo } from \"@/components/Logo\";\nimport constants from \"@/constants/constants.json\";\nimport { useTranslation } from \"react-i18next\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function Footer() {\n const { t } = useTranslation(\"footer\");\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as Record<string, string> | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({ platform, url, Icon: socialIcons[platform] }));\n }, []);\n\n const currentYear = new Date().getFullYear();\n\n return (\n <footer className=\"border-t bg-muted/20\">\n <div className=\"container mx-auto px-4 py-12 lg:py-16\">\n <div className=\"grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-4\">\n {/* Brand */}\n <div className=\"space-y-4\">\n <Logo size=\"md\" />\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n {t(\"description\", \"Quality products and exceptional service for our valued customers.\")}\n </p>\n {socialLinks.length > 0 && (\n <div className=\"flex gap-1\">\n {socialLinks.map(({ platform, url, Icon }) => (\n <a\n key={platform}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"h-9 w-9 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n )}\n </div>\n\n {/* Quick Links */}\n <div className=\"space-y-4\">\n <h4 className=\"font-semibold\">{t(\"quickLinks\", \"Quick Links\")}</h4>\n <ul className=\"space-y-2.5\">\n <li>\n <Link to=\"/about\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"about\", \"About\")}\n </Link>\n </li>\n <li>\n <Link to=\"/contact\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"contact\", \"Contact\")}\n </Link>\n </li>\n </ul>\n </div>\n\n {/* Legal */}\n <div className=\"space-y-4\">\n <h4 className=\"font-semibold\">{t(\"legal\", \"Legal\")}</h4>\n <ul className=\"space-y-2.5\">\n <li>\n <Link to=\"/privacy\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"privacy\", \"Privacy Policy\")}\n </Link>\n </li>\n <li>\n <Link to=\"/terms\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"terms\", \"Terms of Service\")}\n </Link>\n </li>\n <li>\n <Link to=\"/cookies\" className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\">\n {t(\"cookies\", \"Cookie Policy\")}\n </Link>\n </li>\n </ul>\n </div>\n\n {/* Contact */}\n <div className=\"space-y-4\">\n <h4 className=\"font-semibold\">{t(\"contactTitle\", \"Contact\")}</h4>\n <ul className=\"space-y-3\">\n <li className=\"flex items-center gap-3 text-sm text-muted-foreground\">\n <Phone className=\"h-4 w-4 shrink-0\" />\n <span>{constants.phone}</span>\n </li>\n <li className=\"flex items-center gap-3 text-sm text-muted-foreground\">\n <Mail className=\"h-4 w-4 shrink-0\" />\n <span>{constants.email}</span>\n </li>\n <li className=\"flex items-start gap-3 text-sm text-muted-foreground\">\n <MapPin className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>\n {constants.address.city}, {constants.address.state}\n </span>\n </li>\n </ul>\n </div>\n </div>\n\n {/* Bottom */}\n <div className=\"mt-12 pt-6 border-t\">\n <p className=\"text-sm text-muted-foreground text-center\">\n © {currentYear} {constants.site.name}. {t(\"allRightsReserved\", \"All rights reserved.\")}\n </p>\n </div>\n </div>\n </footer>\n );\n}\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "footer/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/footer/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"description\": \"AI will customize this footer description based on your site's purpose and brand.\",\r\n \"quickLinks\": \"Quick Links\",\r\n \"about\": \"About\",\r\n \"contact\": \"Contact\",\r\n \"legal\": \"Legal\",\r\n \"privacy\": \"Privacy Policy\",\r\n \"terms\": \"Terms of Service\",\r\n \"cookies\": \"Cookie Policy\",\r\n \"contactTitle\": \"Contact\",\r\n \"allRightsReserved\": \"All rights reserved.\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "footer/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/footer/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"description\": \"AI bu footer açıklamasını sitenizin amacı ve markasına göre özelleştirecektir.\",\r\n \"quickLinks\": \"Hızlı Bağlantılar\",\r\n \"about\": \"Hakkımızda\",\r\n \"contact\": \"İletişim\",\r\n \"legal\": \"Yasal\",\r\n \"privacy\": \"Gizlilik Politikası\",\r\n \"terms\": \"Kullanım Şartları\",\r\n \"cookies\": \"Çerez Politikası\",\r\n \"contactTitle\": \"İletişim\",\r\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"Footer"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "google-map",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Google Map",
|
|
5
|
+
"description": "Embeddable Google Map component for showing locations. Supports optional API key, custom zoom level, and responsive design. Perfect for contact pages and location displays.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { GoogleMap } from '@/modules/google-map';\n\n<GoogleMap latitude={41.0082} longitude={28.9784} />\n\n// With all options\n<GoogleMap\n latitude={41.0082}\n longitude={28.9784}\n zoom={14}\n height=\"500px\"\n title=\"Office Location\"\n apiKey={import.meta.env.VITE_GOOGLE_MAPS_KEY}\n/>",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "google-map/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/google-map/index.ts",
|
|
13
|
+
"content": "export * from './google-map';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "google-map/google-map.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/google-map/google-map.tsx",
|
|
19
|
+
"content": "import { useState } from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { MapPin } from \"lucide-react\";\r\n\r\ninterface GoogleMapProps {\r\n latitude: number;\r\n longitude: number;\r\n zoom?: number;\r\n apiKey?: string;\r\n height?: string;\r\n className?: string;\r\n title?: string;\r\n}\r\n\r\nexport function GoogleMap({\r\n latitude,\r\n longitude,\r\n zoom = 15,\r\n apiKey,\r\n height = \"400px\",\r\n className,\r\n title,\r\n}: GoogleMapProps) {\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [hasError, setHasError] = useState(false);\r\n\r\n // Build the embed URL\r\n const buildMapUrl = () => {\r\n const baseUrl = apiKey\r\n ? \"https://www.google.com/maps/embed/v1/place\"\r\n : \"https://maps.google.com/maps\";\r\n\r\n if (apiKey) {\r\n // With API key - use official Embed API\r\n const params = new URLSearchParams({\r\n key: apiKey,\r\n q: `${latitude},${longitude}`,\r\n zoom: zoom.toString(),\r\n });\r\n return `${baseUrl}?${params.toString()}`;\r\n } else {\r\n // Without API key - use basic embed\r\n const params = new URLSearchParams({\r\n q: `${latitude},${longitude}`,\r\n z: zoom.toString(),\r\n output: \"embed\",\r\n });\r\n return `${baseUrl}?${params.toString()}`;\r\n }\r\n };\r\n\r\n const handleLoad = () => {\r\n setIsLoading(false);\r\n };\r\n\r\n const handleError = () => {\r\n setIsLoading(false);\r\n setHasError(true);\r\n };\r\n\r\n if (hasError) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center justify-center bg-muted rounded-lg border\",\r\n className\r\n )}\r\n style={{ height }}\r\n >\r\n <MapPin className=\"h-12 w-12 text-muted-foreground mb-3\" />\r\n <p className=\"text-sm text-muted-foreground\">Failed to load map</p>\r\n <a\r\n href={`https://www.google.com/maps?q=${latitude},${longitude}`}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-sm text-primary hover:underline mt-2\"\r\n >\r\n Open in Google Maps\r\n </a>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n className={cn(\"relative rounded-lg overflow-hidden border\", className)}\r\n style={{ height }}\r\n >\r\n {isLoading && (\r\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted\">\r\n <div className=\"flex flex-col items-center gap-3\">\r\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\r\n <span className=\"text-sm text-muted-foreground\">Loading map...</span>\r\n </div>\r\n </div>\r\n )}\r\n <iframe\r\n src={buildMapUrl()}\r\n width=\"100%\"\r\n height=\"100%\"\r\n style={{ border: 0 }}\r\n allowFullScreen\r\n loading=\"lazy\"\r\n referrerPolicy=\"no-referrer-when-downgrade\"\r\n title={title || `Map location: ${latitude}, ${longitude}`}\r\n onLoad={handleLoad}\r\n onError={handleError}\r\n className={cn(isLoading && \"invisible\")}\r\n />\r\n </div>\r\n );\r\n}\r\n"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"envVars": {
|
|
23
|
+
"VITE_GOOGLE_MAPS_KEY": ""
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"types": [],
|
|
27
|
+
"variables": [
|
|
28
|
+
"GoogleMap"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "header-centered-pill",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Centered Pill Header",
|
|
5
|
+
"description": "Modern pill-shaped header with centered navigation. Features rounded border design, navigation links, CTA button, and mobile hamburger menu. Clean minimal aesthetic perfect for landing pages and SaaS sites.",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"lucide-react"
|
|
8
|
+
],
|
|
9
|
+
"registryDependencies": [
|
|
10
|
+
"sheet"
|
|
11
|
+
],
|
|
12
|
+
"usage": "import { HeaderCenteredPill } from '@/modules/header-centered-pill';\n\n<HeaderCenteredPill />\n\n• Installed at: src/modules/header-centered-pill/\n• Customize navigation: src/modules/header-centered-pill/lang/*.json\n• Pill-shaped design with max-w-2xl centered container",
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "header-centered-pill/index.ts",
|
|
16
|
+
"type": "registry:index",
|
|
17
|
+
"target": "$modules$/header-centered-pill/index.ts",
|
|
18
|
+
"content": "export * from './header-centered-pill';\r\n"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"path": "header-centered-pill/header-centered-pill.tsx",
|
|
22
|
+
"type": "registry:component",
|
|
23
|
+
"target": "$modules$/header-centered-pill/header-centered-pill.tsx",
|
|
24
|
+
"content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { Menu } from \"lucide-react\";\r\nimport { Button, buttonVariants } from \"@/components/ui/button\";\r\nimport {\r\n Sheet,\r\n SheetHeader,\r\n SheetTitle,\r\n SheetContent,\r\n SheetTrigger,\r\n} from \"@/components/ui/sheet\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { useTranslation } from \"react-i18next\";\r\n\r\nexport function HeaderCenteredPill() {\r\n const [mobileMenuOpen, setMobileMenuOpen] = useState(false);\r\n const { t } = useTranslation(\"header-centered-pill\");\r\n\r\n const navigation = [\r\n { name: t(\"features\", \"Features\"), href: \"/features\" },\r\n { name: t(\"pricing\", \"Pricing\"), href: \"/pricing\" },\r\n { name: t(\"blog\", \"Blog\"), href: \"/blog\" },\r\n { name: t(\"company\", \"Company\"), href: \"/about\" },\r\n { name: t(\"signIn\", \"Sign in\"), href: \"/login\" },\r\n ];\r\n\r\n return (\r\n <header className=\"p-4\">\r\n <div className=\"mx-auto flex max-w-2xl items-center justify-between space-x-4 rounded-full border bg-background p-1.5 pl-4\">\r\n <Link to=\"/\">\r\n <Logo size=\"sm\" />\r\n </Link>\r\n\r\n {/* Desktop Navigation */}\r\n <div className=\"hidden md:inline-flex\">\r\n {navigation.map((item) => (\r\n <Link\r\n key={item.name}\r\n to={item.href}\r\n className={buttonVariants({ variant: \"ghost\", size: \"sm\" })}\r\n >\r\n {item.name}\r\n </Link>\r\n ))}\r\n </div>\r\n\r\n {/* Desktop CTA */}\r\n <div className=\"hidden md:inline-flex\">\r\n <Link\r\n to=\"/register\"\r\n className={cn(buttonVariants({ size: \"sm\" }), \"rounded-full\")}\r\n >\r\n {t(\"getStarted\", \"Get Started\")}\r\n </Link>\r\n </div>\r\n\r\n {/* Mobile Menu */}\r\n <div className=\"md:hidden\">\r\n <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>\r\n <SheetTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon\" className=\"rounded-full\">\r\n <Menu className=\"h-5 w-5\" />\r\n <span className=\"sr-only\">{t(\"menu\", \"Menu\")}</span>\r\n </Button>\r\n </SheetTrigger>\r\n <SheetContent side=\"right\" className=\"w-[300px] px-6\">\r\n <SheetHeader>\r\n <SheetTitle>{t(\"menu\", \"Menu\")}</SheetTitle>\r\n </SheetHeader>\r\n <div className=\"flex flex-col space-y-4 mt-8\">\r\n {navigation.map((item) => (\r\n <Link\r\n key={item.name}\r\n to={item.href}\r\n className=\"text-lg font-medium hover:text-primary transition-colors\"\r\n onClick={() => setMobileMenuOpen(false)}\r\n >\r\n {item.name}\r\n </Link>\r\n ))}\r\n <Link\r\n to=\"/register\"\r\n className={cn(buttonVariants(), \"rounded-full mt-4\")}\r\n onClick={() => setMobileMenuOpen(false)}\r\n >\r\n {t(\"getStarted\", \"Get Started\")}\r\n </Link>\r\n </div>\r\n </SheetContent>\r\n </Sheet>\r\n </div>\r\n </div>\r\n </header>\r\n );\r\n}\r\n"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": "header-centered-pill/lang/en.json",
|
|
28
|
+
"type": "registry:lang",
|
|
29
|
+
"target": "$modules$/header-centered-pill/lang/en.json",
|
|
30
|
+
"content": "{\r\n \"menu\": \"Menu\",\r\n \"features\": \"Features\",\r\n \"pricing\": \"Pricing\",\r\n \"blog\": \"Blog\",\r\n \"company\": \"Company\",\r\n \"signIn\": \"Sign in\",\r\n \"getStarted\": \"Get Started\"\r\n}\r\n"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"path": "header-centered-pill/lang/tr.json",
|
|
34
|
+
"type": "registry:lang",
|
|
35
|
+
"target": "$modules$/header-centered-pill/lang/tr.json",
|
|
36
|
+
"content": "{\r\n \"menu\": \"Menü\",\r\n \"features\": \"Özellikler\",\r\n \"pricing\": \"Fiyatlandırma\",\r\n \"blog\": \"Blog\",\r\n \"company\": \"Şirket\",\r\n \"signIn\": \"Giriş Yap\",\r\n \"getStarted\": \"Başla\"\r\n}\r\n"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"exports": {
|
|
40
|
+
"types": [],
|
|
41
|
+
"variables": [
|
|
42
|
+
"HeaderCenteredPill"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "header-ecommerce",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "E-commerce Header",
|
|
5
|
+
"description": "Full-featured e-commerce header with logo, main navigation, search bar with autocomplete dropdown, favorites icon with count badge, shopping cart icon with item count, user account dropdown menu, and mobile hamburger menu. Includes sticky positioning, language switcher, and responsive breakpoints. Dark mode support included.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core"
|
|
8
|
+
],
|
|
9
|
+
"usage": "import { Header } from '@/modules/header-ecommerce';\n\n<Header />\n\n• Component is installed at: src/modules/header-ecommerce/\n• Customize navigation links in: src/modules/header-ecommerce/lang/*.json\n• Uses useCart and useFavorites from ecommerce-core for badges\n• Includes search, cart, favorites, and mobile menu",
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "header-ecommerce/index.ts",
|
|
13
|
+
"type": "registry:index",
|
|
14
|
+
"target": "$modules$/header-ecommerce/index.ts",
|
|
15
|
+
"content": "export * from './header-ecommerce';\r\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "header-ecommerce/header-ecommerce.tsx",
|
|
19
|
+
"type": "registry:component",
|
|
20
|
+
"target": "$modules$/header-ecommerce/header-ecommerce.tsx",
|
|
21
|
+
"content": "import { useState } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\nimport { ShoppingCart, Menu, Search, Heart, Package } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Badge } from \"@/components/ui/badge\";\nimport {\n Sheet,\n SheetHeader,\n SheetTitle,\n SheetContent,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { DropdownMenuItem } from \"@/components/ui/dropdown-menu\";\nimport { Logo } from \"@/components/Logo\";\nimport { AuthHeaderMenu } from \"@/modules/auth/auth-header-menu\";\nimport { useTranslation } from \"react-i18next\";\nimport constants from \"@/constants/constants.json\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\nimport {\n useCart,\n useFavorites,\n useSearch,\n formatPrice,\n} from \"@/modules/ecommerce-core\";\n\nexport function HeaderEcommerce() {\n const [mobileMenuOpen, setMobileMenuOpen] = useState(false);\n const [mobileSearchOpen, setMobileSearchOpen] = useState(false);\n const [desktopSearchOpen, setDesktopSearchOpen] = useState(false);\n const [showResults, setShowResults] = useState(false);\n const { itemCount, state } = useCart();\n const { favoriteCount } = useFavorites();\n const navigate = useNavigate();\n const { t } = useTranslation(\"header-ecommerce\");\n\n const {\n searchTerm,\n setSearchTerm,\n results: searchResults,\n clearSearch,\n } = useSearch();\n\n const handleSearchSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (searchTerm.trim()) {\n navigate(`/products?search=${encodeURIComponent(searchTerm)}`);\n setShowResults(false);\n setDesktopSearchOpen(false);\n clearSearch();\n }\n };\n\n const handleSearchFocus = () => {\n setShowResults(true);\n };\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearchTerm(e.target.value);\n setShowResults(true);\n };\n\n const navigation = [\n { name: t(\"home\"), href: \"/\" },\n { name: t(\"products\"), href: \"/products\" },\n { name: t(\"about\"), href: \"/about\" },\n { name: t(\"contact\"), href: \"/contact\" },\n ];\n\n return (\n <header className=\"sticky top-0 z-50 w-full border-b border-border/20 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60\">\n <div className=\"container mx-auto px-3 sm:px-4 lg:px-8\">\n <div className=\"flex h-14 sm:h-16 md:h-20 items-center justify-between gap-2\">\n {/* Logo */}\n <div className=\"flex-shrink-0 min-w-0\">\n <Logo size=\"sm\" className=\"text-base sm:text-xl lg:text-2xl\" />\n </div>\n\n {/* Desktop Navigation - Centered */}\n <nav className=\"hidden lg:flex items-center space-x-12 absolute left-1/2 transform -translate-x-1/2\">\n {navigation.map((item) => (\n <Link\n key={item.name}\n to={item.href}\n className=\"text-base font-medium transition-colors hover:text-primary relative group py-2\"\n >\n {item.name}\n <span className=\"absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full\"></span>\n </Link>\n ))}\n </nav>\n\n {/* Search & Actions - Right Aligned */}\n <div className=\"flex items-center space-x-1 sm:space-x-2 lg:space-x-4 flex-shrink-0\">\n {/* Desktop Search - Modal */}\n <Dialog\n open={desktopSearchOpen}\n onOpenChange={setDesktopSearchOpen}\n >\n <DialogTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"hidden lg:flex h-10 w-10\"\n >\n <Search className=\"h-5 w-5\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>\n {t(\"searchProducts\", \"Search Products\")}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <form onSubmit={handleSearchSubmit}>\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-5 w-5\" />\n <Input\n type=\"search\"\n placeholder={t(\n \"searchPlaceholder\",\n \"Search for products...\"\n )}\n value={searchTerm}\n onChange={handleSearchChange}\n className=\"pl-11 h-12 text-base\"\n autoFocus\n />\n </div>\n </form>\n\n {/* Desktop Search Results */}\n {searchTerm.trim() && (\n <div className=\"max-h-[400px] overflow-y-auto rounded-lg border bg-card\">\n {searchResults.length > 0 ? (\n <div className=\"divide-y\">\n <div className=\"px-4 py-3 bg-muted/50\">\n <p className=\"text-sm font-medium text-muted-foreground\">\n {searchResults.length}{\" \"}\n {searchResults.length === 1\n ? \"result\"\n : \"results\"}{\" \"}\n found\n </p>\n </div>\n {searchResults.slice(0, 8).map((product: Product) => (\n <Link\n key={product.id}\n to={`/products/${product.slug}`}\n onClick={() => {\n setDesktopSearchOpen(false);\n clearSearch();\n }}\n className=\"flex items-center gap-4 p-4 hover:bg-muted/50 transition-colors\"\n >\n <img\n src={\n product.images[0] || \"/images/placeholder.png\"\n }\n alt={product.name}\n className=\"w-16 h-16 object-cover rounded flex-shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-base line-clamp-1\">\n {product.name}\n </h4>\n <p className=\"text-sm text-muted-foreground capitalize\">\n {product.category}\n </p>\n <p className=\"text-base font-semibold text-primary mt-1\">\n {formatPrice(\n product.price,\n constants.site.currency\n )}\n </p>\n </div>\n </Link>\n ))}\n {searchResults.length > 8 && (\n <div className=\"px-4 py-3 bg-muted/30 text-center\">\n <button\n onClick={() => {\n navigate(\n `/products?search=${encodeURIComponent(\n searchTerm\n )}`\n );\n setDesktopSearchOpen(false);\n clearSearch();\n }}\n className=\"text-sm font-medium text-primary hover:underline\"\n >\n {t(\n \"viewAllResults\",\n `View all ${searchResults.length} results`\n )}\n </button>\n </div>\n )}\n </div>\n ) : (\n <div className=\"p-8 text-center\">\n <Search className=\"h-12 w-12 text-muted-foreground mx-auto mb-3 opacity-50\" />\n <p className=\"text-base text-muted-foreground\">\n {t(\"noResults\", \"No products found\")}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\n \"tryDifferentKeywords\",\n \"Try different keywords\"\n )}\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Search - Mobile (Hidden - moved to hamburger menu) */}\n <Dialog open={mobileSearchOpen} onOpenChange={setMobileSearchOpen}>\n <DialogTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"hidden\">\n <Search className=\"h-4 w-4 sm:h-5 sm:w-5\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{t(\"searchProducts\")}</DialogTitle>\n </DialogHeader>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n if (searchTerm.trim()) {\n navigate(\n `/products?search=${encodeURIComponent(searchTerm)}`\n );\n setMobileSearchOpen(false);\n clearSearch();\n }\n }}\n className=\"space-y-4\"\n >\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4\" />\n <Input\n type=\"search\"\n placeholder={t(\"searchPlaceholder\")}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n className=\"pl-10\"\n autoFocus\n />\n </div>\n <div className=\"flex gap-2\">\n <Button type=\"submit\" className=\"flex-1\">\n {t(\"searchButton\", \"Search\")}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n clearSearch();\n setMobileSearchOpen(false);\n }}\n >\n {t(\"cancel\", \"Cancel\")}\n </Button>\n </div>\n </form>\n\n {/* Mobile Search Results */}\n {searchTerm.trim() && (\n <div className=\"mt-4 max-h-64 overflow-y-auto\">\n {searchResults.length > 0 ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground mb-2\">\n {searchResults.length} result\n {searchResults.length !== 1 ? \"s\" : \"\"} found\n </p>\n {searchResults.slice(0, 5).map((product: Product) => (\n <Link\n key={product.id}\n to={`/products/${product.slug}`}\n onClick={() => {\n setMobileSearchOpen(false);\n clearSearch();\n }}\n className=\"block p-2 rounded hover:bg-muted/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <img\n src={\n product.images[0] || \"/images/placeholder.png\"\n }\n alt={product.name}\n className=\"w-10 h-10 object-cover rounded\"\n />\n <div className=\"flex-1\">\n <h4 className=\"font-medium text-sm\">\n {product.name}\n </h4>\n <p className=\"text-xs text-muted-foreground\">\n {product.category}\n </p>\n <p className=\"text-sm font-medium\">\n {formatPrice(\n product.price,\n constants.site.currency\n )}\n </p>\n </div>\n </div>\n </Link>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {t(\"noResults\")}\n </p>\n )}\n </div>\n )}\n </DialogContent>\n </Dialog>\n\n {/* Wishlist - Desktop Only */}\n <Link to=\"/favorites\" className=\"hidden lg:block\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"relative h-10 w-10\"\n >\n <Heart className=\"h-5 w-5\" />\n {favoriteCount > 0 && (\n <Badge\n variant=\"destructive\"\n className=\"absolute -top-1 -right-1 h-4 w-4 flex items-center justify-center p-0 text-[10px]\"\n >\n {favoriteCount}\n </Badge>\n )}\n </Button>\n </Link>\n\n {/* Cart - Desktop Only */}\n <Link to=\"/cart\" className=\"hidden lg:block\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"relative h-10 w-10\"\n >\n <ShoppingCart className=\"h-5 w-5\" />\n {itemCount > 0 && (\n <Badge\n variant=\"destructive\"\n className=\"absolute -top-1 -right-1 h-4 w-4 flex items-center justify-center p-0 text-[10px]\"\n >\n {itemCount}\n </Badge>\n )}\n </Button>\n </Link>\n\n {/* Auth - Desktop Only */}\n <div className=\"hidden lg:flex\">\n <AuthHeaderMenu variant=\"desktop\">\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <Link to=\"/orders\" className=\"flex items-center\">\n <Package className=\"mr-2 h-4 w-4\" />\n {t(\"myOrders\", \"My Orders\")}\n </Link>\n </DropdownMenuItem>\n </AuthHeaderMenu>\n </div>\n\n {/* Mobile Menu */}\n <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>\n <SheetTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"lg:hidden h-8 w-8 sm:h-10 sm:w-10\"\n >\n <Menu className=\"h-4 w-4 sm:h-5 sm:w-5\" />\n </Button>\n </SheetTrigger>\n <SheetContent side=\"right\" className=\"w-[300px] sm:w-[400px] px-6\">\n <SheetHeader>\n <SheetTitle>{t(\"menu\")}</SheetTitle>\n </SheetHeader>\n\n {/* Mobile Search in Hamburger */}\n <div className=\"mt-6 pb-4 border-b\">\n <form onSubmit={handleSearchSubmit}>\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4\" />\n <Input\n type=\"search\"\n placeholder={t(\"searchPlaceholder\")}\n value={searchTerm}\n onChange={handleSearchChange}\n onFocus={handleSearchFocus}\n className=\"pl-10 h-11\"\n />\n </div>\n </form>\n\n {/* Search Results in Hamburger */}\n {showResults && searchTerm && (\n <div className=\"mt-3 max-h-[300px] overflow-y-auto rounded-lg border bg-card\">\n {searchResults.length > 0 ? (\n <div className=\"divide-y\">\n <div className=\"px-3 py-2 bg-muted/50\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {searchResults.length}{\" \"}\n {searchResults.length === 1\n ? \"result\"\n : \"results\"}\n </p>\n </div>\n {searchResults.slice(0, 5).map((product: Product) => (\n <Link\n key={product.id}\n to={`/products/${product.slug}`}\n onClick={() => {\n setMobileMenuOpen(false);\n clearSearch();\n setShowResults(false);\n }}\n className=\"flex items-center gap-3 p-3 hover:bg-muted/50 transition-colors\"\n >\n <img\n src={\n product.images[0] || \"/images/placeholder.png\"\n }\n alt={product.name}\n className=\"w-14 h-14 object-cover rounded flex-shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-sm line-clamp-1\">\n {product.name}\n </h4>\n <p className=\"text-xs text-muted-foreground capitalize\">\n {product.category}\n </p>\n <p className=\"text-sm font-semibold text-primary mt-1\">\n {formatPrice(\n product.price,\n constants.site.currency\n )}\n </p>\n </div>\n </Link>\n ))}\n {searchResults.length > 5 && (\n <div className=\"px-3 py-2 bg-muted/30 text-center\">\n <button\n onClick={() => {\n navigate(\n `/products?search=${encodeURIComponent(\n searchTerm\n )}`\n );\n setMobileMenuOpen(false);\n clearSearch();\n setShowResults(false);\n }}\n className=\"text-xs font-medium text-primary hover:underline\"\n >\n {t(\n \"viewAllResults\",\n `View all ${searchResults.length} results`\n )}\n </button>\n </div>\n )}\n </div>\n ) : (\n <div className=\"p-6 text-center\">\n <Search className=\"h-8 w-8 text-muted-foreground mx-auto mb-2 opacity-50\" />\n <p className=\"text-sm text-muted-foreground\">\n {t(\"noResults\", \"No results found\")}\n </p>\n </div>\n )}\n </div>\n )}\n </div>\n\n <div className=\"flex flex-col space-y-4 mt-6\">\n {navigation.map((item) => (\n <Link\n key={item.name}\n to={item.href}\n className=\"text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n {item.name}\n </Link>\n ))}\n <div className=\"border-t pt-4 space-y-4\">\n <Link\n to=\"/favorites\"\n className=\"flex items-center justify-between text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n <div className=\"flex items-center space-x-2\">\n <Heart className=\"h-5 w-5\" />\n <span>{t(\"favorites\")}</span>\n </div>\n <Badge variant=\"secondary\">{favoriteCount}</Badge>\n </Link>\n <Link\n to=\"/cart\"\n className=\"flex items-center justify-between text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n <div className=\"flex items-center space-x-2\">\n <ShoppingCart className=\"h-5 w-5\" />\n <span>{t(\"cart\")}</span>\n </div>\n <div className=\"flex flex-col items-end\">\n <Badge variant=\"secondary\">{itemCount}</Badge>\n <span className=\"text-xs text-muted-foreground\">\n {formatPrice(state.total, constants.site.currency)}\n </span>\n </div>\n </Link>\n\n {/* Auth - Mobile */}\n <AuthHeaderMenu\n variant=\"mobile\"\n onMenuClose={() => setMobileMenuOpen(false)}\n >\n <Link\n to=\"/orders\"\n className=\"flex items-center space-x-2 text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n <Package className=\"h-5 w-5\" />\n <span>{t(\"myOrders\", \"My Orders\")}</span>\n </Link>\n </AuthHeaderMenu>\n </div>\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </div>\n </div>\n </header>\n );\n}\n"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "header-ecommerce/lang/en.json",
|
|
25
|
+
"type": "registry:lang",
|
|
26
|
+
"target": "$modules$/header-ecommerce/lang/en.json",
|
|
27
|
+
"content": "{\r\n \"menu\": \"Menu\",\r\n \"home\": \"Home\",\r\n \"products\": \"Products\",\r\n \"about\": \"About\",\r\n \"contact\": \"Contact\",\r\n \"cart\": \"Cart\",\r\n \"favorites\": \"Favorites\",\r\n \"searchProducts\": \"Search Products\",\r\n \"searchPlaceholder\": \"Search for products...\",\r\n \"searchButton\": \"Search\",\r\n \"result\": \"result\",\r\n \"results\": \"results\",\r\n \"noResults\": \"No products found\",\r\n \"tryDifferent\": \"Try different keywords\",\r\n \"viewAllResults\": \"View all results\"\r\n}\r\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "header-ecommerce/lang/tr.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/header-ecommerce/lang/tr.json",
|
|
33
|
+
"content": "{\r\n \"menu\": \"Menü\",\r\n \"home\": \"Ana Sayfa\",\r\n \"products\": \"Ürünler\",\r\n \"about\": \"Hakkımızda\",\r\n \"contact\": \"İletişim\",\r\n \"cart\": \"Sepet\",\r\n \"favorites\": \"Favoriler\",\r\n \"searchProducts\": \"Ürün Ara\",\r\n \"searchPlaceholder\": \"Ürün ara...\",\r\n \"searchButton\": \"Ara\",\r\n \"result\": \"sonuç\",\r\n \"results\": \"sonuç\",\r\n \"noResults\": \"Ürün bulunamadı\",\r\n \"tryDifferent\": \"Farklı anahtar kelimeler deneyin\",\r\n \"viewAllResults\": \"Tüm sonuçları gör\"\r\n}\r\n"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"types": [],
|
|
38
|
+
"variables": [
|
|
39
|
+
"HeaderEcommerce"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|