@promakeai/cli 0.1.2 → 0.1.3

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 (60) hide show
  1. package/dist/index.js +56 -56
  2. package/dist/registry/about-page.json +2 -2
  3. package/dist/registry/about-section.json +2 -2
  4. package/dist/registry/announcement-bar.json +1 -1
  5. package/dist/registry/blog-section.json +6 -4
  6. package/dist/registry/cart-drawer.json +5 -4
  7. package/dist/registry/case-study-page.json +2 -2
  8. package/dist/registry/coming-soon-page-minimal.json +1 -1
  9. package/dist/registry/coming-soon-page.json +1 -1
  10. package/dist/registry/contact-info-grid.json +2 -2
  11. package/dist/registry/contact-page-centered.json +2 -2
  12. package/dist/registry/contact-page-split.json +2 -2
  13. package/dist/registry/contact-page.json +2 -2
  14. package/dist/registry/cta-section.json +2 -2
  15. package/dist/registry/docs/blog-section.md +3 -1
  16. package/dist/registry/docs/cart-drawer.md +9 -9
  17. package/dist/registry/docs/favorites-blog-block.md +10 -3
  18. package/dist/registry/docs/favorites-blog-page.md +38 -0
  19. package/dist/registry/docs/favorites-ecommerce-block.md +10 -3
  20. package/dist/registry/docs/favorites-ecommerce-page.md +38 -0
  21. package/dist/registry/docs/login-page.md +6 -16
  22. package/dist/registry/docs/payment-success-block.md +8 -1
  23. package/dist/registry/docs/post-detail-page.md +39 -0
  24. package/dist/registry/docs/product-card-detailed.md +7 -11
  25. package/dist/registry/docs/product-detail-page.md +39 -0
  26. package/dist/registry/docs/product-detail-section.md +7 -13
  27. package/dist/registry/docs/product-quick-view.md +4 -2
  28. package/dist/registry/faq-categorized.json +2 -2
  29. package/dist/registry/faq-simple.json +2 -2
  30. package/dist/registry/favorites-blog-block.json +1 -1
  31. package/dist/registry/favorites-blog-page.json +48 -0
  32. package/dist/registry/favorites-ecommerce-block.json +1 -1
  33. package/dist/registry/favorites-ecommerce-page.json +48 -0
  34. package/dist/registry/feature-section.json +2 -2
  35. package/dist/registry/footer.json +2 -2
  36. package/dist/registry/header-ecommerce.json +1 -1
  37. package/dist/registry/hero-carousel.json +2 -2
  38. package/dist/registry/hero-cta.json +2 -2
  39. package/dist/registry/hero-gradient.json +2 -2
  40. package/dist/registry/hero.json +2 -2
  41. package/dist/registry/index.json +9 -0
  42. package/dist/registry/landing-page-app.json +1 -1
  43. package/dist/registry/landing-page-saas.json +1 -1
  44. package/dist/registry/login-page.json +8 -6
  45. package/dist/registry/logo-cloud.json +1 -1
  46. package/dist/registry/payment-success-block.json +7 -3
  47. package/dist/registry/portfolio-page.json +2 -2
  48. package/dist/registry/post-detail-page.json +48 -0
  49. package/dist/registry/pricing-page.json +1 -1
  50. package/dist/registry/pricing-section.json +2 -2
  51. package/dist/registry/product-card-detailed.json +5 -4
  52. package/dist/registry/product-detail-page.json +48 -0
  53. package/dist/registry/product-detail-section.json +5 -4
  54. package/dist/registry/product-quick-view.json +5 -4
  55. package/dist/registry/team-page.json +1 -1
  56. package/dist/registry/testimonials-carousel.json +2 -2
  57. package/dist/registry/testimonials-grid.json +2 -2
  58. package/dist/registry/timeline-section.json +2 -2
  59. package/dist/registry/video-hero.json +1 -1
  60. package/package.json +1 -1
@@ -20,7 +20,7 @@
20
20
  "path": "header-ecommerce/header-ecommerce.tsx",
21
21
  "type": "registry:component",
22
22
  "target": "$modules$/header-ecommerce/header-ecommerce.tsx",
23
- "content": "import { useState } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\nimport { ShoppingCart, Menu, Search, Heart, Package, User, LogOut } 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 {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Logo } from \"@/components/Logo\";\nimport { useAuth } from \"@/modules/auth-core\";\nimport { CartDrawer } from \"@/modules/cart-drawer\";\nimport { toast } from \"sonner\";\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, removeItem, isDrawerOpen, setDrawerOpen } = useCart();\n const { favoriteCount } = useFavorites();\n const { isAuthenticated, user, logout } = useAuth();\n const navigate = useNavigate();\n const { t } = useTranslation(\"header-ecommerce\");\n\n const handleLogout = () => {\n logout();\n toast.success(t(\"logoutToastTitle\", \"Goodbye!\"), {\n description: t(\"logoutToastDesc\", \"You have been logged out successfully.\"),\n });\n };\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=\"w-full max-w-[var(--container-max-width)] 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 <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"relative h-10 w-10 hidden lg:flex\"\n asChild\n >\n <Link to=\"/cart\">\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 </Link>\n </Button>\n\n {/* Auth - Desktop Only */}\n <div className=\"hidden lg:flex\">\n {isAuthenticated ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-10 w-10\">\n <User className=\"h-5 w-5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-56\">\n <DropdownMenuLabel className=\"font-normal\">\n <div className=\"flex flex-col space-y-1\">\n <p className=\"text-sm font-medium\">{user?.username}</p>\n {user?.email && (\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n )}\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <Link to=\"/my-orders\" className=\"flex items-center\">\n <Package className=\"mr-2 h-4 w-4\" />\n {t(\"myOrders\", \"My Orders\")}\n </Link>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={handleLogout}\n className=\"text-red-600 focus:text-red-600 focus:bg-red-50 cursor-pointer\"\n >\n <LogOut className=\"mr-2 h-4 w-4\" />\n {t(\"logout\", \"Logout\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : (\n <Link to=\"/login\">\n <Button variant=\"ghost\" size=\"icon\" className=\"h-10 w-10\">\n <User className=\"h-5 w-5\" />\n </Button>\n </Link>\n )}\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 w-full 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 {isAuthenticated ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center space-x-3 p-3 bg-muted/50 rounded-lg\">\n <div className=\"h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <User className=\"h-5 w-5 text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{user?.username}</p>\n {user?.email && (\n <p className=\"text-xs text-muted-foreground truncate\">{user.email}</p>\n )}\n </div>\n </div>\n <Link\n to=\"/my-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 <button\n onClick={() => {\n handleLogout();\n setMobileMenuOpen(false);\n }}\n className=\"flex items-center space-x-2 text-lg font-medium text-red-600 hover:text-red-700 transition-colors w-full\"\n >\n <LogOut className=\"h-5 w-5\" />\n <span>{t(\"logout\", \"Logout\")}</span>\n </button>\n </div>\n ) : (\n <Link\n to=\"/login\"\n className=\"flex items-center space-x-2 text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n <User className=\"h-5 w-5\" />\n <span>{t(\"login\", \"Login\")}</span>\n </Link>\n )}\n </div>\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </div>\n </div>\n {/* Cart Drawer */}\n <CartDrawer\n open={isDrawerOpen}\n onOpenChange={setDrawerOpen}\n items={state.items.map((item) => ({\n id: item.id,\n name: item.product.name,\n href: `/products/${item.product.slug}`,\n price: item.product.on_sale && item.product.sale_price\n ? item.product.sale_price\n : item.product.price,\n quantity: item.quantity,\n image: item.product.images[0] || \"/images/placeholder.png\",\n imageAlt: item.product.name,\n }))}\n currency={constants.site.currency}\n onRemove={(id) => removeItem(id)}\n />\n </header>\n );\n}\n"
23
+ "content": "import { useState } from \"react\";\nimport { Link, useNavigate } from \"react-router\";\nimport { ShoppingCart, Menu, Search, Heart, Package, User, LogOut } 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 {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Logo } from \"@/components/Logo\";\nimport { useAuth } from \"@/modules/auth-core\";\nimport { CartDrawer } from \"@/modules/cart-drawer\";\nimport { toast } from \"sonner\";\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 { isAuthenticated, user, logout } = useAuth();\n const navigate = useNavigate();\n const { t } = useTranslation(\"header-ecommerce\");\n\n const handleLogout = () => {\n logout();\n toast.success(t(\"logoutToastTitle\", \"Goodbye!\"), {\n description: t(\"logoutToastDesc\", \"You have been logged out successfully.\"),\n });\n };\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=\"w-full max-w-[var(--container-max-width)] 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 (Goes to Cart Page) */}\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 {isAuthenticated ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"h-10 w-10\">\n <User className=\"h-5 w-5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-56\">\n <DropdownMenuLabel className=\"font-normal\">\n <div className=\"flex flex-col space-y-1\">\n <p className=\"text-sm font-medium\">{user?.username}</p>\n {user?.email && (\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n )}\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem asChild className=\"cursor-pointer\">\n <Link to=\"/my-orders\" className=\"flex items-center\">\n <Package className=\"mr-2 h-4 w-4\" />\n {t(\"myOrders\", \"My Orders\")}\n </Link>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={handleLogout}\n className=\"text-red-600 focus:text-red-600 focus:bg-red-50 cursor-pointer\"\n >\n <LogOut className=\"mr-2 h-4 w-4\" />\n {t(\"logout\", \"Logout\")}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : (\n <Link to=\"/login\">\n <Button variant=\"ghost\" size=\"icon\" className=\"h-10 w-10\">\n <User className=\"h-5 w-5\" />\n </Button>\n </Link>\n )}\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 w-full 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 {isAuthenticated ? (\n <div className=\"space-y-3\">\n <div className=\"flex items-center space-x-3 p-3 bg-muted/50 rounded-lg\">\n <div className=\"h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <User className=\"h-5 w-5 text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{user?.username}</p>\n {user?.email && (\n <p className=\"text-xs text-muted-foreground truncate\">{user.email}</p>\n )}\n </div>\n </div>\n <Link\n to=\"/my-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 <button\n onClick={() => {\n handleLogout();\n setMobileMenuOpen(false);\n }}\n className=\"flex items-center space-x-2 text-lg font-medium text-red-600 hover:text-red-700 transition-colors w-full\"\n >\n <LogOut className=\"h-5 w-5\" />\n <span>{t(\"logout\", \"Logout\")}</span>\n </button>\n </div>\n ) : (\n <Link\n to=\"/login\"\n className=\"flex items-center space-x-2 text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n <User className=\"h-5 w-5\" />\n <span>{t(\"login\", \"Login\")}</span>\n </Link>\n )}\n </div>\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </div>\n </div>\n {/* Cart Drawer */}\n <CartDrawer showTrigger={false} />\n </header>\n );\n}\n"
24
24
  },
