@promakeai/cli 0.0.5 → 0.0.6

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 (91) hide show
  1. package/dist/index.js +214 -135
  2. package/dist/registry/about-page.json +1 -1
  3. package/dist/registry/about-section.json +1 -1
  4. package/dist/registry/api.json +55 -0
  5. package/dist/registry/auth.json +70 -0
  6. package/dist/registry/bento-grid-section.json +1 -1
  7. package/dist/registry/blog-list-page.json +1 -1
  8. package/dist/registry/blog-section.json +1 -1
  9. package/dist/registry/cart-drawer.json +1 -1
  10. package/dist/registry/cart-page.json +3 -2
  11. package/dist/registry/category-section.json +1 -1
  12. package/dist/registry/checkout-page.json +3 -2
  13. package/dist/registry/contact-info-grid.json +1 -1
  14. package/dist/registry/contact-page-centered.json +1 -1
  15. package/dist/registry/contact-page-map-overlay.json +1 -1
  16. package/dist/registry/contact-page.json +1 -1
  17. package/dist/registry/cookies-page.json +1 -1
  18. package/dist/registry/cta-section.json +1 -1
  19. package/dist/registry/db.json +129 -0
  20. package/dist/registry/docs/cart-page.md +1 -0
  21. package/dist/registry/docs/checkout-page.md +1 -0
  22. package/dist/registry/docs/forgot-password-page.md +37 -0
  23. package/dist/registry/docs/header-ecommerce.md +1 -0
  24. package/dist/registry/docs/products-page.md +1 -0
  25. package/dist/registry/docs/register-page.md +39 -0
  26. package/dist/registry/ecommerce-core.json +1 -1
  27. package/dist/registry/empty-page.json +1 -1
  28. package/dist/registry/faq-categorized.json +1 -1
  29. package/dist/registry/faq-simple.json +1 -1
  30. package/dist/registry/favorites-blog-block.json +1 -1
  31. package/dist/registry/favorites-ecommerce-block.json +1 -1
  32. package/dist/registry/feature-section.json +1 -1
  33. package/dist/registry/featured-products.json +1 -1
  34. package/dist/registry/footer-detailed.json +1 -1
  35. package/dist/registry/footer-minimal.json +3 -3
  36. package/dist/registry/footer.json +1 -1
  37. package/dist/registry/forgot-password-page.json +49 -0
  38. package/dist/registry/header-ecommerce.json +3 -2
  39. package/dist/registry/header-mega.json +1 -1
  40. package/dist/registry/header-minimal.json +1 -1
  41. package/dist/registry/header-simple.json +1 -1
  42. package/dist/registry/hero-cta.json +1 -1
  43. package/dist/registry/hero-gradient.json +1 -1
  44. package/dist/registry/hero-profile.json +1 -1
  45. package/dist/registry/hero.json +1 -1
  46. package/dist/registry/index.json +3 -0
  47. package/dist/registry/orders-list-block.json +1 -1
  48. package/dist/registry/payment-success-block.json +1 -1
  49. package/dist/registry/post-detail-block.json +1 -1
  50. package/dist/registry/pricing-section.json +1 -1
  51. package/dist/registry/privacy-page.json +1 -1
  52. package/dist/registry/products-page.json +3 -2
  53. package/dist/registry/register-page.json +49 -0
  54. package/dist/registry/related-posts-block.json +1 -1
  55. package/dist/registry/terms-page.json +1 -1
  56. package/dist/registry/testimonials-carousel.json +1 -1
  57. package/dist/registry/testimonials-grid.json +1 -1
  58. package/package.json +1 -1
  59. package/template/src/App.tsx +3 -24
  60. package/template/src/components/Layout.tsx +0 -4
  61. package/template/src/index.css +1 -0
  62. package/template/src/lang/en/index.json +1 -28
  63. package/template/src/lang/tr/index.json +1 -28
  64. package/template/src/pages/Index.tsx +1 -102
  65. package/template/src/components/Footer.tsx +0 -100
  66. package/template/src/components/Header.tsx +0 -79
  67. package/template/src/components/Hero.tsx +0 -69
  68. package/template/src/modules/api/USAGE.md +0 -515
  69. package/template/src/modules/api/customer-client.ts +0 -20
  70. package/template/src/modules/api/get-error-message.ts +0 -18
  71. package/template/src/modules/api/validation/en.json +0 -29
  72. package/template/src/modules/api/validation/tr.json +0 -29
  73. package/template/src/modules/auth/USAGE.md +0 -248
  74. package/template/src/modules/auth/auth-header-menu.tsx +0 -123
  75. package/template/src/modules/auth/auth-store.ts +0 -57
  76. package/template/src/modules/auth/forgot-password-page.tsx +0 -371
  77. package/template/src/modules/auth/login-page.tsx +0 -183
  78. package/template/src/modules/auth/register-page.tsx +0 -252
  79. package/template/src/modules/auth/use-auth.ts +0 -273
  80. package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
  81. package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
  82. package/template/src/modules/db/adapters/index.ts +0 -2
  83. package/template/src/modules/db/config.ts +0 -59
  84. package/template/src/modules/db/core/DataManager.ts +0 -125
  85. package/template/src/modules/db/core/types.ts +0 -101
  86. package/template/src/modules/db/index.ts +0 -42
  87. package/template/src/modules/db/react/QueryProvider.tsx +0 -16
  88. package/template/src/modules/db/react/index.ts +0 -23
  89. package/template/src/modules/db/react/queryClient.ts +0 -64
  90. package/template/src/modules/db/react/useRepository.ts +0 -400
  91. package/template/src/modules/db/utils/parsers.ts +0 -96
@@ -19,7 +19,7 @@
19
19
  "path": "orders-list-block/orders-list-block.tsx",
20
20
  "type": "registry:block",
21
21
  "target": "$modules$/orders-list-block/orders-list-block.tsx",
22
- "content": "import { Link } from \"react-router\";\nimport { Loader2, ShoppingBag, Package } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { OrderCardCompact } from \"@/modules/order-card-compact/order-card-compact\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface OrderItemSummary {\n id?: string | number;\n name?: string;\n quantity?: number;\n image?: string;\n}\n\ninterface OrderSummary {\n id: string | number;\n orderNumber?: string;\n status: string;\n createdAt?: string;\n totalAmount?: number;\n items?: OrderItemSummary[];\n itemCount?: number;\n paymentMethod?: string;\n}\n\nexport interface OrdersListBlockProps {\n orders: OrderSummary[];\n loading?: boolean;\n error?: boolean;\n onRetry?: () => void;\n getOrderDetailsLink?: (orderId: string | number) => string;\n}\n\nexport function OrdersListBlock({\n orders,\n loading = false,\n error = false,\n onRetry,\n getOrderDetailsLink,\n}: OrdersListBlockProps) {\n const { t } = useTranslation(\"orders-list-block\");\n\n // Loading State\n if (loading) {\n return (\n <div className=\"container mx-auto px-4 py-16\">\n <div className=\"max-w-4xl mx-auto text-center\">\n <Loader2 className=\"w-12 h-12 text-primary mx-auto mb-4 animate-spin\" />\n <h2 className=\"text-2xl font-bold mb-2\">{t('loadingOrders', 'Loading Orders')}</h2>\n <p className=\"text-muted-foreground\">{t('pleaseWait', 'Please wait...')}</p>\n </div>\n </div>\n );\n }\n\n // Error State\n if (error) {\n return (\n <div className=\"container mx-auto px-4 py-16\">\n <div className=\"max-w-2xl mx-auto text-center\">\n <div className=\"w-16 h-16 bg-destructive/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <Package className=\"w-8 h-8 text-destructive\" />\n </div>\n <h2 className=\"text-2xl font-bold mb-2\">{t('errorTitle', 'Something went wrong')}</h2>\n <p className=\"text-muted-foreground mb-8\">\n {t('errorDescription', \"We couldn't load your orders. Please try again.\")}\n </p>\n {onRetry && (\n <Button onClick={onRetry} size=\"lg\">\n {t('tryAgain', 'Try Again')}\n </Button>\n )}\n </div>\n </div>\n );\n }\n\n // Empty State\n if (orders.length === 0) {\n return (\n <div className=\"container mx-auto px-4 py-16\">\n <div className=\"max-w-2xl mx-auto text-center\">\n <div className=\"w-20 h-20 bg-muted rounded-full flex items-center justify-center mx-auto mb-6\">\n <ShoppingBag className=\"w-10 h-10 text-muted-foreground\" />\n </div>\n <h2 className=\"text-3xl font-bold mb-4\">{t('noOrders', 'No Orders Yet')}</h2>\n <p className=\"text-muted-foreground mb-8 text-lg\">\n {t(\n 'noOrdersDescription',\n \"You haven't placed any orders yet. Start shopping to see your orders here.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t('startShopping', 'Start Shopping')}\n </Link>\n </Button>\n </div>\n </div>\n );\n }\n\n // Orders List\n return (\n <div className=\"container mx-auto px-4 py-8\">\n <div className=\"max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-bold mb-2\">{t('title', 'My Orders')}</h1>\n <p className=\"text-muted-foreground\">\n {t('subtitle', 'View and track your orders')}\n </p>\n </div>\n\n {/* Orders Grid */}\n <div className=\"space-y-4\">\n {orders.map((order) => (\n <OrderCardCompact\n key={order.id}\n id={order.id}\n orderNumber={order.orderNumber}\n status={order.status}\n createdAt={order.createdAt}\n totalAmount={order.totalAmount}\n items={order.items}\n itemCount={order.itemCount}\n paymentMethod={order.paymentMethod}\n detailsLink={getOrderDetailsLink ? getOrderDetailsLink(order.id) : undefined}\n />\n ))}\n </div>\n\n {/* Continue Shopping Button */}\n <div className=\"mt-8 text-center\">\n <Button variant=\"outline\" asChild>\n <Link to=\"/products\">\n {t('continueShopping', 'Continue Shopping')}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"
22
+ "content": "import { Link } from \"react-router\";\nimport { Loader2, ShoppingBag, Package } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { OrderCardCompact } from \"@/modules/order-card-compact/order-card-compact\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface OrderItemSummary {\n id?: string | number;\n name?: string;\n quantity?: number;\n image?: string;\n}\n\ninterface OrderSummary {\n id: string | number;\n orderNumber?: string;\n status: string;\n createdAt?: string;\n totalAmount?: number;\n items?: OrderItemSummary[];\n itemCount?: number;\n paymentMethod?: string;\n}\n\nexport interface OrdersListBlockProps {\n orders: OrderSummary[];\n loading?: boolean;\n error?: boolean;\n onRetry?: () => void;\n getOrderDetailsLink?: (orderId: string | number) => string;\n}\n\nexport function OrdersListBlock({\n orders,\n loading = false,\n error = false,\n onRetry,\n getOrderDetailsLink,\n}: OrdersListBlockProps) {\n const { t } = useTranslation(\"orders-list-block\");\n\n // Loading State\n if (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-4xl mx-auto text-center\">\n <Loader2 className=\"w-12 h-12 text-primary mx-auto mb-4 animate-spin\" />\n <h2 className=\"text-2xl font-bold mb-2\">{t('loadingOrders', 'Loading Orders')}</h2>\n <p className=\"text-muted-foreground\">{t('pleaseWait', 'Please wait...')}</p>\n </div>\n </div>\n );\n }\n\n // Error State\n if (error) {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-2xl mx-auto text-center\">\n <div className=\"w-16 h-16 bg-destructive/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <Package className=\"w-8 h-8 text-destructive\" />\n </div>\n <h2 className=\"text-2xl font-bold mb-2\">{t('errorTitle', 'Something went wrong')}</h2>\n <p className=\"text-muted-foreground mb-8\">\n {t('errorDescription', \"We couldn't load your orders. Please try again.\")}\n </p>\n {onRetry && (\n <Button onClick={onRetry} size=\"lg\">\n {t('tryAgain', 'Try Again')}\n </Button>\n )}\n </div>\n </div>\n );\n }\n\n // Empty State\n if (orders.length === 0) {\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-16\">\n <div className=\"max-w-2xl mx-auto text-center\">\n <div className=\"w-20 h-20 bg-muted rounded-full flex items-center justify-center mx-auto mb-6\">\n <ShoppingBag className=\"w-10 h-10 text-muted-foreground\" />\n </div>\n <h2 className=\"text-3xl font-bold mb-4\">{t('noOrders', 'No Orders Yet')}</h2>\n <p className=\"text-muted-foreground mb-8 text-lg\">\n {t(\n 'noOrdersDescription',\n \"You haven't placed any orders yet. Start shopping to see your orders here.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t('startShopping', 'Start Shopping')}\n </Link>\n </Button>\n </div>\n </div>\n );\n }\n\n // Orders List\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-bold mb-2\">{t('title', 'My Orders')}</h1>\n <p className=\"text-muted-foreground\">\n {t('subtitle', 'View and track your orders')}\n </p>\n </div>\n\n {/* Orders Grid */}\n <div className=\"space-y-4\">\n {orders.map((order) => (\n <OrderCardCompact\n key={order.id}\n id={order.id}\n orderNumber={order.orderNumber}\n status={order.status}\n createdAt={order.createdAt}\n totalAmount={order.totalAmount}\n items={order.items}\n itemCount={order.itemCount}\n paymentMethod={order.paymentMethod}\n detailsLink={getOrderDetailsLink ? getOrderDetailsLink(order.id) : undefined}\n />\n ))}\n </div>\n\n {/* Continue Shopping Button */}\n <div className=\"mt-8 text-center\">\n <Button variant=\"outline\" asChild>\n <Link to=\"/products\">\n {t('continueShopping', 'Continue Shopping')}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"
23
23
  },