25
25
  {
26
26
  "path": "header-ecommerce/lang/en.json",
@@ -27,13 +27,13 @@
27
27
  "path": "hero-carousel/lang/en.json",
28
28
  "type": "registry:lang",
29
29
  "target": "$modules$/hero-carousel/lang/en.json",
30
- "content": "{\r\n \"slides\": [\r\n {\r\n \"title\": \"Build Something Amazing\",\r\n \"description\": \"Ask Promake to customize these slides based on your brand message and call-to-action.\",\r\n \"primaryButton\": \"Get Started\",\r\n \"secondaryButton\": \"Learn More\"\r\n },\r\n {\r\n \"title\": \"Powerful Features\",\r\n \"description\": \"Discover tools and features that help you achieve more with less effort.\",\r\n \"primaryButton\": \"Explore Features\"\r\n },\r\n {\r\n \"title\": \"Ready to Transform?\",\r\n \"description\": \"Join thousands of satisfied customers who have already made the switch.\",\r\n \"primaryButton\": \"Contact Us\",\r\n \"secondaryButton\": \"See Demo\"\r\n }\r\n ]\r\n}\r\n"
30
+ "content": "{\r\n \"slides\": [\r\n {\r\n \"title\": \"Build Something Amazing\",\r\n \"description\": \"Edit these slides via Promake to match your brand message and call-to-action.\",\r\n \"primaryButton\": \"Get Started\",\r\n \"secondaryButton\": \"Learn More\"\r\n },\r\n {\r\n \"title\": \"Powerful Features\",\r\n \"description\": \"Discover tools and features that help you achieve more with less effort.\",\r\n \"primaryButton\": \"Explore Features\"\r\n },\r\n {\r\n \"title\": \"Ready to Transform?\",\r\n \"description\": \"Join thousands of satisfied customers who have already made the switch.\",\r\n \"primaryButton\": \"Contact Us\",\r\n \"secondaryButton\": \"See Demo\"\r\n }\r\n ]\r\n}\r\n"
31
31
  },
32
32
  {
33
33
  "path": "hero-carousel/lang/tr.json",
34
34
  "type": "registry:lang",
35
35
  "target": "$modules$/hero-carousel/lang/tr.json",
36
- "content": "{\r\n \"slides\": [\r\n {\r\n \"title\": \"Harika Bir Şey İnşa Edin\",\r\n \"description\": \"Bu slaytları marka mesajınıza ve eylem çağrınıza göre özelleştirmek için Promake'e sorun.\",\r\n \"primaryButton\": \"Başlayın\",\r\n \"secondaryButton\": \"Daha Fazla Bilgi\"\r\n },\r\n {\r\n \"title\": \"Güçlü Özellikler\",\r\n \"description\": \"Daha az çabayla daha fazlasını başarmanıza yardımcı olan araçları ve özellikleri keşfedin.\",\r\n \"primaryButton\": \"Özellikleri Keşfet\"\r\n },\r\n {\r\n \"title\": \"Dönüşüme Hazır mısınız?\",\r\n \"description\": \"Geçiş yapan binlerce memnun müşteriye katılın.\",\r\n \"primaryButton\": \"Bize Ulaşın\",\r\n \"secondaryButton\": \"Demo İzle\"\r\n }\r\n ]\r\n}\r\n"
36
+ "content": "{\r\n \"slides\": [\r\n {\r\n \"title\": \"Harika Bir Şey İnşa Edin\",\r\n \"description\": \"Bu slaytları marka mesajınıza ve eylem çağrınıza uyacak şekilde Promake üzerinden düzenleyin.\",\r\n \"primaryButton\": \"Başlayın\",\r\n \"secondaryButton\": \"Daha Fazla Bilgi\"\r\n },\r\n {\r\n \"title\": \"Güçlü Özellikler\",\r\n \"description\": \"Daha az çabayla daha fazlasını başarmanıza yardımcı olan araçları ve özellikleri keşfedin.\",\r\n \"primaryButton\": \"Özellikleri Keşfet\"\r\n },\r\n {\r\n \"title\": \"Dönüşüme Hazır mısınız?\",\r\n \"description\": \"Geçiş yapan binlerce memnun müşteriye katılın.\",\r\n \"primaryButton\": \"Bize Ulaşın\",\r\n \"secondaryButton\": \"Demo İzle\"\r\n }\r\n ]\r\n}\r\n"
37
37
  }
38
38
  ],
39
39
  "exports": {
@@ -24,13 +24,13 @@
24
24
  "path": "hero-cta/lang/en.json",
25
25
  "type": "registry:lang",
26
26
  "target": "$modules$/hero-cta/lang/en.json",
27
- "content": "{\r\n \"heading\": \"Ask Promake to customize this hero heading based on your site\",\r\n \"description\": \"This is where your hero section description will appear. Ask Promake to replace this text with content relevant to your industry and services. Lorem ipsum dolor sit amet.\",\r\n \"reviews\": \"Ask Promake to customize this review count\",\r\n \"primaryCta\": \"Get Started\",\r\n \"secondaryCta\": \"Learn More\",\r\n \"imageAlt\": \"Hero image\"\r\n}\r\n"
27
+ "content": "{\r\n \"heading\": \"Customize this hero heading with Promake\",\r\n \"description\": \"This is where your hero section description will appear. Let Promake tailor this text to your industry and services. Lorem ipsum dolor sit amet.\",\r\n \"reviews\": \"Update this review count using Promake\",\r\n \"primaryCta\": \"Get Started\",\r\n \"secondaryCta\": \"Learn More\",\r\n \"imageAlt\": \"Hero image\"\r\n}\r\n"
28
28
  },
29
29
  {
30
30
  "path": "hero-cta/lang/tr.json",
31
31
  "type": "registry:lang",
32
32
  "target": "$modules$/hero-cta/lang/tr.json",
33
- "content": "{\r\n \"heading\": \"AI bu hero başlığını sitenize göre özelleştirecektir\",\r\n \"description\": \"Hero bölümü açıklamanız burada görünecektir. AI bu metni hedef kitlenize ve içeriğinize göre ilgili içerikle değiştirecektir. Lorem ipsum dolor sit amet.\",\r\n \"reviews\": \"AI bu yorum sayısını özelleştirecektir\",\r\n \"primaryCta\": \"Başlayın\",\r\n \"secondaryCta\": \"Daha Fazla Bilgi\",\r\n \"imageAlt\": \"Hero görseli\"\r\n}\r\n"
33
+ "content": "{\r\n \"heading\": \"Bu hero başlığını Promake ile özelleştirin\",\r\n \"description\": \"Hero bölümü açıklamanız burada görünecektir. Promake ile bu metni sektörünüze ve hizmetlerinize göre uyarlayın. Lorem ipsum dolor sit amet.\",\r\n \"reviews\": \"Bu yorum sayısını Promake kullanarak güncelleyin\",\r\n \"primaryCta\": \"Başlayın\",\r\n \"secondaryCta\": \"Daha Fazla Bilgi\",\r\n \"imageAlt\": \"Hero görseli\"\r\n}\r\n"
34
34
  }
35
35
  ],
36
36
  "exports": {
@@ -22,13 +22,13 @@
22
22
  "path": "hero-gradient/lang/en.json",
23
23
  "type": "registry:lang",
24
24
  "target": "$modules$/hero-gradient/lang/en.json",
25
- "content": "{\r\n \"badge\": \"Ask Promake to customize this badge text\",\r\n \"headingLine1\": \"Ask Promake to replace this heading\",\r\n \"headingLine2\": \"with your site headline\",\r\n \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ask Promake to customize this description based on your site and audience. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"primaryCta\": \"Get Started\",\r\n \"secondaryCta\": \"Contact Sales\",\r\n \"users\": \"Active Users\",\r\n \"uptime\": \"Uptime\",\r\n \"support\": \"Support\"\r\n}\r\n"
25
+ "content": "{\r\n \"badge\": \"Work with Promake to personalize this badge text\",\r\n \"headingLine1\": \"Have Promake update this heading\",\r\n \"headingLine2\": \"with your site headline\",\r\n \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Promake can help tailor this description for your audience. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"primaryCta\": \"Get Started\",\r\n \"secondaryCta\": \"Contact Sales\",\r\n \"users\": \"Active Users\",\r\n \"uptime\": \"Uptime\",\r\n \"support\": \"Support\"\r\n}\r\n"
26
26
  },
27
27
  {
28
28
  "path": "hero-gradient/lang/tr.json",
29
29
  "type": "registry:lang",
30
30
  "target": "$modules$/hero-gradient/lang/tr.json",
31
- "content": "{\r\n \"badge\": \"AI bu rozet metnini özelleştirecektir\",\r\n \"headingLine1\": \"AI bu başlığı\",\r\n \"headingLine2\": \"site başlığınızla değiştirecektir\",\r\n \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. AI bu açıklamayı sitenize ve hedef kitlenize göre özelleştirecektir. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"primaryCta\": \"Başlayın\",\r\n \"secondaryCta\": \"Satış ile İletişim\",\r\n \"users\": \"Aktif Kullanıcı\",\r\n \"uptime\": \"Çalışma Süresi\",\r\n \"support\": \"Destek\"\r\n}\r\n"
31
+ "content": "{\r\n \"badge\": \"Promake ile bu rozet metnini kişiselleştirin\",\r\n \"headingLine1\": \"Promake'ten bu başlığı\",\r\n \"headingLine2\": \"site başlığınızla güncellemesini isteyin\",\r\n \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Promake bu açıklamayı hedef kitleniz için uyarlamanıza yardımcı olabilir. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"primaryCta\": \"Başlayın\",\r\n \"secondaryCta\": \"Satış ile İletişim\",\r\n \"users\": \"Aktif Kullanıcı\",\r\n \"uptime\": \"Çalışma Süresi\",\r\n \"support\": \"Destek\"\r\n}\r\n"
32
32
  }
33
33
  ],
34
34
  "exports": {
@@ -22,13 +22,13 @@
22
22
  "path": "hero/lang/en.json",
23
23
  "type": "registry:lang",
24
24
  "target": "$modules$/hero/lang/en.json",
25
- "content": "{\r\n \"discover\": \"Discover\",\r\n \"amazing\": \"Inspiring\",\r\n \"content\": \"Stories\",\r\n \"subtitle\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ask Promake to replace this content with your actual blog description.\",\r\n \"readLatest\": \"Start Reading\",\r\n \"exploreTopics\": \"Explore Topics\"\r\n}\r\n"
25
+ "content": "{\r\n \"discover\": \"Discover\",\r\n \"amazing\": \"Inspiring\",\r\n \"content\": \"Stories\",\r\n \"subtitle\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Let Promake personalize this with your actual blog description.\",\r\n \"readLatest\": \"Start Reading\",\r\n \"exploreTopics\": \"Explore Topics\"\r\n}\r\n"
26
26
  },
27
27
  {
28
28
  "path": "hero/lang/tr.json",
29
29
  "type": "registry:lang",
30
30
  "target": "$modules$/hero/lang/tr.json",
31
- "content": "{\r\n \"discover\": \"Keşfedin\",\r\n \"amazing\": \"İlham Verici\",\r\n \"content\": \"Hikayeler\",\r\n \"subtitle\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore. AI bu içeriği gerçek blog açıklamanızla değiştirecektir.\",\r\n \"readLatest\": \"Okumaya Başlayın\",\r\n \"exploreTopics\": \"Konuları Keşfedin\"\r\n}\r\n"
31
+ "content": "{\r\n \"discover\": \"Keşfedin\",\r\n \"amazing\": \"İlham Verici\",\r\n \"content\": \"Hikayeler\",\r\n \"subtitle\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore. Promake ile gerçek blog açıklamanızla kişiselleştirin.\",\r\n \"readLatest\": \"Okumaya Başlayın\",\r\n \"exploreTopics\": \"Konuları Keşfedin\"\r\n}\r\n"
32
32
  }
33
33
  ],
34
34
  "exports": {
@@ -33,13 +33,16 @@
33
33
  "faq-categorized",
34
34
  "faq-simple",
35
35
  "favorites-blog-block",
36
+ "favorites-blog-page",
36
37
  "favorites-ecommerce-block",
38
+ "favorites-ecommerce-page",
37
39
  "feature-section",
38
40
  "featured-products",
39
41
  "footer",
40
42
  "footer-detailed",
41
43
  "footer-minimal",
42
44
  "forgot-password-page",
45
+ "forgot-password-page-split",
43
46
  "google-adsense",
44
47
  "google-map",
45
48
  "header-centered-pill",
@@ -59,14 +62,17 @@
59
62
  "login-page-split",
60
63
  "logo-cloud",
61
64
  "masonry-grid",
65
+ "my-orders-page",
62
66
  "newsletter-section",
63
67
  "order-card-compact",
68
+ "order-confirmation-page",
64
69
  "order-detail-block",
65
70
  "orders-list-block",
66
71
  "payment-success-block",
67
72
  "portfolio-page",
68
73
  "post-card",
69
74
  "post-detail-block",
75
+ "post-detail-page",
70
76
  "pricing-card",
71
77
  "pricing-page",
72
78
  "pricing-section",
@@ -75,13 +81,16 @@
75
81
  "product-card-detailed",
76
82
  "product-card-hover",
77
83
  "product-detail-block",
84
+ "product-detail-page",
78
85
  "product-detail-section",
79
86
  "product-quick-view",
80
87
  "products-page",
81
88
  "reading-progress",
82
89
  "register-page",
90
+ "register-page-split",
83
91
  "related-posts-block",
84
92
  "related-products-block",
93
+ "reset-password-page-split",
85
94
  "service-card",
86
95
  "share-buttons",
87
96
  "skill-card",
@@ -28,7 +28,7 @@
28
28
  "path": "landing-page-app/lang/en.json",
29
29
  "type": "registry:lang",
30
30
  "target": "$modules$/landing-page-app/lang/en.json",
31
- "content": "{\r\n \"title\": \"Mobile App\",\r\n \"badge\": \"Available on iOS & Android\",\r\n \"heroTitle\": \"Your App Name Here\",\r\n \"heroDescription\": \"Ask Promake to customize this description based on your app. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"downloadOn\": \"Download on the\",\r\n \"getItOn\": \"Get it on\",\r\n \"downloads\": \"Downloads\",\r\n \"inCategory\": \"In Category\",\r\n \"taskComplete\": \"Task Complete!\",\r\n \"newMessage\": \"New Message\",\r\n \"featuresLabel\": \"Features\",\r\n \"featuresTitle\": \"Why You'll Love It\",\r\n \"feature1Title\": \"Feature One\",\r\n \"feature1Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature2Title\": \"Feature Two\",\r\n \"feature2Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature3Title\": \"Feature Three\",\r\n \"feature3Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature4Title\": \"Feature Four\",\r\n \"feature4Desc\": \"Ask Promake to customize this feature description.\",\r\n \"screenshotsLabel\": \"Screenshots\",\r\n \"screenshotsTitle\": \"See It in Action\",\r\n \"reviewsLabel\": \"Reviews\",\r\n \"reviewsTitle\": \"What Users Say\",\r\n \"review1\": \"Ask Promake to add real user reviews here. Lorem ipsum dolor sit amet.\",\r\n \"review2\": \"Ask Promake to add real user reviews here. Consectetur adipiscing elit.\",\r\n \"review3\": \"Ask Promake to add real user reviews here. Sed do eiusmod tempor.\",\r\n \"ctaTitle\": \"Download Now\",\r\n \"ctaDescription\": \"Ask Promake to customize this CTA description. Available for free on iOS and Android.\"\r\n}\r\n"
31
+ "content": "{\r\n \"title\": \"Mobile App\",\r\n \"badge\": \"Available on iOS & Android\",\r\n \"heroTitle\": \"Your App Name Here\",\r\n \"heroDescription\": \"Let Promake tailor this description to your app. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"downloadOn\": \"Download on the\",\r\n \"getItOn\": \"Get it on\",\r\n \"downloads\": \"Downloads\",\r\n \"inCategory\": \"In Category\",\r\n \"taskComplete\": \"Task Complete!\",\r\n \"newMessage\": \"New Message\",\r\n \"featuresLabel\": \"Features\",\r\n \"featuresTitle\": \"Why You'll Love It\",\r\n \"feature1Title\": \"Feature One\",\r\n \"feature1Desc\": \"Customize this feature description with Promake.\",\r\n \"feature2Title\": \"Feature Two\",\r\n \"feature2Desc\": \"Use Promake to personalize this feature.\",\r\n \"feature3Title\": \"Feature Three\",\r\n \"feature3Desc\": \"Work with Promake to update this feature.\",\r\n \"feature4Title\": \"Feature Four\",\r\n \"feature4Desc\": \"Have Promake adjust this feature description.\",\r\n \"screenshotsLabel\": \"Screenshots\",\r\n \"screenshotsTitle\": \"See It in Action\",\r\n \"reviewsLabel\": \"Reviews\",\r\n \"reviewsTitle\": \"What Users Say\",\r\n \"review1\": \"Add real user reviews here with Promake. Lorem ipsum dolor sit amet.\",\r\n \"review2\": \"Let Promake help add user reviews. Consectetur adipiscing elit.\",\r\n \"review3\": \"Edit this via Promake with actual reviews. Sed do eiusmod tempor.\",\r\n \"ctaTitle\": \"Download Now\",\r\n \"ctaDescription\": \"Update this CTA description using Promake. Available for free on iOS and Android.\"\r\n}\r\n"
32
32
  },