24
24
  {
25
25
  "path": "orders-list-block/lang/en.json",
@@ -16,7 +16,7 @@
16
16
  "path": "payment-success-block/payment-success-block.tsx",
17
17
  "type": "registry:block",
18
18
  "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=\"container 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=\"container 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=\"container 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"
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"
20
20
  },
21
21
  {
22
22
  "path": "payment-success-block/lang/en.json",
@@ -18,7 +18,7 @@
18
18
  "path": "post-detail-block/post-detail-block.tsx",
19
19
  "type": "registry:block",
20
20
  "target": "$modules$/post-detail-block/post-detail-block.tsx",
21
- "content": "import { Link } from \"react-router\";\nimport {\n Calendar,\n User,\n Eye,\n Clock,\n ArrowLeft,\n Share2,\n Heart,\n} from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface PostDetailBlockProps {\n post: Post;\n onShare?: () => void;\n onAddToFavorites?: () => void;\n isFavorite?: boolean;\n}\n\nexport function PostDetailBlock({\n post,\n onShare,\n onAddToFavorites,\n isFavorite = false,\n}: PostDetailBlockProps) {\n const { t } = useTranslation(\"post-detail-block\");\n\n const formatDate = (dateString?: string) => {\n if (!dateString) return \"\";\n return new Date(dateString).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n };\n\n return (\n <div className=\"container mx-auto px-4 py-8\">\n <div className=\"max-w-4xl mx-auto\">\n {/* Back Button */}\n <div className=\"mb-6\">\n <Button variant=\"ghost\" asChild>\n <Link to=\"/blog\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t(\"backToBlog\", \"Back to Blog\")}\n </Link>\n </Button>\n </div>\n\n {/* Post Header */}\n <div className=\"mb-8\">\n {/* Category Badge */}\n {post.category && (\n <Badge variant=\"secondary\" className=\"mb-4\">\n {post.category}\n </Badge>\n )}\n\n {/* Title */}\n <h1 className=\"text-4xl md:text-5xl font-bold mb-6\">{post.title}</h1>\n\n {/* Meta Information */}\n <div className=\"flex flex-wrap items-center gap-4 text-muted-foreground mb-6\">\n {post.author && (\n <div className=\"flex items-center gap-2\">\n {post.author_avatar ? (\n <Avatar className=\"w-8 h-8\">\n <AvatarImage src={post.author_avatar} alt={post.author} />\n <AvatarFallback>\n {post.author\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")}\n </AvatarFallback>\n </Avatar>\n ) : (\n <User className=\"w-4 h-4\" />\n )}\n <span className=\"font-medium\">{post.author}</span>\n </div>\n )}\n\n {post.published_at && (\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"w-4 h-4\" />\n <span>{formatDate(post.published_at)}</span>\n </div>\n )}\n\n {post.read_time && (\n <div className=\"flex items-center gap-1\">\n <Clock className=\"w-4 h-4\" />\n <span>\n {post.read_time} {t(\"minRead\", \"min read\")}\n </span>\n </div>\n )}\n\n {post.view_count !== undefined && (\n <div className=\"flex items-center gap-1\">\n <Eye className=\"w-4 h-4\" />\n <span>\n {post.view_count} {t(\"views\", \"views\")}\n </span>\n </div>\n )}\n </div>\n\n {/* Actions */}\n <div className=\"flex gap-2\">\n {onShare && (\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"w-4 h-4 mr-2\" />\n {t(\"share\", \"Share\")}\n </Button>\n )}\n {onAddToFavorites && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={onAddToFavorites}\n className={isFavorite ? \"text-red-500\" : \"\"}\n >\n <Heart\n className={`w-4 h-4 mr-2 ${isFavorite ? \"fill-current\" : \"\"}`}\n />\n {isFavorite\n ? t(\"removeFromFavorites\", \"Remove\")\n : t(\"addToFavorites\", \"Add to Favorites\")}\n </Button>\n )}\n </div>\n </div>\n\n {/* Featured Image */}\n {post.featured_image && (\n <div className=\"mb-8\">\n <img\n src={post.featured_image}\n alt={post.title}\n className=\"w-full h-auto rounded-lg\"\n />\n </div>\n )}\n\n <Separator className=\"my-8\" />\n\n {/* Post Content */}\n <article className=\"prose prose-lg dark:prose-invert max-w-none\">\n <div\n dangerouslySetInnerHTML={{ __html: post.content }}\n className=\"leading-relaxed\"\n />\n </article>\n\n {/* Tags */}\n {post.tags && post.tags.length > 0 && (\n <div className=\"mt-8\">\n <Separator className=\"mb-4\" />\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n {t(\"tags\", \"Tags\")}:\n </span>\n {post.tags.map((tag, index) => (\n <Badge key={index} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n {/* Author Box */}\n {post.author && (\n <div className=\"mt-12 p-6 bg-muted/30 rounded-lg\">\n <div className=\"flex items-start gap-4\">\n {post.author_avatar && (\n <Avatar className=\"w-16 h-16\">\n <AvatarImage src={post.author_avatar} alt={post.author} />\n <AvatarFallback>\n {post.author\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")}\n </AvatarFallback>\n </Avatar>\n )}\n <div>\n <h3 className=\"font-semibold text-lg mb-1\">{post.author}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"authorDescription\", \"Author of this post\")}\n </p>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"
21
+ "content": "import { Link } from \"react-router\";\nimport {\n Calendar,\n User,\n Eye,\n Clock,\n ArrowLeft,\n Share2,\n Heart,\n} from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface PostDetailBlockProps {\n post: Post;\n onShare?: () => void;\n onAddToFavorites?: () => void;\n isFavorite?: boolean;\n}\n\nexport function PostDetailBlock({\n post,\n onShare,\n onAddToFavorites,\n isFavorite = false,\n}: PostDetailBlockProps) {\n const { t } = useTranslation(\"post-detail-block\");\n\n const formatDate = (dateString?: string) => {\n if (!dateString) return \"\";\n return new Date(dateString).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n };\n\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <div className=\"max-w-4xl mx-auto\">\n {/* Back Button */}\n <div className=\"mb-6\">\n <Button variant=\"ghost\" asChild>\n <Link to=\"/blog\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t(\"backToBlog\", \"Back to Blog\")}\n </Link>\n </Button>\n </div>\n\n {/* Post Header */}\n <div className=\"mb-8\">\n {/* Category Badge */}\n {post.category && (\n <Badge variant=\"secondary\" className=\"mb-4\">\n {post.category}\n </Badge>\n )}\n\n {/* Title */}\n <h1 className=\"text-4xl md:text-5xl font-bold mb-6\">{post.title}</h1>\n\n {/* Meta Information */}\n <div className=\"flex flex-wrap items-center gap-4 text-muted-foreground mb-6\">\n {post.author && (\n <div className=\"flex items-center gap-2\">\n {post.author_avatar ? (\n <Avatar className=\"w-8 h-8\">\n <AvatarImage src={post.author_avatar} alt={post.author} />\n <AvatarFallback>\n {post.author\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")}\n </AvatarFallback>\n </Avatar>\n ) : (\n <User className=\"w-4 h-4\" />\n )}\n <span className=\"font-medium\">{post.author}</span>\n </div>\n )}\n\n {post.published_at && (\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"w-4 h-4\" />\n <span>{formatDate(post.published_at)}</span>\n </div>\n )}\n\n {post.read_time && (\n <div className=\"flex items-center gap-1\">\n <Clock className=\"w-4 h-4\" />\n <span>\n {post.read_time} {t(\"minRead\", \"min read\")}\n </span>\n </div>\n )}\n\n {post.view_count !== undefined && (\n <div className=\"flex items-center gap-1\">\n <Eye className=\"w-4 h-4\" />\n <span>\n {post.view_count} {t(\"views\", \"views\")}\n </span>\n </div>\n )}\n </div>\n\n {/* Actions */}\n <div className=\"flex gap-2\">\n {onShare && (\n <Button variant=\"outline\" size=\"sm\" onClick={onShare}>\n <Share2 className=\"w-4 h-4 mr-2\" />\n {t(\"share\", \"Share\")}\n </Button>\n )}\n {onAddToFavorites && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={onAddToFavorites}\n className={isFavorite ? \"text-red-500\" : \"\"}\n >\n <Heart\n className={`w-4 h-4 mr-2 ${isFavorite ? \"fill-current\" : \"\"}`}\n />\n {isFavorite\n ? t(\"removeFromFavorites\", \"Remove\")\n : t(\"addToFavorites\", \"Add to Favorites\")}\n </Button>\n )}\n </div>\n </div>\n\n {/* Featured Image */}\n {post.featured_image && (\n <div className=\"mb-8\">\n <img\n src={post.featured_image}\n alt={post.title}\n className=\"w-full h-auto rounded-lg\"\n />\n </div>\n )}\n\n <Separator className=\"my-8\" />\n\n {/* Post Content */}\n <article className=\"prose prose-lg dark:prose-invert max-w-none\">\n <div\n dangerouslySetInnerHTML={{ __html: post.content }}\n className=\"leading-relaxed\"\n />\n </article>\n\n {/* Tags */}\n {post.tags && post.tags.length > 0 && (\n <div className=\"mt-8\">\n <Separator className=\"mb-4\" />\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n {t(\"tags\", \"Tags\")}:\n </span>\n {post.tags.map((tag, index) => (\n <Badge key={index} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n {/* Author Box */}\n {post.author && (\n <div className=\"mt-12 p-6 bg-muted/30 rounded-lg\">\n <div className=\"flex items-start gap-4\">\n {post.author_avatar && (\n <Avatar className=\"w-16 h-16\">\n <AvatarImage src={post.author_avatar} alt={post.author} />\n <AvatarFallback>\n {post.author\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")}\n </AvatarFallback>\n </Avatar>\n )}\n <div>\n <h3 className=\"font-semibold text-lg mb-1\">{post.author}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"authorDescription\", \"Author of this post\")}\n </p>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"
22
22
  },