33
33
  {
34
34
  "path": "landing-page-app/lang/tr.json",
@@ -28,7 +28,7 @@
28
28
  "path": "landing-page-saas/lang/en.json",
29
29
  "type": "registry:lang",
30
30
  "target": "$modules$/landing-page-saas/lang/en.json",
31
- "content": "{\r\n \"title\": \"SaaS Landing\",\r\n \"badge\": \"New Feature Available\",\r\n \"heroTitle\": \"Your Product Name Here\",\r\n \"heroDescription\": \"Ask Promake to customize this description based on your product. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"startFree\": \"Start Free Trial\",\r\n \"watchDemo\": \"Watch Demo\",\r\n \"noCreditCard\": \"No credit card required\",\r\n \"stat1Label\": \"Active Users\",\r\n \"stat2Label\": \"Uptime\",\r\n \"stat3Label\": \"Tasks Done\",\r\n \"stat4Label\": \"Rating\",\r\n \"featuresLabel\": \"Features\",\r\n \"featuresTitle\": \"Everything You Need\",\r\n \"featuresDescription\": \"Ask Promake to customize this features description. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"feature1Title\": \"Feature One\",\r\n \"feature1Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature2Title\": \"Feature Two\",\r\n \"feature2Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature3Title\": \"Feature Three\",\r\n \"feature3Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature4Title\": \"Feature Four\",\r\n \"feature4Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature5Title\": \"Feature Five\",\r\n \"feature5Desc\": \"Ask Promake to customize this feature description.\",\r\n \"feature6Title\": \"Feature Six\",\r\n \"feature6Desc\": \"Ask Promake to customize this feature description.\",\r\n \"testimonialsLabel\": \"Testimonials\",\r\n \"testimonialsTitle\": \"What Our Customers Say\",\r\n \"testimonial1\": \"Ask Promake to add real customer testimonials here. Lorem ipsum dolor sit amet.\",\r\n \"testimonial2\": \"Ask Promake to add real customer testimonials here. Consectetur adipiscing elit.\",\r\n \"testimonial3\": \"Ask Promake to add real customer testimonials here. Sed do eiusmod tempor.\",\r\n \"pricingLabel\": \"Pricing\",\r\n \"pricingTitle\": \"Simple, Transparent Pricing\",\r\n \"starterPlan\": \"Starter\",\r\n \"proPlan\": \"Pro\",\r\n \"enterprisePlan\": \"Enterprise\",\r\n \"custom\": \"Custom\",\r\n \"mostPopular\": \"Most Popular\",\r\n \"getStarted\": \"Get Started\",\r\n \"viewAllPlans\": \"View all pricing plans\",\r\n \"ctaTitle\": \"Ready to Get Started?\",\r\n \"ctaDescription\": \"Ask Promake to customize this CTA description based on your goals. Lorem ipsum dolor sit amet.\",\r\n \"ctaButton\": \"Get Started\"\r\n}\r\n"
31
+ "content": "{\r\n \"title\": \"SaaS Landing\",\r\n \"badge\": \"New Feature Available\",\r\n \"heroTitle\": \"Your Product Name Here\",\r\n \"heroDescription\": \"Customize this description with Promake for your product. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"startFree\": \"Start Free Trial\",\r\n \"watchDemo\": \"Watch Demo\",\r\n \"noCreditCard\": \"No credit card required\",\r\n \"stat1Label\": \"Active Users\",\r\n \"stat2Label\": \"Uptime\",\r\n \"stat3Label\": \"Tasks Done\",\r\n \"stat4Label\": \"Rating\",\r\n \"featuresLabel\": \"Features\",\r\n \"featuresTitle\": \"Everything You Need\",\r\n \"featuresDescription\": \"Let Promake personalize this features section. Sed do eiusmod tempor incididunt ut labore.\",\r\n \"feature1Title\": \"Feature One\",\r\n \"feature1Desc\": \"Update this feature description using Promake.\",\r\n \"feature2Title\": \"Feature Two\",\r\n \"feature2Desc\": \"Work with Promake to tailor this feature.\",\r\n \"feature3Title\": \"Feature Three\",\r\n \"feature3Desc\": \"Promake can help customize this feature.\",\r\n \"feature4Title\": \"Feature Four\",\r\n \"feature4Desc\": \"Have Promake adjust this feature description.\",\r\n \"feature5Title\": \"Feature Five\",\r\n \"feature5Desc\": \"Edit this via Promake for your product.\",\r\n \"feature6Title\": \"Feature Six\",\r\n \"feature6Desc\": \"Use Promake to personalize this feature.\",\r\n \"testimonialsLabel\": \"Testimonials\",\r\n \"testimonialsTitle\": \"What Our Customers Say\",\r\n \"testimonial1\": \"Add real customer testimonials with Promake. Lorem ipsum dolor sit amet.\",\r\n \"testimonial2\": \"Let Promake help add testimonials here. Consectetur adipiscing elit.\",\r\n \"testimonial3\": \"Customize this testimonial with Promake. Sed do eiusmod tempor.\",\r\n \"pricingLabel\": \"Pricing\",\r\n \"pricingTitle\": \"Simple, Transparent Pricing\",\r\n \"starterPlan\": \"Starter\",\r\n \"proPlan\": \"Pro\",\r\n \"enterprisePlan\": \"Enterprise\",\r\n \"custom\": \"Custom\",\r\n \"mostPopular\": \"Most Popular\",\r\n \"getStarted\": \"Get Started\",\r\n \"viewAllPlans\": \"View all pricing plans\",\r\n \"ctaTitle\": \"Ready to Get Started?\",\r\n \"ctaDescription\": \"Promake can help tailor this CTA to your goals. Lorem ipsum dolor sit amet.\",\r\n \"ctaButton\": \"Get Started\"\r\n}\r\n"
32
32
  },
33
33
  {
34
34
  "path": "landing-page-saas/lang/tr.json",
@@ -2,13 +2,15 @@
2
2
  "name": "login-page",
3
3
  "type": "registry:page",
4
4
  "title": "Login Page",
5
- "description": "Presentational login page with email/password form, social OAuth buttons (Google, Microsoft), forgot password link, and create account link. Centered card layout with responsive design. Requires parent to handle authentication via callbacks.",
5
+ "description": "Login page with email/password form, forgot password link, and create account link. Centered card layout with responsive design. Integrated with auth-core for authentication.",
6
6
  "registryDependencies": [
7
7
  "button",
8
8
  "input",
9
- "label"
9
+ "label",
10
+ "auth-core",
11
+ "api"
10
12
  ],
11
- "usage": "import LoginPage from '@/modules/login-page';\n\n// Presentational component - handle auth in parent\n<LoginPage\n onSubmit={(email, password) => {\n // Handle login API call here\n }}\n onGoogleLogin={() => {\n // Handle Google OAuth\n }}\n onMicrosoftLogin={() => {\n // Handle Microsoft OAuth\n }}\n/>\n\n• Installed at: src/modules/login-page/\n• Customize text: src/modules/login-page/lang/*.json\n• This is a presentational component - no built-in API calls\n• Parent must handle authentication via callbacks\n• For built-in API integration, use login-page-split instead",
13
+ "usage": "import LoginPage from '@/modules/login-page';\n\n<LoginPage />\n\n• Installed at: src/modules/login-page/\n• Customize text: src/modules/login-page/lang/*.json\n• Integrated with auth-core for API authentication\n• On success, redirects to previous page or home",
12
14
  "route": {
13
15
  "path": "/login",
14
16
  "componentName": "LoginPage"
@@ -24,19 +26,19 @@
24
26
  "path": "login-page/login-page.tsx",
25
27
  "type": "registry:page",
26
28
  "target": "$modules$/login-page/login-page.tsx",
27
- "content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface LoginPageProps {\r\n onSubmit?: (email: string, password: string) => void;\r\n onGoogleLogin?: () => void;\r\n onMicrosoftLogin?: () => void;\r\n className?: string;\r\n}\r\n\r\nexport function LoginPage({\r\n onSubmit,\r\n onGoogleLogin,\r\n onMicrosoftLogin,\r\n className,\r\n}: LoginPageProps) {\r\n const { t } = useTranslation(\"login-page\");\r\n usePageTitle({ title: t(\"title\", \"Sign In\") });\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n onSubmit?.(email, password);\r\n };\r\n\r\n return (\r\n <section className={cn(\"flex min-h-screen bg-muted/30 px-4 py-16 md:py-32\", className)}>\r\n <form\r\n onSubmit={handleSubmit}\r\n className=\"bg-muted m-auto h-fit w-full max-w-sm overflow-hidden rounded-xl border shadow-md\"\r\n >\r\n <div className=\"bg-card -m-px rounded-xl border p-8 pb-6\">\r\n <div className=\"text-center\">\r\n <Link to=\"/\" aria-label=\"go home\" className=\"mx-auto block w-fit\">\r\n <Logo size=\"sm\" />\r\n </Link>\r\n <h1 className=\"mb-1 mt-4 text-xl font-semibold\">\r\n {t(\"title\", \"Sign In\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"subtitle\", \"Welcome back! Sign in to continue\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"mt-6 space-y-6\">\r\n <div className=\"space-y-2\">\r\n <Label htmlFor=\"email\" className=\"block text-sm\">\r\n {t(\"email\", \"Email\")}\r\n </Label>\r\n <Input\r\n type=\"email\"\r\n required\r\n name=\"email\"\r\n id=\"email\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n placeholder={t(\"emailPlaceholder\", \"you@example.com\")}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <Label htmlFor=\"password\" className=\"text-sm\">\r\n {t(\"password\", \"Password\")}\r\n </Label>\r\n <Button asChild variant=\"link\" size=\"sm\" className=\"h-auto p-0\">\r\n <Link to=\"/forgot-password\" className=\"text-xs\">\r\n {t(\"forgotPassword\", \"Forgot password?\")}\r\n </Link>\r\n </Button>\r\n </div>\r\n <Input\r\n type=\"password\"\r\n required\r\n name=\"password\"\r\n id=\"password\"\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n placeholder=\"••••••••\"\r\n />\r\n </div>\r\n\r\n <Button type=\"submit\" className=\"w-full\">\r\n {t(\"signIn\", \"Sign In\")}\r\n </Button>\r\n </div>\r\n\r\n <div className=\"my-6 grid grid-cols-[1fr_auto_1fr] items-center gap-3\">\r\n <hr className=\"border-dashed\" />\r\n <span className=\"text-muted-foreground text-xs\">\r\n {t(\"orContinueWith\", \"Or continue with\")}\r\n </span>\r\n <hr className=\"border-dashed\" />\r\n </div>\r\n\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <Button type=\"button\" variant=\"outline\" onClick={onGoogleLogin}>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"0.98em\"\r\n height=\"1em\"\r\n viewBox=\"0 0 256 262\"\r\n className=\"mr-2\"\r\n >\r\n <path\r\n fill=\"#4285f4\"\r\n d=\"M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027\"\r\n />\r\n <path\r\n fill=\"#34a853\"\r\n d=\"M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1\"\r\n />\r\n <path\r\n fill=\"#fbbc05\"\r\n d=\"M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z\"\r\n />\r\n <path\r\n fill=\"#eb4335\"\r\n d=\"M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251\"\r\n />\r\n </svg>\r\n <span>Google</span>\r\n </Button>\r\n <Button type=\"button\" variant=\"outline\" onClick={onMicrosoftLogin}>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"1em\"\r\n height=\"1em\"\r\n viewBox=\"0 0 256 256\"\r\n className=\"mr-2\"\r\n >\r\n <path fill=\"#f1511b\" d=\"M121.666 121.666H0V0h121.666z\" />\r\n <path fill=\"#80cc28\" d=\"M256 121.666H134.335V0H256z\" />\r\n <path fill=\"#00adef\" d=\"M121.663 256.002H0V134.336h121.663z\" />\r\n <path fill=\"#fbbc09\" d=\"M256 256.002H134.335V134.336H256z\" />\r\n </svg>\r\n <span>Microsoft</span>\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"p-3\">\r\n <p className=\"text-center text-sm text-muted-foreground\">\r\n {t(\"noAccount\", \"Don't have an account?\")}\r\n <Button asChild variant=\"link\" className=\"px-2\">\r\n <Link to=\"/register\">{t(\"createAccount\", \"Create account\")}</Link>\r\n </Button>\r\n </p>\r\n </div>\r\n </form>\r\n </section>\r\n );\r\n}\r\n\r\nexport default LoginPage;\r\n"
29
+ "content": "import { useState } from \"react\";\r\nimport { Link, useNavigate, useLocation } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { toast } from \"sonner\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { useAuthStore } from \"@/modules/auth-core/auth-store\";\r\nimport { customerClient } from \"@/modules/api/customer-client\";\r\nimport { getErrorMessage } from \"@/modules/api/get-error-message\";\r\n\r\ninterface LoginPageProps {\r\n className?: string;\r\n}\r\n\r\nexport function LoginPage({ className }: LoginPageProps) {\r\n const { t } = useTranslation(\"login-page\");\r\n usePageTitle({ title: t(\"title\", \"Sign In\") });\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n const setAuth = useAuthStore((state) => state.setAuth);\r\n\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const from = (location.state as { from?: string })?.from || \"/\";\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setError(null);\r\n setIsLoading(true);\r\n\r\n try {\r\n const response = await customerClient.auth.login({\r\n username: email,\r\n password,\r\n });\r\n\r\n setAuth(\r\n { username: email, email: (response as any).email || email },\r\n {\r\n accessToken: response.accessToken,\r\n refreshToken: response.refreshToken,\r\n idToken: response.idToken,\r\n encryptionKey: response.encryptionKey,\r\n expiresAt: response.expiresIn\r\n ? Date.now() + response.expiresIn * 1000\r\n : undefined,\r\n }\r\n );\r\n\r\n customerClient.setToken(response.accessToken);\r\n toast.success(t(\"loginSuccess\", \"Login successful!\"));\r\n navigate(from, { replace: true });\r\n } catch (err) {\r\n const errorMessage = getErrorMessage(\r\n err,\r\n t(\"loginError\", \"Login failed. Please check your credentials.\")\r\n );\r\n setError(errorMessage);\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n <section\r\n className={cn(\"flex min-h-screen bg-muted/30 px-4 py-16 md:py-32\", className)}\r\n >\r\n <form\r\n onSubmit={handleSubmit}\r\n className=\"bg-muted m-auto h-fit w-full max-w-sm overflow-hidden rounded-xl border shadow-md\"\r\n >\r\n <div className=\"bg-card -m-px rounded-xl border p-8 pb-6\">\r\n <div className=\"text-center\">\r\n <Link to=\"/\" aria-label=\"go home\" className=\"mx-auto block w-fit\">\r\n <Logo size=\"sm\" />\r\n </Link>\r\n <h1 className=\"mb-1 mt-4 text-xl font-semibold\">\r\n {t(\"title\", \"Sign In\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"subtitle\", \"Welcome back! Sign in to continue\")}\r\n </p>\r\n </div>\r\n\r\n {error && (\r\n <div className=\"mt-4 p-3 text-sm text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400 rounded-md\">\r\n {error}\r\n </div>\r\n )}\r\n\r\n <div className=\"mt-6 space-y-6\">\r\n <div className=\"space-y-2\">\r\n <Label htmlFor=\"email\" className=\"block text-sm\">\r\n {t(\"email\", \"Email\")}\r\n </Label>\r\n <Input\r\n type=\"email\"\r\n required\r\n name=\"email\"\r\n id=\"email\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n placeholder={t(\"emailPlaceholder\", \"you@example.com\")}\r\n disabled={isLoading}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <Label htmlFor=\"password\" className=\"text-sm\">\r\n {t(\"password\", \"Password\")}\r\n </Label>\r\n <Button asChild variant=\"link\" size=\"sm\" className=\"h-auto p-0\">\r\n <Link to=\"/forgot-password\" className=\"text-xs\">\r\n {t(\"forgotPassword\", \"Forgot password?\")}\r\n </Link>\r\n </Button>\r\n </div>\r\n <Input\r\n type=\"password\"\r\n required\r\n name=\"password\"\r\n id=\"password\"\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n placeholder=\"••••••••\"\r\n disabled={isLoading}\r\n />\r\n </div>\r\n\r\n <Button type=\"submit\" className=\"w-full\" disabled={isLoading}>\r\n {isLoading ? (\r\n <>\r\n <div className=\"w-4 h-4 border-2 border-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\r\n {t(\"signingIn\", \"Signing in...\")}\r\n </>\r\n ) : (\r\n t(\"signIn\", \"Sign In\")\r\n )}\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"p-3\">\r\n <p className=\"text-center text-sm text-muted-foreground\">\r\n {t(\"noAccount\", \"Don't have an account?\")}\r\n <Button asChild variant=\"link\" className=\"px-2\">\r\n <Link to=\"/register\">{t(\"createAccount\", \"Create account\")}</Link>\r\n </Button>\r\n </p>\r\n </div>\r\n </form>\r\n </section>\r\n );\r\n}\r\n\r\nexport default LoginPage;\r\n"
28
30
  },
29
31
  {
30
32
  "path": "login-page/lang/en.json",
31
33
  "type": "registry:lang",
32
34
  "target": "$modules$/login-page/lang/en.json",
33
- "content": "{\r\n \"title\": \"Sign In\",\r\n \"subtitle\": \"Welcome back! Sign in to continue\",\r\n \"email\": \"Email\",\r\n \"emailPlaceholder\": \"you@example.com\",\r\n \"password\": \"Password\",\r\n \"forgotPassword\": \"Forgot password?\",\r\n \"signIn\": \"Sign In\",\r\n \"orContinueWith\": \"Or continue with\",\r\n \"noAccount\": \"Don't have an account?\",\r\n \"createAccount\": \"Create account\"\r\n}\r\n"
35
+ "content": "{\r\n \"title\": \"Sign In\",\r\n \"subtitle\": \"Welcome back! Sign in to continue\",\r\n \"email\": \"Email\",\r\n \"emailPlaceholder\": \"you@example.com\",\r\n \"password\": \"Password\",\r\n \"forgotPassword\": \"Forgot password?\",\r\n \"signIn\": \"Sign In\",\r\n \"signingIn\": \"Signing in...\",\r\n \"noAccount\": \"Don't have an account?\",\r\n \"createAccount\": \"Create account\",\r\n \"loginSuccess\": \"Login successful!\",\r\n \"loginError\": \"Login failed. Please check your credentials.\"\r\n}\r\n"
34
36
  },
35
37
  {
36
38
  "path": "login-page/lang/tr.json",
37
39
  "type": "registry:lang",
38
40
  "target": "$modules$/login-page/lang/tr.json",
39
- "content": "{\r\n \"title\": \"Giriş Yap\",\r\n \"subtitle\": \"Tekrar hoş geldiniz! Devam etmek için giriş yapın\",\r\n \"email\": \"E-posta\",\r\n \"emailPlaceholder\": \"ornek@email.com\",\r\n \"password\": \"Şifre\",\r\n \"forgotPassword\": \"Şifremi unuttum\",\r\n \"signIn\": \"Giriş Yap\",\r\n \"orContinueWith\": \"Veya şununla devam et\",\r\n \"noAccount\": \"Hesabınız yok mu?\",\r\n \"createAccount\": \"Hesap oluştur\"\r\n}\r\n"
41
+ "content": "{\r\n \"title\": \"Giriş Yap\",\r\n \"subtitle\": \"Tekrar hoş geldiniz! Devam etmek için giriş yapın\",\r\n \"email\": \"E-posta\",\r\n \"emailPlaceholder\": \"ornek@email.com\",\r\n \"password\": \"Şifre\",\r\n \"forgotPassword\": \"Şifremi unuttum\",\r\n \"signIn\": \"Giriş Yap\",\r\n \"signingIn\": \"Giriş yapılıyor...\",\r\n \"noAccount\": \"Hesabınız yok mu?\",\r\n \"createAccount\": \"Hesap oluştur\",\r\n \"loginSuccess\": \"Giriş başarılı!\",\r\n \"loginError\": \"Giriş başarısız. Lütfen bilgilerinizi kontrol edin.\"\r\n}\r\n"
40
42
  }
41
43
  ],