23
23
  {
24
24
  "path": "post-detail-block/lang/en.json",
@@ -19,7 +19,7 @@
19
19
  "path": "pricing-section/pricing-section.tsx",
20
20
  "type": "registry:component",
21
21
  "target": "$modules$/pricing-section/pricing-section.tsx",
22
- "content": "import { Link } from \"react-router\";\r\nimport { ArrowRight, Check } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport {\r\n Card,\r\n CardContent,\r\n CardFooter,\r\n CardHeader,\r\n CardTitle,\r\n} from \"@/components/ui/card\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\n\r\ninterface PricingSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function PricingSection({ className }: PricingSectionProps) {\r\n const { t } = useTranslation(\"pricing-section\");\r\n\r\n const tiers = [\r\n {\r\n id: \"starter\",\r\n name: t(\"starterName\", \"Starter\"),\r\n description: t(\r\n \"starterDesc\",\r\n \"Perfect for individuals and small projects\"\r\n ),\r\n price: t(\"starterPrice\", \"$9\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"starterFeature1\", \"Up to 3 projects\"),\r\n t(\"starterFeature2\", \"Basic analytics\"),\r\n t(\"starterFeature3\", \"Email support\"),\r\n t(\"starterFeature4\", \"1GB storage\"),\r\n ],\r\n popular: false,\r\n },\r\n {\r\n id: \"professional\",\r\n name: t(\"proName\", \"Professional\"),\r\n description: t(\"proDesc\", \"Best for growing businesses and teams\"),\r\n price: t(\"proPrice\", \"$29\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"proFeature1\", \"Unlimited projects\"),\r\n t(\"proFeature2\", \"Advanced analytics\"),\r\n t(\"proFeature3\", \"Priority support\"),\r\n t(\"proFeature4\", \"10GB storage\"),\r\n t(\"proFeature5\", \"API access\"),\r\n ],\r\n popular: true,\r\n },\r\n {\r\n id: \"enterprise\",\r\n name: t(\"enterpriseName\", \"Enterprise\"),\r\n description: t(\r\n \"enterpriseDesc\",\r\n \"For large organizations with custom needs\"\r\n ),\r\n price: t(\"enterprisePrice\", \"$99\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"enterpriseFeature1\", \"Everything in Pro\"),\r\n t(\"enterpriseFeature2\", \"Unlimited storage\"),\r\n t(\"enterpriseFeature3\", \"24/7 phone support\"),\r\n t(\"enterpriseFeature4\", \"Custom integrations\"),\r\n t(\"enterpriseFeature5\", \"Dedicated manager\"),\r\n t(\"enterpriseFeature6\", \"SLA guarantee\"),\r\n ],\r\n popular: false,\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <p className=\"text-sm font-medium text-primary mb-2\">\r\n {t(\"label\", \"Pricing\")}\r\n </p>\r\n <h2 className=\"text-3xl font-bold md:text-4xl lg:text-5xl mb-4\">\r\n {t(\"title\", \"Simple, transparent pricing\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\r\n \"subtitle\",\r\n \"Choose the plan that's right for you. All plans include a 14-day free trial.\"\r\n )}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto\">\r\n {tiers.map((tier) => (\r\n <Card\r\n key={tier.id}\r\n className={cn(\r\n \"relative overflow-hidden\",\r\n tier.popular && \"border-primary shadow-lg scale-105\"\r\n )}\r\n >\r\n {tier.popular && (\r\n <div className=\"absolute top-0 right-0\">\r\n <Badge className=\"rounded-full\">\r\n {t(\"popular\", \"Most Popular\")}\r\n </Badge>\r\n </div>\r\n )}\r\n\r\n <CardHeader>\r\n <CardTitle className=\"text-xl font-bold\">{tier.name}</CardTitle>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {tier.description}\r\n </p>\r\n </CardHeader>\r\n\r\n <CardContent>\r\n <div className=\"flex items-baseline gap-1 mb-6\">\r\n <span className=\"text-4xl font-bold\">{tier.price}</span>\r\n <span className=\"text-muted-foreground text-sm\">\r\n {tier.frequency}\r\n </span>\r\n </div>\r\n\r\n <ul className=\"space-y-3\">\r\n {tier.features.map((feature, index) => (\r\n <li key={index} className=\"flex items-center gap-2\">\r\n <Check className=\"h-4 w-4 text-primary flex-shrink-0\" />\r\n <span className=\"text-sm text-muted-foreground\">\r\n {feature}\r\n </span>\r\n </li>\r\n ))}\r\n </ul>\r\n </CardContent>\r\n\r\n <CardFooter>\r\n <Button\r\n asChild\r\n className=\"w-full\"\r\n variant={tier.popular ? \"default\" : \"outline\"}\r\n >\r\n <Link to=\"/register\">\r\n {t(\"cta\", \"Get Started\")}\r\n <ArrowRight className=\"ml-2 h-4 w-4\" />\r\n </Link>\r\n </Button>\r\n </CardFooter>\r\n </Card>\r\n ))}\r\n </div>\r\n\r\n <p className=\"text-center text-sm text-muted-foreground mt-8\">\r\n {t(\"guarantee\", \"30-day money-back guarantee. No questions asked.\")}\r\n </p>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
22
+ "content": "import { Link } from \"react-router\";\r\nimport { ArrowRight, Check } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport {\r\n Card,\r\n CardContent,\r\n CardFooter,\r\n CardHeader,\r\n CardTitle,\r\n} from \"@/components/ui/card\";\r\nimport { Badge } from \"@/components/ui/badge\";\r\n\r\ninterface PricingSectionProps {\r\n className?: string;\r\n}\r\n\r\nexport function PricingSection({ className }: PricingSectionProps) {\r\n const { t } = useTranslation(\"pricing-section\");\r\n\r\n const tiers = [\r\n {\r\n id: \"starter\",\r\n name: t(\"starterName\", \"Starter\"),\r\n description: t(\r\n \"starterDesc\",\r\n \"Perfect for individuals and small projects\"\r\n ),\r\n price: t(\"starterPrice\", \"$9\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"starterFeature1\", \"Up to 3 projects\"),\r\n t(\"starterFeature2\", \"Basic analytics\"),\r\n t(\"starterFeature3\", \"Email support\"),\r\n t(\"starterFeature4\", \"1GB storage\"),\r\n ],\r\n popular: false,\r\n },\r\n {\r\n id: \"professional\",\r\n name: t(\"proName\", \"Professional\"),\r\n description: t(\"proDesc\", \"Best for growing businesses and teams\"),\r\n price: t(\"proPrice\", \"$29\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"proFeature1\", \"Unlimited projects\"),\r\n t(\"proFeature2\", \"Advanced analytics\"),\r\n t(\"proFeature3\", \"Priority support\"),\r\n t(\"proFeature4\", \"10GB storage\"),\r\n t(\"proFeature5\", \"API access\"),\r\n ],\r\n popular: true,\r\n },\r\n {\r\n id: \"enterprise\",\r\n name: t(\"enterpriseName\", \"Enterprise\"),\r\n description: t(\r\n \"enterpriseDesc\",\r\n \"For large organizations with custom needs\"\r\n ),\r\n price: t(\"enterprisePrice\", \"$99\"),\r\n frequency: t(\"perMonth\", \"/month\"),\r\n features: [\r\n t(\"enterpriseFeature1\", \"Everything in Pro\"),\r\n t(\"enterpriseFeature2\", \"Unlimited storage\"),\r\n t(\"enterpriseFeature3\", \"24/7 phone support\"),\r\n t(\"enterpriseFeature4\", \"Custom integrations\"),\r\n t(\"enterpriseFeature5\", \"Dedicated manager\"),\r\n t(\"enterpriseFeature6\", \"SLA guarantee\"),\r\n ],\r\n popular: false,\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <p className=\"text-sm font-medium text-primary mb-2\">\r\n {t(\"label\", \"Pricing\")}\r\n </p>\r\n <h2 className=\"text-3xl font-bold md:text-4xl lg:text-5xl mb-4\">\r\n {t(\"title\", \"Simple, transparent pricing\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\r\n \"subtitle\",\r\n \"Choose the plan that's right for you. All plans include a 14-day free trial.\"\r\n )}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto\">\r\n {tiers.map((tier) => (\r\n <Card\r\n key={tier.id}\r\n className={cn(\r\n \"relative overflow-hidden\",\r\n tier.popular && \"border-primary shadow-lg scale-105\"\r\n )}\r\n >\r\n {tier.popular && (\r\n <div className=\"absolute top-0 right-0\">\r\n <Badge className=\"rounded-full\">\r\n {t(\"popular\", \"Most Popular\")}\r\n </Badge>\r\n </div>\r\n )}\r\n\r\n <CardHeader>\r\n <CardTitle className=\"text-xl font-bold\">{tier.name}</CardTitle>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {tier.description}\r\n </p>\r\n </CardHeader>\r\n\r\n <CardContent>\r\n <div className=\"flex items-baseline gap-1 mb-6\">\r\n <span className=\"text-4xl font-bold\">{tier.price}</span>\r\n <span className=\"text-muted-foreground text-sm\">\r\n {tier.frequency}\r\n </span>\r\n </div>\r\n\r\n <ul className=\"space-y-3\">\r\n {tier.features.map((feature, index) => (\r\n <li key={index} className=\"flex items-center gap-2\">\r\n <Check className=\"h-4 w-4 text-primary flex-shrink-0\" />\r\n <span className=\"text-sm text-muted-foreground\">\r\n {feature}\r\n </span>\r\n </li>\r\n ))}\r\n </ul>\r\n </CardContent>\r\n\r\n <CardFooter>\r\n <Button\r\n asChild\r\n className=\"w-full\"\r\n variant={tier.popular ? \"default\" : \"outline\"}\r\n >\r\n <Link to=\"/register\">\r\n {t(\"cta\", \"Get Started\")}\r\n <ArrowRight className=\"ml-2 h-4 w-4\" />\r\n </Link>\r\n </Button>\r\n </CardFooter>\r\n </Card>\r\n ))}\r\n </div>\r\n\r\n <p className=\"text-center text-sm text-muted-foreground mt-8\">\r\n {t(\"guarantee\", \"30-day money-back guarantee. No questions asked.\")}\r\n </p>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
23
23
  },
24
24
  {
25
25
  "path": "pricing-section/lang/en.json",
@@ -20,7 +20,7 @@
20
20
  "path": "privacy-page/privacy-page.tsx",
21
21
  "type": "registry:page",
22
22
  "target": "$modules$/privacy-page/privacy-page.tsx",
23
- "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Shield, Eye, Lock, UserCheck, Mail, FileText } from \"lucide-react\";\nimport { FadeIn, StaggerContainer, StaggerItem } from \"@/modules/animations\";\n\nexport function PrivacyPage() {\n const { t } = useTranslation(\"privacy-page\");\n usePageTitle({ title: t(\"title\") });\n\n const sections = [\n { icon: Eye, titleKey: \"collectionTitle\", contentKey: \"collectionContent\" },\n { icon: FileText, titleKey: \"usageTitle\", contentKey: \"usageContent\" },\n { icon: Lock, titleKey: \"securityTitle\", contentKey: \"securityContent\" },\n { icon: UserCheck, titleKey: \"rightsTitle\", contentKey: \"rightsContent\" },\n { icon: Shield, titleKey: \"cookiesTitle\", contentKey: \"cookiesContent\" },\n { icon: Mail, titleKey: \"contactTitle\", contentKey: \"contactContent\" },\n ];\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n {/* Header */}\n <FadeIn className=\"text-center mb-12\">\n <div className=\"w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <Shield className=\"w-8 h-8 text-primary\" />\n </div>\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"lastUpdated\")}: {t(\"updateDate\")}\n </p>\n </FadeIn>\n\n {/* Introduction */}\n <FadeIn delay={0.1} className=\"max-w-4xl mx-auto mb-12\">\n <Card>\n <CardContent className=\"p-8\">\n <p className=\"text-muted-foreground leading-relaxed\">\n {t(\"introduction\")}\n </p>\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Sections */}\n <StaggerContainer className=\"max-w-4xl mx-auto space-y-6\">\n {sections.map(({ icon: Icon, titleKey, contentKey }, index) => (\n <StaggerItem key={titleKey}>\n <Card>\n <CardContent className=\"p-8\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0\">\n <Icon className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-xl font-semibold mb-3\">\n {index + 1}. {t(titleKey)}\n </h2>\n <p className=\"text-muted-foreground\">{t(contentKey)}</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </StaggerItem>\n ))}\n </StaggerContainer>\n\n {/* Footer Note */}\n <FadeIn className=\"max-w-4xl mx-auto mt-12 text-center\">\n <p className=\"text-sm text-muted-foreground\">{t(\"footerNote\")}</p>\n </FadeIn>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default PrivacyPage;\n"
23
+ "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Shield, Eye, Lock, UserCheck, Mail, FileText } from \"lucide-react\";\nimport { FadeIn, StaggerContainer, StaggerItem } from \"@/modules/animations\";\n\nexport function PrivacyPage() {\n const { t } = useTranslation(\"privacy-page\");\n usePageTitle({ title: t(\"title\") });\n\n const sections = [\n { icon: Eye, titleKey: \"collectionTitle\", contentKey: \"collectionContent\" },\n { icon: FileText, titleKey: \"usageTitle\", contentKey: \"usageContent\" },\n { icon: Lock, titleKey: \"securityTitle\", contentKey: \"securityContent\" },\n { icon: UserCheck, titleKey: \"rightsTitle\", contentKey: \"rightsContent\" },\n { icon: Shield, titleKey: \"cookiesTitle\", contentKey: \"cookiesContent\" },\n { icon: Mail, titleKey: \"contactTitle\", contentKey: \"contactContent\" },\n ];\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <FadeIn className=\"text-center mb-12\">\n <div className=\"w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <Shield className=\"w-8 h-8 text-primary\" />\n </div>\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"lastUpdated\")}: {t(\"updateDate\")}\n </p>\n </FadeIn>\n\n {/* Introduction */}\n <FadeIn delay={0.1} className=\"max-w-4xl mx-auto mb-12\">\n <Card>\n <CardContent className=\"p-8\">\n <p className=\"text-muted-foreground leading-relaxed\">\n {t(\"introduction\")}\n </p>\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Sections */}\n <StaggerContainer className=\"max-w-4xl mx-auto space-y-6\">\n {sections.map(({ icon: Icon, titleKey, contentKey }, index) => (\n <StaggerItem key={titleKey}>\n <Card>\n <CardContent className=\"p-8\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0\">\n <Icon className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-xl font-semibold mb-3\">\n {index + 1}. {t(titleKey)}\n </h2>\n <p className=\"text-muted-foreground\">{t(contentKey)}</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </StaggerItem>\n ))}\n </StaggerContainer>\n\n {/* Footer Note */}\n <FadeIn className=\"max-w-4xl mx-auto mt-12 text-center\">\n <p className=\"text-sm text-muted-foreground\">{t(\"footerNote\")}</p>\n </FadeIn>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default PrivacyPage;\n"
24
24
  },