42
44
  "exports": {
@@ -11,7 +11,7 @@
11
11
  "path": "logo-cloud/logo-cloud.tsx",
12
12
  "type": "registry:component",
13
13
  "target": "$modules$/logo-cloud/logo-cloud.tsx",
14
- "content": "\"use client\";\r\n\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface Logo {\r\n name: string;\r\n image: string;\r\n url?: string;\r\n}\r\n\r\ninterface LogoCloudProps {\r\n logos?: Logo[];\r\n title?: string;\r\n grayscale?: boolean;\r\n animate?: boolean;\r\n speed?: \"slow\" | \"normal\" | \"fast\";\r\n pauseOnHover?: boolean;\r\n className?: string;\r\n}\r\n\r\nconst defaultLogos: Logo[] = [\r\n { name: \"Company 1\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 2\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 3\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 4\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 5\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 6\", image: \"/images/placeholder.png\" },\r\n];\r\n\r\nconst speedDuration = {\r\n slow: \"40s\",\r\n normal: \"25s\",\r\n fast: \"15s\",\r\n};\r\n\r\nexport function LogoCloud({\r\n logos = defaultLogos,\r\n title,\r\n grayscale = true,\r\n animate = true,\r\n speed = \"slow\",\r\n pauseOnHover = true,\r\n className,\r\n}: LogoCloudProps) {\r\n const { t } = useTranslation(\"logo-cloud\");\r\n\r\n const displayTitle = title ?? t(\"title\");\r\n\r\n const renderLogo = (logo: Logo, index: number) => {\r\n const imageElement = (\r\n <img\r\n src={logo.image}\r\n alt={logo.name}\r\n className={cn(\r\n \"h-10 md:h-12 w-auto object-contain transition-all duration-300\",\r\n grayscale && \"grayscale opacity-60 hover:grayscale-0 hover:opacity-100\"\r\n )}\r\n draggable={false}\r\n />\r\n );\r\n\r\n if (logo.url) {\r\n return (\r\n <a\r\n key={index}\r\n href={logo.url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"flex items-center justify-center px-8 md:px-12 shrink-0\"\r\n >\r\n {imageElement}\r\n </a>\r\n );\r\n }\r\n\r\n return (\r\n <div key={index} className=\"flex items-center justify-center px-8 md:px-12 shrink-0\">\r\n {imageElement}\r\n </div>\r\n );\r\n };\r\n\r\n return (\r\n <section className={cn(\"py-12 md:py-16 overflow-hidden\", className)}>\r\n <div className=\"container max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {displayTitle && (\r\n <p className=\"text-center text-sm font-medium text-muted-foreground uppercase tracking-wider mb-8\">\r\n {displayTitle}\r\n </p>\r\n )}\r\n </div>\r\n\r\n {/* Marquee Container */}\r\n <div\r\n className={cn(\r\n \"relative w-full\",\r\n pauseOnHover && \"[&:hover_.marquee-content]:pause\"\r\n )}\r\n >\r\n {/* Gradient Masks */}\r\n <div className=\"absolute left-0 top-0 bottom-0 w-20 md:w-32 bg-gradient-to-r from-background to-transparent z-10 pointer-events-none\" />\r\n <div className=\"absolute right-0 top-0 bottom-0 w-20 md:w-32 bg-gradient-to-l from-background to-transparent z-10 pointer-events-none\" />\r\n\r\n {/* Scrolling Content */}\r\n <div className=\"flex overflow-hidden\">\r\n <div\r\n className={cn(\r\n \"marquee-content flex items-center\",\r\n animate && \"animate-marquee\"\r\n )}\r\n style={{\r\n [\"--marquee-duration\" as string]: speedDuration[speed],\r\n }}\r\n >\r\n {/* First set of logos */}\r\n {logos.map((logo, index) => renderLogo(logo, index))}\r\n {/* Duplicate for seamless loop */}\r\n {logos.map((logo, index) => renderLogo(logo, index + logos.length))}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <style>{`\r\n @keyframes marquee {\r\n 0% {\r\n transform: translateX(0);\r\n }\r\n 100% {\r\n transform: translateX(-50%);\r\n }\r\n }\r\n .animate-marquee {\r\n animation: marquee var(--marquee-duration, 25s) linear infinite;\r\n }\r\n .pause {\r\n animation-play-state: paused !important;\r\n }\r\n `}</style>\r\n </section>\r\n );\r\n}\r\n"
14
+ "content": "\"use client\";\r\n\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface Logo {\r\n name: string;\r\n image: string;\r\n url?: string;\r\n}\r\n\r\ninterface LogoCloudProps {\r\n logos?: Logo[];\r\n title?: string;\r\n grayscale?: boolean;\r\n animate?: boolean;\r\n speed?: \"slow\" | \"normal\" | \"fast\";\r\n pauseOnHover?: boolean;\r\n className?: string;\r\n}\r\n\r\nconst defaultLogos: Logo[] = [\r\n { name: \"Company 1\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 2\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 3\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 4\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 5\", image: \"/images/placeholder.png\" },\r\n { name: \"Company 6\", image: \"/images/placeholder.png\" },\r\n];\r\n\r\nconst speedDuration = {\r\n slow: \"40s\",\r\n normal: \"25s\",\r\n fast: \"15s\",\r\n};\r\n\r\nexport function LogoCloud({\r\n logos = defaultLogos,\r\n title,\r\n grayscale = true,\r\n animate = true,\r\n speed = \"slow\",\r\n pauseOnHover = true,\r\n className,\r\n}: LogoCloudProps) {\r\n const { t } = useTranslation(\"logo-cloud\");\r\n\r\n const displayTitle = title ?? t(\"title\");\r\n\r\n const renderLogo = (logo: Logo, index: number) => {\r\n const imageElement = (\r\n <img\r\n src={logo.image}\r\n alt={logo.name}\r\n className={cn(\r\n \"h-10 md:h-12 w-auto object-contain transition-all duration-300 rounded-[var(--radius)]\",\r\n grayscale && \"grayscale opacity-60 hover:grayscale-0 hover:opacity-100\"\r\n )}\r\n draggable={false}\r\n />\r\n );\r\n\r\n if (logo.url) {\r\n return (\r\n <a\r\n key={index}\r\n href={logo.url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"flex items-center justify-center px-8 md:px-12 shrink-0\"\r\n >\r\n {imageElement}\r\n </a>\r\n );\r\n }\r\n\r\n return (\r\n <div key={index} className=\"flex items-center justify-center px-8 md:px-12 shrink-0\">\r\n {imageElement}\r\n </div>\r\n );\r\n };\r\n\r\n return (\r\n <section className={cn(\"py-12 md:py-16 overflow-hidden\", className)}>\r\n <div className=\"container max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {displayTitle && (\r\n <p className=\"text-center text-sm font-medium text-muted-foreground uppercase tracking-wider mb-8\">\r\n {displayTitle}\r\n </p>\r\n )}\r\n </div>\r\n\r\n {/* Marquee Container */}\r\n <div\r\n className={cn(\r\n \"relative w-full\",\r\n pauseOnHover && \"[&:hover_.marquee-content]:pause\"\r\n )}\r\n >\r\n {/* Gradient Masks */}\r\n <div className=\"absolute left-0 top-0 bottom-0 w-20 md:w-32 bg-gradient-to-r from-background to-transparent z-10 pointer-events-none\" />\r\n <div className=\"absolute right-0 top-0 bottom-0 w-20 md:w-32 bg-gradient-to-l from-background to-transparent z-10 pointer-events-none\" />\r\n\r\n {/* Scrolling Content */}\r\n <div className=\"flex overflow-hidden\">\r\n <div\r\n className={cn(\r\n \"marquee-content flex items-center\",\r\n animate && \"animate-marquee\"\r\n )}\r\n style={{\r\n [\"--marquee-duration\" as string]: speedDuration[speed],\r\n }}\r\n >\r\n {/* First set of logos */}\r\n {logos.map((logo, index) => renderLogo(logo, index))}\r\n {/* Duplicate for seamless loop */}\r\n {logos.map((logo, index) => renderLogo(logo, index + logos.length))}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <style>{`\r\n @keyframes marquee {\r\n 0% {\r\n transform: translateX(0);\r\n }\r\n 100% {\r\n transform: translateX(-50%);\r\n }\r\n }\r\n .animate-marquee {\r\n animation: marquee var(--marquee-duration, 25s) linear infinite;\r\n }\r\n .pause {\r\n animation-play-state: paused !important;\r\n }\r\n `}</style>\r\n </section>\r\n );\r\n}\r\n"
15
15
  },
16
16
  {
17
17
  "path": "logo-cloud/index.ts",
@@ -2,8 +2,12 @@
2
2
  "name": "payment-success-block",
3
3
  "type": "registry:block",
4
4
  "title": "Payment Success Block",
5
- "description": "Payment verification UI with loading, success, and failed states. Displays order details and navigation options",
6
- "registryDependencies": [],
5
+ "description": "Payment verification UI with loading, success, and failed states. Displays order details and navigation options. Uses formatPrice from ecommerce-core.",
6
+ "registryDependencies": [
7
+ "button",
8
+ "card",
9
+ "ecommerce-core"
10
+ ],
7
11
  "usage": "import { PaymentSuccessBlock } from '@/modules/payment-success-block';\n\n<PaymentSuccessBlock status=\"success\" orderId={orderId} />\n\n• Installed at: src/modules/payment-success-block/\n• Handles loading, success, and failed states\n• Use in payment callback/confirmation pages",
8
12
  "files": [
9
13
  {
@@ -16,7 +20,7 @@
16
20
  "path": "payment-success-block/payment-success-block.tsx",
17
21
  "type": "registry:block",
18
22
  "target": "$modules$/payment-success-block/payment-success-block.tsx",
19
- "content": "import { Link } from \"react-router\";\nimport {\n CheckCircle,\n XCircle,\n Loader2,\n ShoppingBag,\n Package,\n} from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { useTranslation } from \"react-i18next\";\n\ntype PaymentStatus = \"loading\" | \"success\" | \"failed\";\n\ninterface OrderDetails {\n id?: string;\n totalAmount?: number;\n currency?: string;\n paymentMethod?: string;\n paymentStatus?: string;\n status?: string;\n}\n\ninterface PaymentSuccessBlockProps {\n status: PaymentStatus;\n orderDetails?: OrderDetails | null;\n errorMessage?: string;\n onRetry?: () => void;\n}\n\nexport function PaymentSuccessBlock({\n status,\n orderDetails,\n errorMessage,\n onRetry,\n}: PaymentSuccessBlockProps) {\n const { t } = useTranslation(\"payment-success-block\");\n\n // Loading State\n if (status === \"loading\") {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-md mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <Loader2 className=\"w-16 h-16 text-primary mx-auto mb-4 animate-spin\" />\n <h1 className=\"text-2xl font-bold mb-2\">\n {t(\"verifyingPayment\", \"Verifying Payment\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"pleaseWait\",\n \"Please wait while we verify your payment...\"\n )}\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n }\n\n // Failed State\n if (status === \"failed\") {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-md mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <XCircle className=\"w-16 h-16 text-destructive mx-auto mb-4\" />\n <h1 className=\"text-2xl font-bold mb-2\">\n {t(\"paymentFailed\", \"Payment Failed\")}\n </h1>\n\n {/* Error Message */}\n {errorMessage && (\n <div className=\"bg-destructive/10 border border-destructive/30 rounded-lg p-4 mb-4\">\n <p className=\"text-sm text-destructive\">{errorMessage}</p>\n </div>\n )}\n\n <p className=\"text-muted-foreground mb-6\">\n {t(\n \"paymentFailedDescription\",\n \"We couldn't verify your payment. Please try again or contact support.\"\n )}\n </p>\n\n <div className=\"flex flex-col gap-3\">\n {onRetry && (\n <Button onClick={onRetry}>\n {t(\"tryAgain\", \"Try Again\")}\n </Button>\n )}\n <Button variant=\"outline\" asChild>\n <Link to=\"/contact\">\n {t(\"contactSupport\", \"Contact Support\")}\n </Link>\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n }\n\n // Success State\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-lg mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <CheckCircle className=\"w-20 h-20 text-green-600 dark:text-green-400 mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold mb-2\">\n {t(\"thankYou\", \"Thank You!\")}\n </h1>\n <p className=\"text-xl text-muted-foreground mb-6\">\n {t(\"orderConfirmed\", \"Your order has been confirmed.\")}\n </p>\n\n {orderDetails && (\n <div className=\"bg-muted/50 rounded-lg p-4 mb-6 text-left\">\n <h3 className=\"font-semibold mb-2\">\n {t(\"orderDetails\", \"Order Details\")}\n </h3>\n <div className=\"text-sm space-y-1\">\n {orderDetails.id && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"orderId\", \"Order ID\")}:\n </span>{\" \"}\n <span className=\"font-medium\">{orderDetails.id}</span>\n </p>\n )}\n {orderDetails.totalAmount !== undefined && orderDetails.currency && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"total\", \"Total\")}:\n </span>{\" \"}\n <span className=\"font-medium\">\n {orderDetails.currency.toUpperCase()}{\" \"}\n {orderDetails.totalAmount.toFixed(2)}\n </span>\n </p>\n )}\n {orderDetails.paymentMethod && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"paymentMethod\", \"Payment Method\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize\">\n {orderDetails.paymentMethod}\n </span>\n </p>\n )}\n {orderDetails.paymentStatus && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"paymentStatus\", \"Payment Status\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize text-green-600 dark:text-green-400\">\n {orderDetails.paymentStatus}\n </span>\n </p>\n )}\n {orderDetails.status && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"orderStatus\", \"Order Status\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize\">\n {orderDetails.status}\n </span>\n </p>\n )}\n </div>\n </div>\n )}\n\n <p className=\"text-sm text-muted-foreground mb-6\">\n {t(\n \"confirmationEmailSent\",\n \"A confirmation email has been sent to your email address.\"\n )}\n </p>\n\n <div className=\"flex flex-col gap-3\">\n <Button asChild size=\"lg\">\n <Link to=\"/orders\">\n <Package className=\"w-4 h-4 mr-2\" />\n {t(\"viewMyOrders\", \"View My Orders\")}\n </Link>\n </Button>\n <Button variant=\"outline\" asChild>\n <Link to=\"/products\">\n <ShoppingBag className=\"w-4 h-4 mr-2\" />\n {t(\"continueShopping\", \"Continue Shopping\")}\n </Link>\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"
23
+ "content": "import { Link } from \"react-router\";\nimport {\n CheckCircle,\n XCircle,\n Loader2,\n ShoppingBag,\n Package,\n} from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { useTranslation } from \"react-i18next\";\nimport { formatPrice } from \"@/modules/ecommerce-core\";\n\ntype PaymentStatus = \"loading\" | \"success\" | \"failed\";\n\ninterface OrderDetails {\n id?: string;\n totalAmount?: number;\n currency?: string;\n paymentMethod?: string;\n paymentStatus?: string;\n status?: string;\n}\n\ninterface PaymentSuccessBlockProps {\n status: PaymentStatus;\n orderDetails?: OrderDetails | null;\n errorMessage?: string;\n onRetry?: () => void;\n}\n\nexport function PaymentSuccessBlock({\n status,\n orderDetails,\n errorMessage,\n onRetry,\n}: PaymentSuccessBlockProps) {\n const { t } = useTranslation(\"payment-success-block\");\n\n // Loading State\n if (status === \"loading\") {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-md mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <Loader2 className=\"w-16 h-16 text-primary mx-auto mb-4 animate-spin\" />\n <h1 className=\"text-2xl font-bold mb-2\">\n {t(\"verifyingPayment\", \"Verifying Payment\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"pleaseWait\",\n \"Please wait while we verify your payment...\"\n )}\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n }\n\n // Failed State\n if (status === \"failed\") {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-md mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <XCircle className=\"w-16 h-16 text-destructive mx-auto mb-4\" />\n <h1 className=\"text-2xl font-bold mb-2\">\n {t(\"paymentFailed\", \"Payment Failed\")}\n </h1>\n\n {/* Error Message */}\n {errorMessage && (\n <div className=\"bg-destructive/10 border border-destructive/30 rounded-lg p-4 mb-4\">\n <p className=\"text-sm text-destructive\">{errorMessage}</p>\n </div>\n )}\n\n <p className=\"text-muted-foreground mb-6\">\n {t(\n \"paymentFailedDescription\",\n \"We couldn't verify your payment. Please try again or contact support.\"\n )}\n </p>\n\n <div className=\"flex flex-col gap-3\">\n {onRetry && (\n <Button onClick={onRetry}>\n {t(\"tryAgain\", \"Try Again\")}\n </Button>\n )}\n <Button variant=\"outline\" asChild>\n <Link to=\"/contact\">\n {t(\"contactSupport\", \"Contact Support\")}\n </Link>\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n }\n\n // Success State\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-lg mx-auto text-center\">\n <Card>\n <CardContent className=\"pt-8 pb-8\">\n <CheckCircle className=\"w-20 h-20 text-green-600 dark:text-green-400 mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold mb-2\">\n {t(\"thankYou\", \"Thank You!\")}\n </h1>\n <p className=\"text-xl text-muted-foreground mb-6\">\n {t(\"orderConfirmed\", \"Your order has been confirmed.\")}\n </p>\n\n {orderDetails && (\n <div className=\"bg-muted/50 rounded-lg p-4 mb-6 text-left\">\n <h3 className=\"font-semibold mb-2\">\n {t(\"orderDetails\", \"Order Details\")}\n </h3>\n <div className=\"text-sm space-y-1\">\n {orderDetails.id && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"orderId\", \"Order ID\")}:\n </span>{\" \"}\n <span className=\"font-medium\">{orderDetails.id}</span>\n </p>\n )}\n {orderDetails.totalAmount !== undefined && orderDetails.currency && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"total\", \"Total\")}:\n </span>{\" \"}\n <span className=\"font-medium\">\n {formatPrice(orderDetails.totalAmount, orderDetails.currency)}\n </span>\n </p>\n )}\n {orderDetails.paymentMethod && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"paymentMethod\", \"Payment Method\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize\">\n {orderDetails.paymentMethod}\n </span>\n </p>\n )}\n {orderDetails.paymentStatus && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"paymentStatus\", \"Payment Status\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize text-green-600 dark:text-green-400\">\n {orderDetails.paymentStatus}\n </span>\n </p>\n )}\n {orderDetails.status && (\n <p>\n <span className=\"text-muted-foreground\">\n {t(\"orderStatus\", \"Order Status\")}:\n </span>{\" \"}\n <span className=\"font-medium capitalize\">\n {orderDetails.status}\n </span>\n </p>\n )}\n </div>\n </div>\n )}\n\n <p className=\"text-sm text-muted-foreground mb-6\">\n {t(\n \"confirmationEmailSent\",\n \"A confirmation email has been sent to your email address.\"\n )}\n </p>\n\n <div className=\"flex flex-col gap-3\">\n <Button asChild size=\"lg\">\n <Link to=\"/orders\">\n <Package className=\"w-4 h-4 mr-2\" />\n {t(\"viewMyOrders\", \"View My Orders\")}\n </Link>\n </Button>\n <Button variant=\"outline\" asChild>\n <Link to=\"/products\">\n <ShoppingBag className=\"w-4 h-4 mr-2\" />\n {t(\"continueShopping\", \"Continue Shopping\")}\n </Link>\n </Button>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"
20
24
  },
21
25
  {
22
26
  "path": "payment-success-block/lang/en.json",
@@ -26,13 +26,13 @@
26
26
  "path": "portfolio-page/lang/en.json",
27
27
  "type": "registry:lang",
28
28
  "target": "$modules$/portfolio-page/lang/en.json",
29
- "content": "{\r\n \"pageTitle\": \"Portfolio\",\r\n \"title\": \"Our Portfolio\",\r\n \"subtitle\": \"Ask Promake to customize these projects based on your work and case studies.\",\r\n \"noItems\": \"No projects found in this category.\"\r\n}\r\n"
29
+ "content": "{\r\n \"pageTitle\": \"Portfolio\",\r\n \"title\": \"Our Portfolio\",\r\n \"subtitle\": \"Customize these projects with Promake based on your work.\",\r\n \"noItems\": \"No projects found in this category.\"\r\n}\r\n"
30
30
  },
31
31
  {
32
32
  "path": "portfolio-page/lang/tr.json",
33
33
  "type": "registry:lang",
34
34
  "target": "$modules$/portfolio-page/lang/tr.json",
35
- "content": "{\r\n \"pageTitle\": \"Portfolyo\",\r\n \"title\": \"Portfolyomuz\",\r\n \"subtitle\": \"Bu projeleri kendi çalışmalarınıza ve vaka çalışmalarınıza göre özelleştirmek için Promake'e sorun.\",\r\n \"noItems\": \"Bu kategoride proje bulunamadı.\"\r\n}\r\n"
35
+ "content": "{\r\n \"pageTitle\": \"Portfolyo\",\r\n \"title\": \"Portfolyomuz\",\r\n \"subtitle\": \"Bu projeleri çalışmalarınıza göre Promake ile özelleştirin.\",\r\n \"noItems\": \"Bu kategoride proje bulunamadı.\"\r\n}\r\n"
36
36
  }
37
37
  ],
38
38
  "exports": {