25
25
  {
26
26
  "path": "privacy-page/lang/en.json",
@@ -5,7 +5,8 @@
5
5
  "description": "Full-featured product listing page with sidebar filters (category, price range, rating), sorting options (price, name, rating, newest), view toggle (grid/list), pagination, and search integration. Uses ProductCard component for display. Includes loading skeletons, empty state handling, and responsive design with collapsible mobile filters.",
6
6
  "registryDependencies": [
7
7
  "ecommerce-core",
8
- "product-card"
8
+ "product-card",
9
+ "animations"
9
10
  ],
10
11
  "route": {
11
12
  "path": "/products",
@@ -23,7 +24,7 @@
23
24
  "path": "products-page/products-page.tsx",
24
25
  "type": "registry:page",
25
26
  "target": "$modules$/products-page/products-page.tsx",
26
- "content": "import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { useTranslation } from \"react-i18next\";\nimport { Filter, Grid, List } from \"lucide-react\";\nimport { Layout } from \"@/components/Layout\";\nimport { Button } from \"@/components/ui/button\";\nimport { FadeIn } from \"@/modules/animations\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useProducts, useCategories } from \"@/modules/ecommerce-core\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\nexport function ProductsPage() {\n const { t } = useTranslation(\"products-page\");\n const { products, loading: productsLoading } = useProducts();\n const { categories, loading: categoriesLoading } = useCategories();\n const loading = productsLoading || categoriesLoading;\n\n const [searchParams, setSearchParams] = useSearchParams();\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n const [sortBy, setSortBy] = useState(\"featured\");\n const [selectedCategories, setSelectedCategories] = useState<string[]>([]);\n const [selectedFeatures, setSelectedFeatures] = useState<string[]>([]);\n const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const minPriceRef = useRef<HTMLInputElement>(null);\n const maxPriceRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n const query = searchParams.get(\"search\") || \"\";\n const categorySlug = searchParams.get(\"category\");\n setSearchQuery(query);\n if (categorySlug) {\n setSelectedCategories([categorySlug]);\n }\n }, [searchParams]);\n\n useEffect(() => {\n const minPrice = parseFloat(searchParams.get(\"minPrice\") || \"0\") || 0;\n const maxPrice =\n parseFloat(searchParams.get(\"maxPrice\") || \"999999\") || 999999;\n\n let filtered = products.filter((product) => {\n const currentPrice =\n product.on_sale && product.sale_price\n ? product.sale_price\n : product.price;\n return currentPrice >= minPrice && currentPrice <= maxPrice;\n });\n\n if (selectedCategories.length > 0) {\n filtered = filtered.filter((product) => {\n return selectedCategories.some((selectedCategory) => {\n if (product.category === selectedCategory) return true;\n return product.categories?.some(\n (cat) => cat.slug === selectedCategory\n );\n });\n });\n }\n\n if (selectedFeatures.length > 0) {\n filtered = filtered.filter((product) => {\n return selectedFeatures.every((feature) => {\n switch (feature) {\n case \"on_sale\":\n return product.on_sale;\n case \"is_new\":\n return product.is_new;\n case \"featured\":\n return product.featured;\n case \"in_stock\":\n return product.stock > 0;\n default:\n return true;\n }\n });\n });\n }\n\n // Apply sorting\n const sorted = [...filtered].sort((a, b) => {\n switch (sortBy) {\n case \"price-low\":\n return (\n (a.on_sale ? a.sale_price || a.price : a.price) -\n (b.on_sale ? b.sale_price || b.price : b.price)\n );\n case \"price-high\":\n return (\n (b.on_sale ? b.sale_price || b.price : b.price) -\n (a.on_sale ? a.sale_price || a.price : a.price)\n );\n case \"newest\":\n return (\n new Date(b.created_at || 0).getTime() -\n new Date(a.created_at || 0).getTime()\n );\n case \"featured\":\n default:\n return (b.featured ? 1 : 0) - (a.featured ? 1 : 0);\n }\n });\n\n setFilteredProducts(sorted);\n }, [products, searchParams, selectedFeatures, selectedCategories, sortBy]);\n\n const handlePriceFilter = useCallback(() => {\n const minPrice = minPriceRef.current?.value || \"\";\n const maxPrice = maxPriceRef.current?.value || \"\";\n const params = new URLSearchParams(searchParams);\n if (minPrice) params.set(\"minPrice\", minPrice);\n else params.delete(\"minPrice\");\n if (maxPrice) params.set(\"maxPrice\", maxPrice);\n else params.delete(\"maxPrice\");\n setSearchParams(params);\n }, [searchParams, setSearchParams]);\n\n const handleCategoryChange = useCallback(\n (category: string, checked: boolean) => {\n if (checked) {\n setSelectedCategories((prev) => [...prev, category]);\n } else {\n setSelectedCategories((prev) => prev.filter((c) => c !== category));\n }\n },\n []\n );\n\n const handleFeatureChange = useCallback(\n (feature: string, checked: boolean) => {\n if (checked) {\n setSelectedFeatures((prev) => [...prev, feature]);\n } else {\n setSelectedFeatures((prev) => prev.filter((f) => f !== feature));\n }\n },\n []\n );\n\n const sortOptions = [\n { value: \"featured\", label: t(\"featured\", \"Featured\") },\n { value: \"price-low\", label: t(\"sortPriceLow\", \"Price: Low to High\") },\n { value: \"price-high\", label: t(\"sortPriceHigh\", \"Price: High to Low\") },\n { value: \"newest\", label: t(\"sortNewest\", \"Newest\") },\n ];\n\n const FilterSidebar = () => (\n <div className=\"space-y-6\">\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"categories\", \"Categories\")}\n </h3>\n <div className=\"space-y-3\">\n {categories.map((category) => (\n <div\n key={category.id}\n className=\"flex items-center space-x-3 p-2 rounded-lg hover:bg-muted/50 transition-colors\"\n >\n <Checkbox\n id={`category-${category.id}`}\n checked={selectedCategories.includes(category.slug)}\n onCheckedChange={(checked) =>\n handleCategoryChange(category.slug, checked as boolean)\n }\n className=\"data-[state=checked]:bg-primary data-[state=checked]:border-primary\"\n />\n <label\n htmlFor={`category-${category.id}`}\n className=\"text-sm font-medium leading-none cursor-pointer flex-1\"\n >\n {category.name}\n </label>\n </div>\n ))}\n </div>\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"priceRange\", \"Price Range\")}\n </h3>\n <div className=\"space-y-3 p-3 bg-muted/30 rounded-lg\">\n <div className=\"grid grid-cols-2 gap-3\">\n <input\n ref={minPriceRef}\n type=\"number\"\n placeholder={t(\"minPrice\", \"Min\")}\n defaultValue={searchParams.get(\"minPrice\") || \"\"}\n onKeyDown={(e) => e.key === \"Enter\" && handlePriceFilter()}\n className=\"w-full px-3 py-2 border border-input rounded-lg text-sm bg-background\"\n />\n <input\n ref={maxPriceRef}\n type=\"number\"\n placeholder={t(\"maxPrice\", \"Max\")}\n defaultValue={searchParams.get(\"maxPrice\") || \"\"}\n onKeyDown={(e) => e.key === \"Enter\" && handlePriceFilter()}\n className=\"w-full px-3 py-2 border border-input rounded-lg text-sm bg-background\"\n />\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"features\", \"Features\")}\n </h3>\n <div className=\"space-y-3\">\n {[\n { key: \"on_sale\", label: t(\"onSale\", \"On Sale\") },\n { key: \"is_new\", label: t(\"newArrivals\", \"New Arrivals\") },\n { key: \"featured\", label: t(\"featuredLabel\", \"Featured\") },\n { key: \"in_stock\", label: t(\"inStock\", \"In Stock\") },\n ].map((feature) => (\n <div\n key={feature.key}\n className=\"flex items-center space-x-3 p-2 rounded-lg hover:bg-muted/50 transition-colors\"\n >\n <Checkbox\n id={feature.key}\n checked={selectedFeatures.includes(feature.key)}\n onCheckedChange={(checked) =>\n handleFeatureChange(feature.key, checked as boolean)\n }\n className=\"data-[state=checked]:bg-primary data-[state=checked]:border-primary\"\n />\n <label\n htmlFor={feature.key}\n className=\"text-sm font-medium leading-none cursor-pointer flex-1\"\n >\n {feature.label}\n </label>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n\n return (\n <Layout>\n <div className=\"container mx-auto px-4 py-8\">\n <FadeIn className=\"mb-8\">\n <div className=\"flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-6\">\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl lg:text-3xl font-bold\">\n {searchQuery\n ? t(\"searchResultsFor\", `Search Results for \"${searchQuery}\"`)\n : t(\"allProducts\", \"All Products\")}\n </h1>\n <p className=\"text-sm lg:text-base text-muted-foreground\">\n {t(\"showing\", \"Showing\")} {filteredProducts.length}{\" \"}\n {t(\"of\", \"of\")} {products.length} {t(\"products\", \"products\")}\n </p>\n </div>\n {searchQuery && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setSearchQuery(\"\");\n setSearchParams({});\n }}\n className=\"w-fit\"\n >\n {t(\"clearSearch\", \"Clear Search\")}\n </Button>\n )}\n </div>\n\n <div className=\"flex flex-col sm:flex-row gap-3 items-stretch sm:items-center justify-between\">\n <Sheet>\n <SheetTrigger asChild>\n <Button\n variant=\"outline\"\n className=\"lg:hidden w-full sm:w-auto\"\n >\n <Filter className=\"h-4 w-4 mr-2\" />\n {t(\"filters\", \"Filters\")}\n </Button>\n </SheetTrigger>\n <SheetContent side=\"left\" className=\"w-[300px]\">\n <SheetHeader>\n <SheetTitle>{t(\"filters\", \"Filters\")}</SheetTitle>\n <SheetDescription>\n {t(\"refineSearch\", \"Refine your product search\")}\n </SheetDescription>\n </SheetHeader>\n <div className=\"mt-6\">\n <FilterSidebar />\n </div>\n </SheetContent>\n </Sheet>\n\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3\">\n <Select value={sortBy} onValueChange={setSortBy}>\n <SelectTrigger className=\"w-full sm:w-[160px]\">\n <SelectValue placeholder={t(\"sortBy\", \"Sort by\")} />\n </SelectTrigger>\n <SelectContent>\n {sortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <div className=\"flex border rounded-lg p-1 w-full sm:w-auto\">\n <Button\n variant={viewMode === \"grid\" ? \"default\" : \"ghost\"}\n size=\"sm\"\n onClick={() => setViewMode(\"grid\")}\n className=\"flex-1 sm:flex-none\"\n >\n <Grid className=\"h-4 w-4\" />\n </Button>\n <Button\n variant={viewMode === \"list\" ? \"default\" : \"ghost\"}\n size=\"sm\"\n onClick={() => setViewMode(\"list\")}\n className=\"flex-1 sm:flex-none\"\n >\n <List className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </FadeIn>\n\n <div className=\"flex gap-8\">\n <aside className=\"hidden lg:block w-64 flex-shrink-0\">\n <div className=\"sticky top-24\">\n <FilterSidebar />\n </div>\n </aside>\n\n <div className=\"flex-1\">\n {loading ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8\">\n {[...Array(6)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse bg-card rounded-lg shadow-md overflow-hidden\"\n >\n <div className=\"aspect-square bg-muted mb-4\"></div>\n <div className=\"p-4\">\n <div className=\"h-4 bg-muted rounded w-3/4 mb-2\"></div>\n <div className=\"h-3 bg-muted rounded w-1/2 mb-3\"></div>\n <div className=\"h-4 bg-muted rounded w-1/3\"></div>\n </div>\n </div>\n ))}\n </div>\n ) : viewMode === \"grid\" ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8\">\n {filteredProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n variant=\"grid\"\n />\n ))}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {filteredProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n variant=\"list\"\n />\n ))}\n </div>\n )}\n\n {!loading && filteredProducts.length === 0 && (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">\n {t(\n \"noProductsFound\",\n \"No products found matching your criteria.\"\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
27
+ "content": "import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useSearchParams } from \"react-router\";\nimport { useTranslation } from \"react-i18next\";\nimport { Filter, Grid, List } from \"lucide-react\";\nimport { Layout } from \"@/components/Layout\";\nimport { Button } from \"@/components/ui/button\";\nimport { FadeIn } from \"@/modules/animations\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useProducts, useCategories } from \"@/modules/ecommerce-core\";\nimport type { Product } from \"@/modules/ecommerce-core/types\";\n\nexport function ProductsPage() {\n const { t } = useTranslation(\"products-page\");\n const { products, loading: productsLoading } = useProducts();\n const { categories, loading: categoriesLoading } = useCategories();\n const loading = productsLoading || categoriesLoading;\n\n const [searchParams, setSearchParams] = useSearchParams();\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n const [sortBy, setSortBy] = useState(\"featured\");\n const [selectedCategories, setSelectedCategories] = useState<string[]>([]);\n const [selectedFeatures, setSelectedFeatures] = useState<string[]>([]);\n const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);\n const [searchQuery, setSearchQuery] = useState(\"\");\n const minPriceRef = useRef<HTMLInputElement>(null);\n const maxPriceRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n const query = searchParams.get(\"search\") || \"\";\n const categorySlug = searchParams.get(\"category\");\n setSearchQuery(query);\n if (categorySlug) {\n setSelectedCategories([categorySlug]);\n }\n }, [searchParams]);\n\n useEffect(() => {\n const minPrice = parseFloat(searchParams.get(\"minPrice\") || \"0\") || 0;\n const maxPrice =\n parseFloat(searchParams.get(\"maxPrice\") || \"999999\") || 999999;\n\n let filtered = products.filter((product) => {\n const currentPrice =\n product.on_sale && product.sale_price\n ? product.sale_price\n : product.price;\n return currentPrice >= minPrice && currentPrice <= maxPrice;\n });\n\n if (selectedCategories.length > 0) {\n filtered = filtered.filter((product) => {\n return selectedCategories.some((selectedCategory) => {\n if (product.category === selectedCategory) return true;\n return product.categories?.some(\n (cat) => cat.slug === selectedCategory\n );\n });\n });\n }\n\n if (selectedFeatures.length > 0) {\n filtered = filtered.filter((product) => {\n return selectedFeatures.every((feature) => {\n switch (feature) {\n case \"on_sale\":\n return product.on_sale;\n case \"is_new\":\n return product.is_new;\n case \"featured\":\n return product.featured;\n case \"in_stock\":\n return product.stock > 0;\n default:\n return true;\n }\n });\n });\n }\n\n // Apply sorting\n const sorted = [...filtered].sort((a, b) => {\n switch (sortBy) {\n case \"price-low\":\n return (\n (a.on_sale ? a.sale_price || a.price : a.price) -\n (b.on_sale ? b.sale_price || b.price : b.price)\n );\n case \"price-high\":\n return (\n (b.on_sale ? b.sale_price || b.price : b.price) -\n (a.on_sale ? a.sale_price || a.price : a.price)\n );\n case \"newest\":\n return (\n new Date(b.created_at || 0).getTime() -\n new Date(a.created_at || 0).getTime()\n );\n case \"featured\":\n default:\n return (b.featured ? 1 : 0) - (a.featured ? 1 : 0);\n }\n });\n\n setFilteredProducts(sorted);\n }, [products, searchParams, selectedFeatures, selectedCategories, sortBy]);\n\n const handlePriceFilter = useCallback(() => {\n const minPrice = minPriceRef.current?.value || \"\";\n const maxPrice = maxPriceRef.current?.value || \"\";\n const params = new URLSearchParams(searchParams);\n if (minPrice) params.set(\"minPrice\", minPrice);\n else params.delete(\"minPrice\");\n if (maxPrice) params.set(\"maxPrice\", maxPrice);\n else params.delete(\"maxPrice\");\n setSearchParams(params);\n }, [searchParams, setSearchParams]);\n\n const handleCategoryChange = useCallback(\n (category: string, checked: boolean) => {\n if (checked) {\n setSelectedCategories((prev) => [...prev, category]);\n } else {\n setSelectedCategories((prev) => prev.filter((c) => c !== category));\n }\n },\n []\n );\n\n const handleFeatureChange = useCallback(\n (feature: string, checked: boolean) => {\n if (checked) {\n setSelectedFeatures((prev) => [...prev, feature]);\n } else {\n setSelectedFeatures((prev) => prev.filter((f) => f !== feature));\n }\n },\n []\n );\n\n const sortOptions = [\n { value: \"featured\", label: t(\"featured\", \"Featured\") },\n { value: \"price-low\", label: t(\"sortPriceLow\", \"Price: Low to High\") },\n { value: \"price-high\", label: t(\"sortPriceHigh\", \"Price: High to Low\") },\n { value: \"newest\", label: t(\"sortNewest\", \"Newest\") },\n ];\n\n const FilterSidebar = () => (\n <div className=\"space-y-6\">\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"categories\", \"Categories\")}\n </h3>\n <div className=\"space-y-3\">\n {categories.map((category) => (\n <div\n key={category.id}\n className=\"flex items-center space-x-3 p-2 rounded-lg hover:bg-muted/50 transition-colors\"\n >\n <Checkbox\n id={`category-${category.id}`}\n checked={selectedCategories.includes(category.slug)}\n onCheckedChange={(checked) =>\n handleCategoryChange(category.slug, checked as boolean)\n }\n className=\"data-[state=checked]:bg-primary data-[state=checked]:border-primary\"\n />\n <label\n htmlFor={`category-${category.id}`}\n className=\"text-sm font-medium leading-none cursor-pointer flex-1\"\n >\n {category.name}\n </label>\n </div>\n ))}\n </div>\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"priceRange\", \"Price Range\")}\n </h3>\n <div className=\"space-y-3 p-3 bg-muted/30 rounded-lg\">\n <div className=\"grid grid-cols-2 gap-3\">\n <input\n ref={minPriceRef}\n type=\"number\"\n placeholder={t(\"minPrice\", \"Min\")}\n defaultValue={searchParams.get(\"minPrice\") || \"\"}\n onKeyDown={(e) => e.key === \"Enter\" && handlePriceFilter()}\n className=\"w-full px-3 py-2 border border-input rounded-lg text-sm bg-background\"\n />\n <input\n ref={maxPriceRef}\n type=\"number\"\n placeholder={t(\"maxPrice\", \"Max\")}\n defaultValue={searchParams.get(\"maxPrice\") || \"\"}\n onKeyDown={(e) => e.key === \"Enter\" && handlePriceFilter()}\n className=\"w-full px-3 py-2 border border-input rounded-lg text-sm bg-background\"\n />\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"font-semibold mb-4 text-base\">\n {t(\"features\", \"Features\")}\n </h3>\n <div className=\"space-y-3\">\n {[\n { key: \"on_sale\", label: t(\"onSale\", \"On Sale\") },\n { key: \"is_new\", label: t(\"newArrivals\", \"New Arrivals\") },\n { key: \"featured\", label: t(\"featuredLabel\", \"Featured\") },\n { key: \"in_stock\", label: t(\"inStock\", \"In Stock\") },\n ].map((feature) => (\n <div\n key={feature.key}\n className=\"flex items-center space-x-3 p-2 rounded-lg hover:bg-muted/50 transition-colors\"\n >\n <Checkbox\n id={feature.key}\n checked={selectedFeatures.includes(feature.key)}\n onCheckedChange={(checked) =>\n handleFeatureChange(feature.key, checked as boolean)\n }\n className=\"data-[state=checked]:bg-primary data-[state=checked]:border-primary\"\n />\n <label\n htmlFor={feature.key}\n className=\"text-sm font-medium leading-none cursor-pointer flex-1\"\n >\n {feature.label}\n </label>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n\n return (\n <Layout>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-8\">\n <FadeIn className=\"mb-8\">\n <div className=\"flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-6\">\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl lg:text-3xl font-bold\">\n {searchQuery\n ? t(\"searchResultsFor\", `Search Results for \"${searchQuery}\"`)\n : t(\"allProducts\", \"All Products\")}\n </h1>\n <p className=\"text-sm lg:text-base text-muted-foreground\">\n {t(\"showing\", \"Showing\")} {filteredProducts.length}{\" \"}\n {t(\"of\", \"of\")} {products.length} {t(\"products\", \"products\")}\n </p>\n </div>\n {searchQuery && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => {\n setSearchQuery(\"\");\n setSearchParams({});\n }}\n className=\"w-fit\"\n >\n {t(\"clearSearch\", \"Clear Search\")}\n </Button>\n )}\n </div>\n\n <div className=\"flex flex-col sm:flex-row gap-3 items-stretch sm:items-center justify-between\">\n <Sheet>\n <SheetTrigger asChild>\n <Button\n variant=\"outline\"\n className=\"lg:hidden w-full sm:w-auto\"\n >\n <Filter className=\"h-4 w-4 mr-2\" />\n {t(\"filters\", \"Filters\")}\n </Button>\n </SheetTrigger>\n <SheetContent side=\"left\" className=\"w-[300px]\">\n <SheetHeader>\n <SheetTitle>{t(\"filters\", \"Filters\")}</SheetTitle>\n <SheetDescription>\n {t(\"refineSearch\", \"Refine your product search\")}\n </SheetDescription>\n </SheetHeader>\n <div className=\"mt-6\">\n <FilterSidebar />\n </div>\n </SheetContent>\n </Sheet>\n\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3\">\n <Select value={sortBy} onValueChange={setSortBy}>\n <SelectTrigger className=\"w-full sm:w-[160px]\">\n <SelectValue placeholder={t(\"sortBy\", \"Sort by\")} />\n </SelectTrigger>\n <SelectContent>\n {sortOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <div className=\"flex border rounded-lg p-1 w-full sm:w-auto\">\n <Button\n variant={viewMode === \"grid\" ? \"default\" : \"ghost\"}\n size=\"sm\"\n onClick={() => setViewMode(\"grid\")}\n className=\"flex-1 sm:flex-none\"\n >\n <Grid className=\"h-4 w-4\" />\n </Button>\n <Button\n variant={viewMode === \"list\" ? \"default\" : \"ghost\"}\n size=\"sm\"\n onClick={() => setViewMode(\"list\")}\n className=\"flex-1 sm:flex-none\"\n >\n <List className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </FadeIn>\n\n <div className=\"flex gap-8\">\n <aside className=\"hidden lg:block w-64 flex-shrink-0\">\n <div className=\"sticky top-24\">\n <FilterSidebar />\n </div>\n </aside>\n\n <div className=\"flex-1\">\n {loading ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8\">\n {[...Array(6)].map((_, i) => (\n <div\n key={i}\n className=\"animate-pulse bg-card rounded-lg shadow-md overflow-hidden\"\n >\n <div className=\"aspect-square bg-muted mb-4\"></div>\n <div className=\"p-4\">\n <div className=\"h-4 bg-muted rounded w-3/4 mb-2\"></div>\n <div className=\"h-3 bg-muted rounded w-1/2 mb-3\"></div>\n <div className=\"h-4 bg-muted rounded w-1/3\"></div>\n </div>\n </div>\n ))}\n </div>\n ) : viewMode === \"grid\" ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8\">\n {filteredProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n variant=\"grid\"\n />\n ))}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {filteredProducts.map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n variant=\"list\"\n />\n ))}\n </div>\n )}\n\n {!loading && filteredProducts.length === 0 && (\n <div className=\"text-center py-12\">\n <p className=\"text-muted-foreground\">\n {t(\n \"noProductsFound\",\n \"No products found matching your criteria.\"\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
27
28
  },
28
29
  {
29
30
  "path": "products-page/lang/en.json",
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "register-page",
3
+ "type": "registry:page",
4
+ "title": "Register Page",
5
+ "description": "Registration page with name, email, password form, social OAuth buttons (Google, Microsoft), and sign in link. Centered card layout with responsive design.",
6
+ "registryDependencies": [
7
+ "button",
8
+ "input",
9
+ "label"
10
+ ],
11
+ "usage": "import RegisterPage from '@/modules/register-page';\n\n<RegisterPage\n onSubmit={(name, email, password) => console.log(name, email, password)}\n onGoogleSignup={() => console.log('Google')}\n onMicrosoftSignup={() => console.log('Microsoft')}\n/>",
12
+ "route": {
13
+ "path": "/register",
14
+ "componentName": "RegisterPage"
15
+ },
16
+ "files": [
17
+ {
18
+ "path": "register-page/index.ts",
19
+ "type": "registry:index",
20
+ "target": "$modules$/register-page/index.ts",
21
+ "content": "export { default } from './register-page';\r\nexport { default as RegisterPage } from './register-page';\r\n"
22
+ },
23
+ {
24
+ "path": "register-page/register-page.tsx",
25
+ "type": "registry:page",
26
+ "target": "$modules$/register-page/register-page.tsx",
27
+ "content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\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 RegisterPageProps {\r\n onSubmit?: (name: string, email: string, password: string) => void;\r\n onGoogleSignup?: () => void;\r\n onMicrosoftSignup?: () => void;\r\n className?: string;\r\n}\r\n\r\nexport default function RegisterPage({\r\n onSubmit,\r\n onGoogleSignup,\r\n onMicrosoftSignup,\r\n className,\r\n}: RegisterPageProps) {\r\n const { t } = useTranslation(\"register-page\");\r\n const [name, setName] = useState(\"\");\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?.(name, 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\", \"Create Account\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"subtitle\", \"Sign up to get started\")}\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=\"name\" className=\"block text-sm\">\r\n {t(\"name\", \"Full Name\")}\r\n </Label>\r\n <Input\r\n type=\"text\"\r\n required\r\n name=\"name\"\r\n id=\"name\"\r\n value={name}\r\n onChange={(e) => setName(e.target.value)}\r\n placeholder={t(\"namePlaceholder\", \"John Doe\")}\r\n />\r\n </div>\r\n\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 <Label htmlFor=\"password\" className=\"text-sm\">\r\n {t(\"password\", \"Password\")}\r\n </Label>\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(\"signUp\", \"Sign Up\")}\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={onGoogleSignup}>\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={onMicrosoftSignup}>\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(\"hasAccount\", \"Already have an account?\")}\r\n <Button asChild variant=\"link\" className=\"px-2\">\r\n <Link to=\"/login\">{t(\"signIn\", \"Sign in\")}</Link>\r\n </Button>\r\n </p>\r\n </div>\r\n </form>\r\n </section>\r\n );\r\n}\r\n"
28
+ },
29
+ {
30
+ "path": "register-page/lang/en.json",
31
+ "type": "registry:lang",
32
+ "target": "$modules$/register-page/lang/en.json",
33
+ "content": "{\r\n \"title\": \"Create Account\",\r\n \"subtitle\": \"Sign up to get started\",\r\n \"name\": \"Full Name\",\r\n \"namePlaceholder\": \"John Doe\",\r\n \"email\": \"Email\",\r\n \"emailPlaceholder\": \"you@example.com\",\r\n \"password\": \"Password\",\r\n \"signUp\": \"Sign Up\",\r\n \"orContinueWith\": \"Or continue with\",\r\n \"hasAccount\": \"Already have an account?\",\r\n \"signIn\": \"Sign in\"\r\n}\r\n"
34
+ },
35
+ {
36
+ "path": "register-page/lang/tr.json",
37
+ "type": "registry:lang",
38
+ "target": "$modules$/register-page/lang/tr.json",
39
+ "content": "{\r\n \"title\": \"Hesap Oluştur\",\r\n \"subtitle\": \"Başlamak için kaydolun\",\r\n \"name\": \"Ad Soyad\",\r\n \"namePlaceholder\": \"Ahmet Yılmaz\",\r\n \"email\": \"E-posta\",\r\n \"emailPlaceholder\": \"ornek@email.com\",\r\n \"password\": \"Şifre\",\r\n \"signUp\": \"Kaydol\",\r\n \"orContinueWith\": \"Veya şununla devam et\",\r\n \"hasAccount\": \"Zaten hesabınız var mı?\",\r\n \"signIn\": \"Giriş yap\"\r\n}\r\n"
40
+ }
41
+ ],
42
+ "exports": {
43
+ "types": [],
44
+ "variables": [
45
+ "RegisterPage",
46
+ "default"
47
+ ]
48
+ }
49
+ }
@@ -19,7 +19,7 @@
19
19
  "path": "related-posts-block/related-posts-block.tsx",
20
20
  "type": "registry:block",
21
21
  "target": "$modules$/related-posts-block/related-posts-block.tsx",
22
- "content": "import { PostCard } from \"@/modules/post-card/post-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface RelatedPostsBlockProps {\n posts: Post[];\n title?: string;\n}\n\nexport function RelatedPostsBlock({ posts, title }: RelatedPostsBlockProps) {\n const { t } = useTranslation(\"related-posts-block\");\n\n if (posts.length === 0) {\n return null;\n }\n\n return (\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-6xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-8\">\n {title || t(\"title\", \"Related Posts\")}\n </h2>\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\n {posts.map((post) => (\n <PostCard key={post.id} post={post} layout=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
22
+ "content": "import { PostCard } from \"@/modules/post-card/post-card\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface RelatedPostsBlockProps {\n posts: Post[];\n title?: string;\n}\n\nexport function RelatedPostsBlock({ posts, title }: RelatedPostsBlockProps) {\n const { t } = useTranslation(\"related-posts-block\");\n\n if (posts.length === 0) {\n return null;\n }\n\n return (\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-12\">\n <div className=\"max-w-6xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-8\">\n {title || t(\"title\", \"Related Posts\")}\n </h2>\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\n {posts.map((post) => (\n <PostCard key={post.id} post={post} layout=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n"
23
23
  },
24
24
  {
25
25
  "path": "related-posts-block/lang/en.json",
@@ -20,7 +20,7 @@
20
20
  "path": "terms-page/terms-page.tsx",
21
21
  "type": "registry:page",
22
22
  "target": "$modules$/terms-page/terms-page.tsx",
23
- "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { FileText, Scale, AlertTriangle, Ban, RefreshCw, Gavel } from \"lucide-react\";\nimport { FadeIn, StaggerContainer, StaggerItem } from \"@/modules/animations\";\n\nexport function TermsPage() {\n const { t } = useTranslation(\"terms-page\");\n usePageTitle({ title: t(\"title\") });\n\n const sections = [\n { icon: FileText, titleKey: \"acceptanceTitle\", contentKey: \"acceptanceContent\" },\n { icon: Scale, titleKey: \"licenseTitle\", contentKey: \"licenseContent\" },\n { icon: AlertTriangle, titleKey: \"restrictionsTitle\", contentKey: \"restrictionsContent\" },\n { icon: Ban, titleKey: \"terminationTitle\", contentKey: \"terminationContent\" },\n { icon: RefreshCw, titleKey: \"changesTitle\", contentKey: \"changesContent\" },\n { icon: Gavel, titleKey: \"governingTitle\", contentKey: \"governingContent\" },\n ];\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4\">\n {/* Header */}\n <FadeIn className=\"text-center mb-12\">\n <div className=\"w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <FileText className=\"w-8 h-8 text-primary\" />\n </div>\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"lastUpdated\")}: {t(\"updateDate\")}\n </p>\n </FadeIn>\n\n {/* Introduction */}\n <FadeIn delay={0.1} className=\"max-w-4xl mx-auto mb-12\">\n <Card>\n <CardContent className=\"p-8\">\n <p className=\"text-muted-foreground leading-relaxed\">\n {t(\"introduction\")}\n </p>\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Sections */}\n <StaggerContainer className=\"max-w-4xl mx-auto space-y-6\">\n {sections.map(({ icon: Icon, titleKey, contentKey }, index) => (\n <StaggerItem key={titleKey}>\n <Card>\n <CardContent className=\"p-8\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0\">\n <Icon className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-xl font-semibold mb-3\">\n {index + 1}. {t(titleKey)}\n </h2>\n <p className=\"text-muted-foreground\">{t(contentKey)}</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </StaggerItem>\n ))}\n </StaggerContainer>\n\n {/* Footer Note */}\n <FadeIn className=\"max-w-4xl mx-auto mt-12 text-center\">\n <p className=\"text-sm text-muted-foreground\">{t(\"footerNote\")}</p>\n </FadeIn>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default TermsPage;\n"
23
+ "content": "import { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Layout } from \"@/components/Layout\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { FileText, Scale, AlertTriangle, Ban, RefreshCw, Gavel } from \"lucide-react\";\nimport { FadeIn, StaggerContainer, StaggerItem } from \"@/modules/animations\";\n\nexport function TermsPage() {\n const { t } = useTranslation(\"terms-page\");\n usePageTitle({ title: t(\"title\") });\n\n const sections = [\n { icon: FileText, titleKey: \"acceptanceTitle\", contentKey: \"acceptanceContent\" },\n { icon: Scale, titleKey: \"licenseTitle\", contentKey: \"licenseContent\" },\n { icon: AlertTriangle, titleKey: \"restrictionsTitle\", contentKey: \"restrictionsContent\" },\n { icon: Ban, titleKey: \"terminationTitle\", contentKey: \"terminationContent\" },\n { icon: RefreshCw, titleKey: \"changesTitle\", contentKey: \"changesContent\" },\n { icon: Gavel, titleKey: \"governingTitle\", contentKey: \"governingContent\" },\n ];\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <FadeIn className=\"text-center mb-12\">\n <div className=\"w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <FileText className=\"w-8 h-8 text-primary\" />\n </div>\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"lastUpdated\")}: {t(\"updateDate\")}\n </p>\n </FadeIn>\n\n {/* Introduction */}\n <FadeIn delay={0.1} className=\"max-w-4xl mx-auto mb-12\">\n <Card>\n <CardContent className=\"p-8\">\n <p className=\"text-muted-foreground leading-relaxed\">\n {t(\"introduction\")}\n </p>\n </CardContent>\n </Card>\n </FadeIn>\n\n {/* Sections */}\n <StaggerContainer className=\"max-w-4xl mx-auto space-y-6\">\n {sections.map(({ icon: Icon, titleKey, contentKey }, index) => (\n <StaggerItem key={titleKey}>\n <Card>\n <CardContent className=\"p-8\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0\">\n <Icon className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"flex-1\">\n <h2 className=\"text-xl font-semibold mb-3\">\n {index + 1}. {t(titleKey)}\n </h2>\n <p className=\"text-muted-foreground\">{t(contentKey)}</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </StaggerItem>\n ))}\n </StaggerContainer>\n\n {/* Footer Note */}\n <FadeIn className=\"max-w-4xl mx-auto mt-12 text-center\">\n <p className=\"text-sm text-muted-foreground\">{t(\"footerNote\")}</p>\n </FadeIn>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default TermsPage;\n"
24
24
  },
25
25
  {
26
26
  "path": "terms-page/lang/en.json",
@@ -20,7 +20,7 @@
20
20
  "path": "testimonials-carousel/testimonials-carousel.tsx",
21
21
  "type": "registry:component",
22
22
  "target": "$modules$/testimonials-carousel/testimonials-carousel.tsx",
23
- "content": "import { useState, useEffect } from \"react\";\r\nimport { Star } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Card, CardContent, CardHeader, CardFooter, CardTitle } from \"@/components/ui/card\";\r\nimport {\r\n Carousel,\r\n CarouselContent,\r\n CarouselItem,\r\n CarouselNext,\r\n CarouselPrevious,\r\n type CarouselApi,\r\n} from \"@/components/ui/carousel\";\r\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface Testimonial {\r\n id: number;\r\n name: string;\r\n role: string;\r\n image: string;\r\n review: string;\r\n rating: number;\r\n}\r\n\r\ninterface TestimonialsCarouselProps {\r\n className?: string;\r\n}\r\n\r\nexport function TestimonialsCarousel({ className }: TestimonialsCarouselProps) {\r\n const { t } = useTranslation(\"testimonials-carousel\");\r\n const [api, setApi] = useState<CarouselApi>();\r\n const [current, setCurrent] = useState(0);\r\n\r\n const testimonials: Testimonial[] = [\r\n {\r\n id: 1,\r\n name: t(\"testimonial1Name\", \"Sarah Johnson\"),\r\n role: t(\"testimonial1Role\", \"Product Manager\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial1Review\", \"This platform has completely transformed how we work. The intuitive interface and powerful features have boosted our team's productivity significantly.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 2,\r\n name: t(\"testimonial2Name\", \"Michael Chen\"),\r\n role: t(\"testimonial2Role\", \"Software Engineer\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial2Review\", \"Exceptional quality and attention to detail. The support team is incredibly responsive and helpful. Highly recommend for any development team.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 3,\r\n name: t(\"testimonial3Name\", \"Emily Davis\"),\r\n role: t(\"testimonial3Role\", \"Design Lead\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial3Review\", \"The best investment we've made for our design workflow. Clean, modern, and incredibly flexible. Our clients love the results.\"),\r\n rating: 4,\r\n },\r\n {\r\n id: 4,\r\n name: t(\"testimonial4Name\", \"James Wilson\"),\r\n role: t(\"testimonial4Role\", \"Startup Founder\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial4Review\", \"Launched our MVP in record time thanks to this amazing toolkit. The documentation is excellent and the community is super helpful.\"),\r\n rating: 5,\r\n },\r\n ];\r\n\r\n useEffect(() => {\r\n if (!api) return;\r\n\r\n const onSelect = () => {\r\n setCurrent(api.selectedScrollSnap());\r\n };\r\n\r\n api.on(\"select\", onSelect);\r\n return () => {\r\n api.off(\"select\", onSelect);\r\n };\r\n }, [api]);\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4\">\r\n <header className=\"mb-12 text-center\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"What Our Customers Say\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"Don't just take our word for it. Here's what our customers have to say about their experience.\")}\r\n </p>\r\n </header>\r\n\r\n <Carousel\r\n setApi={setApi}\r\n opts={{\r\n align: \"start\",\r\n loop: true,\r\n }}\r\n className=\"w-full max-w-6xl mx-auto\"\r\n >\r\n <CarouselContent className=\"-ml-4\">\r\n {testimonials.map((testimonial) => (\r\n <CarouselItem\r\n key={testimonial.id}\r\n className=\"pl-4 basis-full sm:basis-1/2 lg:basis-1/3\"\r\n >\r\n <Card className=\"h-full\">\r\n <CardHeader>\r\n <div className=\"flex items-center gap-4\">\r\n <Avatar className=\"h-12 w-12\">\r\n <AvatarImage\r\n src={testimonial.image}\r\n alt={testimonial.name}\r\n />\r\n <AvatarFallback>\r\n {testimonial.name.charAt(0)}\r\n </AvatarFallback>\r\n </Avatar>\r\n <div>\r\n <CardTitle className=\"text-base\">\r\n {testimonial.name}\r\n </CardTitle>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {testimonial.role}\r\n </p>\r\n </div>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n <p className=\"text-muted-foreground text-sm leading-relaxed\">\r\n \"{testimonial.review}\"\r\n </p>\r\n </CardContent>\r\n <CardFooter>\r\n <div className=\"flex items-center gap-1\">\r\n {[...Array(5)].map((_, index) => (\r\n <Star\r\n key={index}\r\n className={cn(\r\n \"h-4 w-4\",\r\n index < testimonial.rating\r\n ? \"fill-yellow-400 text-yellow-400\"\r\n : \"fill-muted text-muted\"\r\n )}\r\n />\r\n ))}\r\n </div>\r\n </CardFooter>\r\n </Card>\r\n </CarouselItem>\r\n ))}\r\n </CarouselContent>\r\n <CarouselPrevious className=\"hidden lg:flex\" />\r\n <CarouselNext className=\"hidden lg:flex\" />\r\n </Carousel>\r\n\r\n {/* Dot Navigation */}\r\n <div className=\"flex items-center justify-center gap-2 mt-8\">\r\n {testimonials.map((_, index) => (\r\n <Button\r\n key={index}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => api?.scrollTo(index)}\r\n className={cn(\r\n \"h-2 rounded-full p-0 transition-all\",\r\n current === index\r\n ? \"w-6 bg-primary\"\r\n : \"w-2 bg-muted hover:bg-muted-foreground/20\"\r\n )}\r\n aria-label={`Go to slide ${index + 1}`}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
23
+ "content": "import { useState, useEffect } from \"react\";\r\nimport { Star } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Card, CardContent, CardHeader, CardFooter, CardTitle } from \"@/components/ui/card\";\r\nimport {\r\n Carousel,\r\n CarouselContent,\r\n CarouselItem,\r\n CarouselNext,\r\n CarouselPrevious,\r\n type CarouselApi,\r\n} from \"@/components/ui/carousel\";\r\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface Testimonial {\r\n id: number;\r\n name: string;\r\n role: string;\r\n image: string;\r\n review: string;\r\n rating: number;\r\n}\r\n\r\ninterface TestimonialsCarouselProps {\r\n className?: string;\r\n}\r\n\r\nexport function TestimonialsCarousel({ className }: TestimonialsCarouselProps) {\r\n const { t } = useTranslation(\"testimonials-carousel\");\r\n const [api, setApi] = useState<CarouselApi>();\r\n const [current, setCurrent] = useState(0);\r\n\r\n const testimonials: Testimonial[] = [\r\n {\r\n id: 1,\r\n name: t(\"testimonial1Name\", \"Sarah Johnson\"),\r\n role: t(\"testimonial1Role\", \"Product Manager\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial1Review\", \"This platform has completely transformed how we work. The intuitive interface and powerful features have boosted our team's productivity significantly.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 2,\r\n name: t(\"testimonial2Name\", \"Michael Chen\"),\r\n role: t(\"testimonial2Role\", \"Software Engineer\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial2Review\", \"Exceptional quality and attention to detail. The support team is incredibly responsive and helpful. Highly recommend for any development team.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 3,\r\n name: t(\"testimonial3Name\", \"Emily Davis\"),\r\n role: t(\"testimonial3Role\", \"Design Lead\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial3Review\", \"The best investment we've made for our design workflow. Clean, modern, and incredibly flexible. Our clients love the results.\"),\r\n rating: 4,\r\n },\r\n {\r\n id: 4,\r\n name: t(\"testimonial4Name\", \"James Wilson\"),\r\n role: t(\"testimonial4Role\", \"Startup Founder\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial4Review\", \"Launched our MVP in record time thanks to this amazing toolkit. The documentation is excellent and the community is super helpful.\"),\r\n rating: 5,\r\n },\r\n ];\r\n\r\n useEffect(() => {\r\n if (!api) return;\r\n\r\n const onSelect = () => {\r\n setCurrent(api.selectedScrollSnap());\r\n };\r\n\r\n api.on(\"select\", onSelect);\r\n return () => {\r\n api.off(\"select\", onSelect);\r\n };\r\n }, [api]);\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <header className=\"mb-12 text-center\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"What Our Customers Say\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"Don't just take our word for it. Here's what our customers have to say about their experience.\")}\r\n </p>\r\n </header>\r\n\r\n <Carousel\r\n setApi={setApi}\r\n opts={{\r\n align: \"start\",\r\n loop: true,\r\n }}\r\n className=\"w-full max-w-6xl mx-auto\"\r\n >\r\n <CarouselContent className=\"-ml-4\">\r\n {testimonials.map((testimonial) => (\r\n <CarouselItem\r\n key={testimonial.id}\r\n className=\"pl-4 basis-full sm:basis-1/2 lg:basis-1/3\"\r\n >\r\n <Card className=\"h-full\">\r\n <CardHeader>\r\n <div className=\"flex items-center gap-4\">\r\n <Avatar className=\"h-12 w-12\">\r\n <AvatarImage\r\n src={testimonial.image}\r\n alt={testimonial.name}\r\n />\r\n <AvatarFallback>\r\n {testimonial.name.charAt(0)}\r\n </AvatarFallback>\r\n </Avatar>\r\n <div>\r\n <CardTitle className=\"text-base\">\r\n {testimonial.name}\r\n </CardTitle>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {testimonial.role}\r\n </p>\r\n </div>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n <p className=\"text-muted-foreground text-sm leading-relaxed\">\r\n \"{testimonial.review}\"\r\n </p>\r\n </CardContent>\r\n <CardFooter>\r\n <div className=\"flex items-center gap-1\">\r\n {[...Array(5)].map((_, index) => (\r\n <Star\r\n key={index}\r\n className={cn(\r\n \"h-4 w-4\",\r\n index < testimonial.rating\r\n ? \"fill-yellow-400 text-yellow-400\"\r\n : \"fill-muted text-muted\"\r\n )}\r\n />\r\n ))}\r\n </div>\r\n </CardFooter>\r\n </Card>\r\n </CarouselItem>\r\n ))}\r\n </CarouselContent>\r\n <CarouselPrevious className=\"hidden lg:flex\" />\r\n <CarouselNext className=\"hidden lg:flex\" />\r\n </Carousel>\r\n\r\n {/* Dot Navigation */}\r\n <div className=\"flex items-center justify-center gap-2 mt-8\">\r\n {testimonials.map((_, index) => (\r\n <Button\r\n key={index}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => api?.scrollTo(index)}\r\n className={cn(\r\n \"h-2 rounded-full p-0 transition-all\",\r\n current === index\r\n ? \"w-6 bg-primary\"\r\n : \"w-2 bg-muted hover:bg-muted-foreground/20\"\r\n )}\r\n aria-label={`Go to slide ${index + 1}`}\r\n />\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
24
24
  },
25
25
  {
26
26
  "path": "testimonials-carousel/lang/en.json",
@@ -19,7 +19,7 @@
19
19
  "path": "testimonials-grid/testimonials-grid.tsx",
20
20
  "type": "registry:component",
21
21
  "target": "$modules$/testimonials-grid/testimonials-grid.tsx",
22
- "content": "import { Star, Quote } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\r\n\r\ninterface TestimonialsGridProps {\r\n className?: string;\r\n}\r\n\r\nexport function TestimonialsGrid({ className }: TestimonialsGridProps) {\r\n const { t } = useTranslation(\"testimonials-grid\");\r\n\r\n const testimonials = [\r\n {\r\n id: 1,\r\n name: t(\"testimonial1Name\", \"Alex Thompson\"),\r\n role: t(\"testimonial1Role\", \"CEO at TechStart\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial1Review\", \"Incredible experience from start to finish. The team went above and beyond to ensure we got exactly what we needed. Would highly recommend.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 2,\r\n name: t(\"testimonial2Name\", \"Maria Garcia\"),\r\n role: t(\"testimonial2Role\", \"Marketing Director\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial2Review\", \"The attention to detail is remarkable. Every aspect of the product shows careful thought and excellent execution. Our team loves it.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 3,\r\n name: t(\"testimonial3Name\", \"David Kim\"),\r\n role: t(\"testimonial3Role\", \"Lead Developer\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial3Review\", \"As a developer, I appreciate clean code and good documentation. This delivers on both fronts. Integration was seamless and support was excellent.\"),\r\n rating: 5,\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24 bg-muted/30\", className)}>\r\n <div className=\"container mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Trusted by Industry Leaders\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"See what professionals across various industries have to say about working with us.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto\">\r\n {testimonials.map((testimonial) => (\r\n <Card key={testimonial.id} className=\"relative overflow-hidden\">\r\n <CardContent className=\"pt-6\">\r\n {/* Quote Icon */}\r\n <Quote className=\"h-8 w-8 text-primary/20 mb-4\" />\r\n\r\n {/* Review */}\r\n <p className=\"text-muted-foreground mb-6 leading-relaxed\">\r\n \"{testimonial.review}\"\r\n </p>\r\n\r\n {/* Rating */}\r\n <div className=\"flex items-center gap-1 mb-4\">\r\n {[...Array(5)].map((_, index) => (\r\n <Star\r\n key={index}\r\n className={cn(\r\n \"h-4 w-4\",\r\n index < testimonial.rating\r\n ? \"fill-yellow-400 text-yellow-400\"\r\n : \"fill-muted text-muted\"\r\n )}\r\n />\r\n ))}\r\n </div>\r\n\r\n {/* Author */}\r\n <div className=\"flex items-center gap-3 pt-4 border-t\">\r\n <Avatar className=\"h-10 w-10\">\r\n <AvatarImage\r\n src={testimonial.image}\r\n alt={testimonial.name}\r\n />\r\n <AvatarFallback>\r\n {testimonial.name.charAt(0)}\r\n </AvatarFallback>\r\n </Avatar>\r\n <div>\r\n <p className=\"font-semibold text-sm\">{testimonial.name}</p>\r\n <p className=\"text-xs text-muted-foreground\">\r\n {testimonial.role}\r\n </p>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
22
+ "content": "import { Star, Quote } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\r\n\r\ninterface TestimonialsGridProps {\r\n className?: string;\r\n}\r\n\r\nexport function TestimonialsGrid({ className }: TestimonialsGridProps) {\r\n const { t } = useTranslation(\"testimonials-grid\");\r\n\r\n const testimonials = [\r\n {\r\n id: 1,\r\n name: t(\"testimonial1Name\", \"Alex Thompson\"),\r\n role: t(\"testimonial1Role\", \"CEO at TechStart\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial1Review\", \"Incredible experience from start to finish. The team went above and beyond to ensure we got exactly what we needed. Would highly recommend.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 2,\r\n name: t(\"testimonial2Name\", \"Maria Garcia\"),\r\n role: t(\"testimonial2Role\", \"Marketing Director\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial2Review\", \"The attention to detail is remarkable. Every aspect of the product shows careful thought and excellent execution. Our team loves it.\"),\r\n rating: 5,\r\n },\r\n {\r\n id: 3,\r\n name: t(\"testimonial3Name\", \"David Kim\"),\r\n role: t(\"testimonial3Role\", \"Lead Developer\"),\r\n image: \"/images/placeholder.png\",\r\n review: t(\"testimonial3Review\", \"As a developer, I appreciate clean code and good documentation. This delivers on both fronts. Integration was seamless and support was excellent.\"),\r\n rating: 5,\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24 bg-muted/30\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Trusted by Industry Leaders\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"See what professionals across various industries have to say about working with us.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto\">\r\n {testimonials.map((testimonial) => (\r\n <Card key={testimonial.id} className=\"relative overflow-hidden\">\r\n <CardContent className=\"pt-6\">\r\n {/* Quote Icon */}\r\n <Quote className=\"h-8 w-8 text-primary/20 mb-4\" />\r\n\r\n {/* Review */}\r\n <p className=\"text-muted-foreground mb-6 leading-relaxed\">\r\n \"{testimonial.review}\"\r\n </p>\r\n\r\n {/* Rating */}\r\n <div className=\"flex items-center gap-1 mb-4\">\r\n {[...Array(5)].map((_, index) => (\r\n <Star\r\n key={index}\r\n className={cn(\r\n \"h-4 w-4\",\r\n index < testimonial.rating\r\n ? \"fill-yellow-400 text-yellow-400\"\r\n : \"fill-muted text-muted\"\r\n )}\r\n />\r\n ))}\r\n </div>\r\n\r\n {/* Author */}\r\n <div className=\"flex items-center gap-3 pt-4 border-t\">\r\n <Avatar className=\"h-10 w-10\">\r\n <AvatarImage\r\n src={testimonial.image}\r\n alt={testimonial.name}\r\n />\r\n <AvatarFallback>\r\n {testimonial.name.charAt(0)}\r\n </AvatarFallback>\r\n </Avatar>\r\n <div>\r\n <p className=\"font-semibold text-sm\">{testimonial.name}</p>\r\n <p className=\"text-xs text-muted-foreground\">\r\n {testimonial.role}\r\n </p>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
23
23
  },
24
24
  {
25
25
  "path": "testimonials-grid/lang/en.json",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promakeai/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "promake": "dist/index.js"
@@ -1,32 +1,11 @@
1
1
  import { TooltipProvider } from "@/components/ui/tooltip";
2
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
- import { DBQueryProvider } from "./modules/db";
4
2
  import { Router } from "./router";
5
- import { usePageTitle } from "./hooks/use-page-title";
6
- import { useEffect } from "react";
7
- import { useTranslation } from "react-i18next";
8
- import { customerClient } from "./modules/api/customer-client";
9
-
10
- const queryClient = new QueryClient();
11
3
 
12
4
  const App = () => {
13
- const {
14
- i18n: { language },
15
- } = useTranslation();
16
- usePageTitle();
17
-
18
- useEffect(() => {
19
- customerClient.setLanguage(language);
20
- }, [language]);
21
-
22
5
  return (
23
- <QueryClientProvider client={queryClient}>
24
- <DBQueryProvider>
25
- <TooltipProvider>
26
- <Router />
27
- </TooltipProvider>
28
- </DBQueryProvider>
29
- </QueryClientProvider>
6
+ <TooltipProvider>
7
+ <Router />
8
+ </TooltipProvider>
30
9
  );
31
10
  };
32
11
 
@@ -1,7 +1,5 @@
1
1
  import { type ReactNode, useEffect } from "react";
2
2
  import { useLocation } from "react-router";
3
- import { Header } from "./Header";
4
- import { Footer } from "./Footer";
5
3
 
6
4
  interface LayoutProps {
7
5
  children: ReactNode;
@@ -17,9 +15,7 @@ export function Layout({ children }: LayoutProps) {
17
15
 
18
16
  return (
19
17
  <div className="min-h-screen flex flex-col">
20
- <Header />
21
18
  <main className="flex-1">{children}</main>
22
- <Footer />
23
19
  </div>
24
20
  );
25
21
  }
@@ -47,6 +47,7 @@
47
47
  }
48
48
 
49
49
  :root {
50
+ --container-max-width: 80rem;
50
51
  --radius: 0.625rem;
51
52
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
52
53
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;