@promakeai/cli 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +212 -0
- package/dist/registry/about-page.json +45 -0
- package/dist/registry/about-section.json +40 -0
- package/dist/registry/animations.json +69 -0
- package/dist/registry/bento-grid-section.json +42 -0
- package/dist/registry/blog-core.json +74 -0
- package/dist/registry/blog-list-page.json +48 -0
- package/dist/registry/blog-section.json +43 -0
- package/dist/registry/cards-carousel-section.json +46 -0
- package/dist/registry/cart-drawer.json +43 -0
- package/dist/registry/cart-page.json +47 -0
- package/dist/registry/category-section.json +43 -0
- package/dist/registry/checkout-page.json +47 -0
- package/dist/registry/contact-info-grid.json +40 -0
- package/dist/registry/contact-page-centered.json +50 -0
- package/dist/registry/contact-page-map-overlay.json +54 -0
- package/dist/registry/contact-page-map-split.json +54 -0
- package/dist/registry/contact-page-split.json +49 -0
- package/dist/registry/contact-page.json +45 -0
- package/dist/registry/content-section.json +40 -0
- package/dist/registry/cookies-page.json +45 -0
- package/dist/registry/cta-section.json +40 -0
- package/dist/registry/docs/about-page.md +32 -0
- package/dist/registry/docs/about-section.md +33 -0
- package/dist/registry/docs/animations.md +44 -0
- package/dist/registry/docs/bento-grid-section.md +40 -0
- package/dist/registry/docs/blog-core.md +37 -0
- package/dist/registry/docs/blog-list-page.md +38 -0
- package/dist/registry/docs/blog-section.md +39 -0
- package/dist/registry/docs/cards-carousel-section.md +39 -0
- package/dist/registry/docs/cart-drawer.md +42 -0
- package/dist/registry/docs/cart-page.md +37 -0
- package/dist/registry/docs/category-section.md +34 -0
- package/dist/registry/docs/checkout-page.md +38 -0
- package/dist/registry/docs/contact-info-grid.md +33 -0
- package/dist/registry/docs/contact-page-centered.md +41 -0
- package/dist/registry/docs/contact-page-map-overlay.md +44 -0
- package/dist/registry/docs/contact-page-map-split.md +44 -0
- package/dist/registry/docs/contact-page-split.md +40 -0
- package/dist/registry/docs/contact-page.md +33 -0
- package/dist/registry/docs/content-section.md +35 -0
- package/dist/registry/docs/cookies-page.md +32 -0
- package/dist/registry/docs/cta-section.md +32 -0
- package/dist/registry/docs/ecommerce-core.md +41 -0
- package/dist/registry/docs/empty-page.md +31 -0
- package/dist/registry/docs/faq-categorized.md +38 -0
- package/dist/registry/docs/faq-simple.md +38 -0
- package/dist/registry/docs/favorites-blog-block.md +38 -0
- package/dist/registry/docs/favorites-ecommerce-block.md +38 -0
- package/dist/registry/docs/feature-section.md +33 -0
- package/dist/registry/docs/featured-products.md +38 -0
- package/dist/registry/docs/footer-detailed.md +33 -0
- package/dist/registry/docs/footer-minimal.md +32 -0
- package/dist/registry/docs/footer.md +32 -0
- package/dist/registry/docs/google-map.md +36 -0
- package/dist/registry/docs/header-centered-pill.md +37 -0
- package/dist/registry/docs/header-ecommerce.md +38 -0
- package/dist/registry/docs/header-mega.md +40 -0
- package/dist/registry/docs/header-minimal.md +38 -0
- package/dist/registry/docs/header-simple.md +32 -0
- package/dist/registry/docs/hero-cta.md +38 -0
- package/dist/registry/docs/hero-gradient.md +33 -0
- package/dist/registry/docs/hero-grid.md +40 -0
- package/dist/registry/docs/hero-profile.md +33 -0
- package/dist/registry/docs/hero.md +32 -0
- package/dist/registry/docs/login-page-split.md +40 -0
- package/dist/registry/docs/login-page.md +39 -0
- package/dist/registry/docs/newsletter-section.md +40 -0
- package/dist/registry/docs/order-card-compact.md +37 -0
- package/dist/registry/docs/order-detail-block.md +37 -0
- package/dist/registry/docs/orders-list-block.md +40 -0
- package/dist/registry/docs/payment-success-block.md +32 -0
- package/dist/registry/docs/post-card.md +37 -0
- package/dist/registry/docs/post-detail-block.md +37 -0
- package/dist/registry/docs/pricing-card.md +37 -0
- package/dist/registry/docs/pricing-section.md +39 -0
- package/dist/registry/docs/privacy-page.md +32 -0
- package/dist/registry/docs/product-card-detailed.md +42 -0
- package/dist/registry/docs/product-card-hover.md +35 -0
- package/dist/registry/docs/product-card.md +37 -0
- package/dist/registry/docs/product-detail-block.md +37 -0
- package/dist/registry/docs/product-detail-section.md +45 -0
- package/dist/registry/docs/products-page.md +39 -0
- package/dist/registry/docs/related-posts-block.md +38 -0
- package/dist/registry/docs/related-products-block.md +38 -0
- package/dist/registry/docs/service-card.md +34 -0
- package/dist/registry/docs/skill-card.md +33 -0
- package/dist/registry/docs/terms-page.md +32 -0
- package/dist/registry/docs/testimonials-carousel.md +40 -0
- package/dist/registry/docs/testimonials-grid.md +39 -0
- package/dist/registry/ecommerce-core.json +95 -0
- package/dist/registry/empty-page.json +45 -0
- package/dist/registry/faq-categorized.json +42 -0
- package/dist/registry/faq-simple.json +42 -0
- package/dist/registry/favorites-blog-block.json +43 -0
- package/dist/registry/favorites-ecommerce-block.json +43 -0
- package/dist/registry/feature-section.json +40 -0
- package/dist/registry/featured-products.json +43 -0
- package/dist/registry/footer-detailed.json +43 -0
- package/dist/registry/footer-minimal.json +40 -0
- package/dist/registry/footer.json +40 -0
- package/dist/registry/google-map.json +31 -0
- package/dist/registry/header-centered-pill.json +45 -0
- package/dist/registry/header-ecommerce.json +42 -0
- package/dist/registry/header-mega.json +47 -0
- package/dist/registry/header-minimal.json +45 -0
- package/dist/registry/header-simple.json +43 -0
- package/dist/registry/hero-cta.json +42 -0
- package/dist/registry/hero-gradient.json +40 -0
- package/dist/registry/hero-grid.json +42 -0
- package/dist/registry/hero-profile.json +62 -0
- package/dist/registry/hero.json +40 -0
- package/dist/registry/index.json +70 -0
- package/dist/registry/login-page-split.json +47 -0
- package/dist/registry/login-page.json +49 -0
- package/dist/registry/newsletter-section.json +44 -0
- package/dist/registry/order-card-compact.json +42 -0
- package/dist/registry/order-detail-block.json +42 -0
- package/dist/registry/orders-list-block.json +45 -0
- package/dist/registry/payment-success-block.json +40 -0
- package/dist/registry/post-card.json +42 -0
- package/dist/registry/post-detail-block.json +42 -0
- package/dist/registry/pricing-card.json +40 -0
- package/dist/registry/pricing-section.json +43 -0
- package/dist/registry/privacy-page.json +45 -0
- package/dist/registry/product-card-detailed.json +45 -0
- package/dist/registry/product-card-hover.json +40 -0
- package/dist/registry/product-card.json +42 -0
- package/dist/registry/product-detail-block.json +42 -0
- package/dist/registry/product-detail-section.json +46 -0
- package/dist/registry/products-page.json +48 -0
- package/dist/registry/related-posts-block.json +43 -0
- package/dist/registry/related-products-block.json +43 -0
- package/dist/registry/service-card.json +28 -0
- package/dist/registry/skill-card.json +28 -0
- package/dist/registry/terms-page.json +45 -0
- package/dist/registry/testimonials-carousel.json +44 -0
- package/dist/registry/testimonials-grid.json +43 -0
- package/package.json +52 -0
- package/template/.env +6 -0
- package/template/.prettierignore +3 -0
- package/template/.prettierrc +1 -0
- package/template/README.md +73 -0
- package/template/bun.lock +1007 -0
- package/template/components.json +22 -0
- package/template/eslint.config.js +32 -0
- package/template/index.html +285 -0
- package/template/package.json +92 -0
- package/template/promake.json +6 -0
- package/template/public/_redirects +1 -0
- package/template/public/data/database.db +0 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/images/placeholder.png +0 -0
- package/template/public/robots.txt +14 -0
- package/template/scripts/init-db.ts +131 -0
- package/template/src/App.tsx +33 -0
- package/template/src/components/Footer.tsx +100 -0
- package/template/src/components/Header.tsx +79 -0
- package/template/src/components/Hero.tsx +69 -0
- package/template/src/components/LanguageSwitcher.tsx +47 -0
- package/template/src/components/Layout.tsx +25 -0
- package/template/src/components/Logo.tsx +64 -0
- package/template/src/components/ThemeSwitcher.tsx +58 -0
- package/template/src/components/ui/accordion.tsx +64 -0
- package/template/src/components/ui/alert-dialog.tsx +155 -0
- package/template/src/components/ui/alert.tsx +66 -0
- package/template/src/components/ui/aspect-ratio.tsx +11 -0
- package/template/src/components/ui/avatar.tsx +51 -0
- package/template/src/components/ui/badge.tsx +46 -0
- package/template/src/components/ui/breadcrumb.tsx +109 -0
- package/template/src/components/ui/button-group.tsx +83 -0
- package/template/src/components/ui/button.tsx +62 -0
- package/template/src/components/ui/calendar.tsx +220 -0
- package/template/src/components/ui/card.tsx +92 -0
- package/template/src/components/ui/carousel.tsx +239 -0
- package/template/src/components/ui/chart.tsx +357 -0
- package/template/src/components/ui/checkbox.tsx +32 -0
- package/template/src/components/ui/collapsible.tsx +31 -0
- package/template/src/components/ui/command.tsx +182 -0
- package/template/src/components/ui/context-menu.tsx +252 -0
- package/template/src/components/ui/dialog.tsx +141 -0
- package/template/src/components/ui/drawer.tsx +135 -0
- package/template/src/components/ui/dropdown-menu.tsx +255 -0
- package/template/src/components/ui/empty.tsx +104 -0
- package/template/src/components/ui/field.tsx +246 -0
- package/template/src/components/ui/form.tsx +168 -0
- package/template/src/components/ui/hover-card.tsx +44 -0
- package/template/src/components/ui/input-group.tsx +170 -0
- package/template/src/components/ui/input-otp.tsx +75 -0
- package/template/src/components/ui/input.tsx +21 -0
- package/template/src/components/ui/item.tsx +193 -0
- package/template/src/components/ui/kbd.tsx +28 -0
- package/template/src/components/ui/label.tsx +24 -0
- package/template/src/components/ui/menubar.tsx +274 -0
- package/template/src/components/ui/navigation-menu.tsx +168 -0
- package/template/src/components/ui/pagination.tsx +127 -0
- package/template/src/components/ui/popover.tsx +48 -0
- package/template/src/components/ui/progress.tsx +29 -0
- package/template/src/components/ui/radio-group.tsx +45 -0
- package/template/src/components/ui/resizable.tsx +54 -0
- package/template/src/components/ui/scroll-area.tsx +58 -0
- package/template/src/components/ui/select.tsx +188 -0
- package/template/src/components/ui/separator.tsx +28 -0
- package/template/src/components/ui/sheet.tsx +137 -0
- package/template/src/components/ui/sidebar.tsx +726 -0
- package/template/src/components/ui/skeleton.tsx +13 -0
- package/template/src/components/ui/slider.tsx +63 -0
- package/template/src/components/ui/sonner.tsx +38 -0
- package/template/src/components/ui/spinner.tsx +16 -0
- package/template/src/components/ui/switch.tsx +31 -0
- package/template/src/components/ui/table.tsx +114 -0
- package/template/src/components/ui/tabs.tsx +66 -0
- package/template/src/components/ui/textarea.tsx +18 -0
- package/template/src/components/ui/toggle-group.tsx +81 -0
- package/template/src/components/ui/toggle.tsx +45 -0
- package/template/src/components/ui/tooltip.tsx +61 -0
- package/template/src/constants/constants.json +58 -0
- package/template/src/hooks/use-is-mobile.ts +21 -0
- package/template/src/hooks/use-page-title.ts +49 -0
- package/template/src/hooks/use-theme.ts +57 -0
- package/template/src/index.css +128 -0
- package/template/src/lang/en/about.json +4 -0
- package/template/src/lang/en/contact.json +39 -0
- package/template/src/lang/en/cookies.json +4 -0
- package/template/src/lang/en/footer.json +12 -0
- package/template/src/lang/en/forgotPassword.json +37 -0
- package/template/src/lang/en/header.json +10 -0
- package/template/src/lang/en/hero.json +8 -0
- package/template/src/lang/en/index.json +30 -0
- package/template/src/lang/en/login.json +18 -0
- package/template/src/lang/en/notfound.json +7 -0
- package/template/src/lang/en/privacy.json +4 -0
- package/template/src/lang/en/register.json +25 -0
- package/template/src/lang/en/terms.json +4 -0
- package/template/src/lang/index.ts +86 -0
- package/template/src/lang/tr/about.json +4 -0
- package/template/src/lang/tr/contact.json +39 -0
- package/template/src/lang/tr/cookies.json +4 -0
- package/template/src/lang/tr/footer.json +12 -0
- package/template/src/lang/tr/forgotPassword.json +37 -0
- package/template/src/lang/tr/header.json +10 -0
- package/template/src/lang/tr/hero.json +8 -0
- package/template/src/lang/tr/index.json +30 -0
- package/template/src/lang/tr/login.json +18 -0
- package/template/src/lang/tr/notfound.json +7 -0
- package/template/src/lang/tr/privacy.json +4 -0
- package/template/src/lang/tr/register.json +25 -0
- package/template/src/lang/tr/terms.json +4 -0
- package/template/src/lib/api.ts +237 -0
- package/template/src/lib/storage.ts +109 -0
- package/template/src/lib/utils.ts +15 -0
- package/template/src/main.tsx +13 -0
- package/template/src/modules/api/USAGE.md +515 -0
- package/template/src/modules/api/customer-client.ts +20 -0
- package/template/src/modules/api/get-error-message.ts +18 -0
- package/template/src/modules/api/validation/en.json +29 -0
- package/template/src/modules/api/validation/tr.json +29 -0
- package/template/src/modules/auth/USAGE.md +248 -0
- package/template/src/modules/auth/auth-header-menu.tsx +123 -0
- package/template/src/modules/auth/auth-store.ts +57 -0
- package/template/src/modules/auth/forgot-password-page.tsx +371 -0
- package/template/src/modules/auth/login-page.tsx +183 -0
- package/template/src/modules/auth/register-page.tsx +252 -0
- package/template/src/modules/auth/use-auth.ts +273 -0
- package/template/src/modules/db/adapters/IDataAdapter.ts +26 -0
- package/template/src/modules/db/adapters/SqliteAdapter.ts +364 -0
- package/template/src/modules/db/adapters/index.ts +2 -0
- package/template/src/modules/db/config.ts +59 -0
- package/template/src/modules/db/core/DataManager.ts +125 -0
- package/template/src/modules/db/core/types.ts +101 -0
- package/template/src/modules/db/index.ts +42 -0
- package/template/src/modules/db/react/QueryProvider.tsx +16 -0
- package/template/src/modules/db/react/index.ts +23 -0
- package/template/src/modules/db/react/queryClient.ts +64 -0
- package/template/src/modules/db/react/useRepository.ts +400 -0
- package/template/src/modules/db/utils/parsers.ts +96 -0
- package/template/src/pages/Index.tsx +108 -0
- package/template/src/pages/NotFound.tsx +35 -0
- package/template/src/router.tsx +14 -0
- package/template/src/types/index.ts +0 -0
- package/template/src/vite-env.d.ts +1 -0
- package/template/tsconfig.app.json +32 -0
- package/template/tsconfig.json +17 -0
- package/template/tsconfig.node.json +26 -0
- package/template/vite.config.ts +74 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "login-page",
|
|
3
|
+
"type": "registry:page",
|
|
4
|
+
"title": "Login Page",
|
|
5
|
+
"description": "Beautiful login page with email/password form, social OAuth buttons (Google, Microsoft), forgot password link, and create account link. Centered card layout with responsive design.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"button",
|
|
8
|
+
"input",
|
|
9
|
+
"label"
|
|
10
|
+
],
|
|
11
|
+
"usage": "import LoginPage from '@/modules/login-page';\n\n<LoginPage\n onSubmit={(email, password) => console.log(email, password)}\n onGoogleLogin={() => console.log('Google')}\n onMicrosoftLogin={() => console.log('Microsoft')}\n/>",
|
|
12
|
+
"route": {
|
|
13
|
+
"path": "/login",
|
|
14
|
+
"componentName": "LoginPage"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
{
|
|
18
|
+
"path": "login-page/index.ts",
|
|
19
|
+
"type": "registry:index",
|
|
20
|
+
"target": "$modules$/login-page/index.ts",
|
|
21
|
+
"content": "export { default } from './login-page';\r\nexport { default as LoginPage } from './login-page';\r\n"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "login-page/login-page.tsx",
|
|
25
|
+
"type": "registry:page",
|
|
26
|
+
"target": "$modules$/login-page/login-page.tsx",
|
|
27
|
+
"content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface LoginPageProps {\r\n onSubmit?: (email: string, password: string) => void;\r\n onGoogleLogin?: () => void;\r\n onMicrosoftLogin?: () => void;\r\n className?: string;\r\n}\r\n\r\nexport default function LoginPage({\r\n onSubmit,\r\n onGoogleLogin,\r\n onMicrosoftLogin,\r\n className,\r\n}: LoginPageProps) {\r\n const { t } = useTranslation(\"login-page\");\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n onSubmit?.(email, password);\r\n };\r\n\r\n return (\r\n <section className={cn(\"flex min-h-screen bg-muted/30 px-4 py-16 md:py-32\", className)}>\r\n <form\r\n onSubmit={handleSubmit}\r\n className=\"bg-muted m-auto h-fit w-full max-w-sm overflow-hidden rounded-xl border shadow-md\"\r\n >\r\n <div className=\"bg-card -m-px rounded-xl border p-8 pb-6\">\r\n <div className=\"text-center\">\r\n <Link to=\"/\" aria-label=\"go home\" className=\"mx-auto block w-fit\">\r\n <Logo size=\"sm\" />\r\n </Link>\r\n <h1 className=\"mb-1 mt-4 text-xl font-semibold\">\r\n {t(\"title\", \"Sign In\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"subtitle\", \"Welcome back! Sign in to continue\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"mt-6 space-y-6\">\r\n <div className=\"space-y-2\">\r\n <Label htmlFor=\"email\" className=\"block text-sm\">\r\n {t(\"email\", \"Email\")}\r\n </Label>\r\n <Input\r\n type=\"email\"\r\n required\r\n name=\"email\"\r\n id=\"email\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n placeholder={t(\"emailPlaceholder\", \"you@example.com\")}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <Label htmlFor=\"password\" className=\"text-sm\">\r\n {t(\"password\", \"Password\")}\r\n </Label>\r\n <Button asChild variant=\"link\" size=\"sm\" className=\"h-auto p-0\">\r\n <Link to=\"/forgot-password\" className=\"text-xs\">\r\n {t(\"forgotPassword\", \"Forgot password?\")}\r\n </Link>\r\n </Button>\r\n </div>\r\n <Input\r\n type=\"password\"\r\n required\r\n name=\"password\"\r\n id=\"password\"\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n placeholder=\"••••••••\"\r\n />\r\n </div>\r\n\r\n <Button type=\"submit\" className=\"w-full\">\r\n {t(\"signIn\", \"Sign In\")}\r\n </Button>\r\n </div>\r\n\r\n <div className=\"my-6 grid grid-cols-[1fr_auto_1fr] items-center gap-3\">\r\n <hr className=\"border-dashed\" />\r\n <span className=\"text-muted-foreground text-xs\">\r\n {t(\"orContinueWith\", \"Or continue with\")}\r\n </span>\r\n <hr className=\"border-dashed\" />\r\n </div>\r\n\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <Button type=\"button\" variant=\"outline\" onClick={onGoogleLogin}>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"0.98em\"\r\n height=\"1em\"\r\n viewBox=\"0 0 256 262\"\r\n className=\"mr-2\"\r\n >\r\n <path\r\n fill=\"#4285f4\"\r\n d=\"M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027\"\r\n />\r\n <path\r\n fill=\"#34a853\"\r\n d=\"M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1\"\r\n />\r\n <path\r\n fill=\"#fbbc05\"\r\n d=\"M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z\"\r\n />\r\n <path\r\n fill=\"#eb4335\"\r\n d=\"M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251\"\r\n />\r\n </svg>\r\n <span>Google</span>\r\n </Button>\r\n <Button type=\"button\" variant=\"outline\" onClick={onMicrosoftLogin}>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"1em\"\r\n height=\"1em\"\r\n viewBox=\"0 0 256 256\"\r\n className=\"mr-2\"\r\n >\r\n <path fill=\"#f1511b\" d=\"M121.666 121.666H0V0h121.666z\" />\r\n <path fill=\"#80cc28\" d=\"M256 121.666H134.335V0H256z\" />\r\n <path fill=\"#00adef\" d=\"M121.663 256.002H0V134.336h121.663z\" />\r\n <path fill=\"#fbbc09\" d=\"M256 256.002H134.335V134.336H256z\" />\r\n </svg>\r\n <span>Microsoft</span>\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"p-3\">\r\n <p className=\"text-center text-sm text-muted-foreground\">\r\n {t(\"noAccount\", \"Don't have an account?\")}\r\n <Button asChild variant=\"link\" className=\"px-2\">\r\n <Link to=\"/register\">{t(\"createAccount\", \"Create account\")}</Link>\r\n </Button>\r\n </p>\r\n </div>\r\n </form>\r\n </section>\r\n );\r\n}\r\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "login-page/lang/en.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/login-page/lang/en.json",
|
|
33
|
+
"content": "{\r\n \"title\": \"Sign In\",\r\n \"subtitle\": \"Welcome back! Sign in to continue\",\r\n \"email\": \"Email\",\r\n \"emailPlaceholder\": \"you@example.com\",\r\n \"password\": \"Password\",\r\n \"forgotPassword\": \"Forgot password?\",\r\n \"signIn\": \"Sign In\",\r\n \"orContinueWith\": \"Or continue with\",\r\n \"noAccount\": \"Don't have an account?\",\r\n \"createAccount\": \"Create account\"\r\n}\r\n"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"path": "login-page/lang/tr.json",
|
|
37
|
+
"type": "registry:lang",
|
|
38
|
+
"target": "$modules$/login-page/lang/tr.json",
|
|
39
|
+
"content": "{\r\n \"title\": \"Giriş Yap\",\r\n \"subtitle\": \"Tekrar hoş geldiniz! Devam etmek için giriş yapın\",\r\n \"email\": \"E-posta\",\r\n \"emailPlaceholder\": \"ornek@email.com\",\r\n \"password\": \"Şifre\",\r\n \"forgotPassword\": \"Şifremi unuttum\",\r\n \"signIn\": \"Giriş Yap\",\r\n \"orContinueWith\": \"Veya şununla devam et\",\r\n \"noAccount\": \"Hesabınız yok mu?\",\r\n \"createAccount\": \"Hesap oluştur\"\r\n}\r\n"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"exports": {
|
|
43
|
+
"types": [],
|
|
44
|
+
"variables": [
|
|
45
|
+
"LoginPage",
|
|
46
|
+
"default"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "newsletter-section",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Newsletter Section",
|
|
5
|
+
"description": "Newsletter subscription section with image and email form. Features a two-column card layout with customizable image on the left and centered subscription form on the right. Accent background with clean, minimal design.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"card",
|
|
8
|
+
"input",
|
|
9
|
+
"button"
|
|
10
|
+
],
|
|
11
|
+
"usage": "import { NewsletterSection } from '@/modules/newsletter-section';\n\n<NewsletterSection\n image=\"/images/newsletter.jpg\"\n/>\n\n• Installed at: src/modules/newsletter-section/\n• Customize text: src/modules/newsletter-section/lang/*.json",
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "newsletter-section/index.ts",
|
|
15
|
+
"type": "registry:index",
|
|
16
|
+
"target": "$modules$/newsletter-section/index.ts",
|
|
17
|
+
"content": "export * from './newsletter-section';\r\n"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "newsletter-section/newsletter-section.tsx",
|
|
21
|
+
"type": "registry:component",
|
|
22
|
+
"target": "$modules$/newsletter-section/newsletter-section.tsx",
|
|
23
|
+
"content": "import { Button } from \"@/components/ui/button\";\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { useTranslation } from \"react-i18next\";\r\n\r\ninterface NewsletterSectionProps {\r\n image?: string;\r\n className?: string;\r\n}\r\n\r\nexport function NewsletterSection({\r\n image = \"/images/placeholder.png\",\r\n className,\r\n}: NewsletterSectionProps) {\r\n const { t } = useTranslation(\"newsletter-section\");\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n // Handle newsletter subscription\r\n };\r\n\r\n return (\r\n <section className={`px-4 py-20 bg-accent ${className || \"\"}`}>\r\n <div className=\"mx-auto max-w-6xl\">\r\n <Card className=\"overflow-hidden shadow-none p-0\">\r\n <CardContent className=\"p-0\">\r\n <div className=\"grid md:grid-cols-2 items-center\">\r\n <img\r\n loading=\"lazy\"\r\n decoding=\"async\"\r\n src={image}\r\n alt={t(\"imageAlt\", \"Newsletter\")}\r\n width={1200}\r\n height={800}\r\n className=\"h-64 w-full object-cover md:h-full\"\r\n />\r\n <form\r\n onSubmit={handleSubmit}\r\n className=\"flex flex-col gap-4 p-8 max-w-md w-full mx-auto\"\r\n >\r\n <div className=\"space-y-1 text-center\">\r\n <h2 className=\"text-2xl font-bold tracking-tight\">\r\n {t(\"title\", \"Get the inside scoop\")}\r\n </h2>\r\n <p className=\"text-sm text-muted-foreground\">\r\n {t(\"description\", \"Join our newsletter to stay ahead.\")}\r\n </p>\r\n </div>\r\n <div className=\"grid gap-2 text-left\">\r\n <Label htmlFor=\"newsletter-email\">\r\n {t(\"emailLabel\", \"Email\")}\r\n </Label>\r\n <Input\r\n id=\"newsletter-email\"\r\n type=\"email\"\r\n placeholder={t(\"emailPlaceholder\", \"Enter your email\")}\r\n required\r\n />\r\n </div>\r\n <Button type=\"submit\">{t(\"subscribe\", \"Subscribe\")}</Button>\r\n </form>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"path": "newsletter-section/lang/en.json",
|
|
27
|
+
"type": "registry:lang",
|
|
28
|
+
"target": "$modules$/newsletter-section/lang/en.json",
|
|
29
|
+
"content": "{\r\n \"title\": \"Get the inside scoop\",\r\n \"description\": \"Join our newsletter to stay ahead.\",\r\n \"emailLabel\": \"Email\",\r\n \"emailPlaceholder\": \"Enter your email\",\r\n \"subscribe\": \"Subscribe\",\r\n \"imageAlt\": \"Newsletter\"\r\n}\r\n"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"path": "newsletter-section/lang/tr.json",
|
|
33
|
+
"type": "registry:lang",
|
|
34
|
+
"target": "$modules$/newsletter-section/lang/tr.json",
|
|
35
|
+
"content": "{\r\n \"title\": \"Haberdar Olun\",\r\n \"description\": \"Bültenimize katılarak güncel kalın.\",\r\n \"emailLabel\": \"E-posta\",\r\n \"emailPlaceholder\": \"E-posta adresinizi girin\",\r\n \"subscribe\": \"Abone Ol\",\r\n \"imageAlt\": \"Bülten\"\r\n}\r\n"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"exports": {
|
|
39
|
+
"types": [],
|
|
40
|
+
"variables": [
|
|
41
|
+
"NewsletterSection"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "order-card-compact",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Order Card Compact",
|
|
5
|
+
"description": "Compact order card component for displaying order summary with status badge, items preview, and quick actions",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core"
|
|
8
|
+
],
|
|
9
|
+
"usage": "import { OrderCardCompact } from '@/modules/order-card-compact';\n\n<OrderCardCompact order={order} />\n\n• Installed at: src/modules/order-card-compact/\n• Shows status, items preview, date, total\n• Use in orders list or dashboard",
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "order-card-compact/index.ts",
|
|
13
|
+
"type": "registry:index",
|
|
14
|
+
"target": "$modules$/order-card-compact/index.ts",
|
|
15
|
+
"content": "export * from './order-card-compact';\r\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "order-card-compact/order-card-compact.tsx",
|
|
19
|
+
"type": "registry:component",
|
|
20
|
+
"target": "$modules$/order-card-compact/order-card-compact.tsx",
|
|
21
|
+
"content": "import { Link } from \"react-router\";\nimport {\n CheckCircle2,\n Clock,\n Truck,\n XCircle,\n Package,\n Calendar,\n CreditCard,\n} from \"lucide-react\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { useTranslation } from \"react-i18next\";\nimport constants from \"@/constants/constants.json\";\nimport { formatPrice } from \"@/modules/ecommerce-core/format-price\";\n\ninterface OrderCardItem {\n id?: string | number;\n name?: string;\n quantity?: number;\n image?: string;\n}\n\ninterface OrderCardProps {\n id: string | number;\n orderNumber?: string;\n status: string;\n createdAt?: string;\n totalAmount?: number;\n items?: OrderCardItem[];\n itemCount?: number;\n paymentMethod?: string;\n onViewDetails?: () => void;\n detailsLink?: string;\n}\n\nexport function OrderCardCompact({\n id,\n orderNumber,\n status,\n createdAt,\n totalAmount,\n items = [],\n itemCount,\n paymentMethod,\n onViewDetails,\n detailsLink,\n}: OrderCardProps) {\n const { t } = useTranslation(\"order-card-compact\");\n\n const getStatusIcon = (orderStatus: string) => {\n const statusLower = orderStatus.toLowerCase();\n if (\n statusLower === \"paid\" ||\n statusLower === \"completed\" ||\n statusLower === \"tamamlandı\"\n ) {\n return <CheckCircle2 className=\"w-4 h-4\" />;\n }\n if (statusLower === \"hazırlanıyor\" || statusLower === \"processing\") {\n return <Clock className=\"w-4 h-4\" />;\n }\n if (statusLower === \"kargoda\" || statusLower === \"shipped\") {\n return <Truck className=\"w-4 h-4\" />;\n }\n if (statusLower === \"cancelled\" || statusLower === \"iptal\") {\n return <XCircle className=\"w-4 h-4\" />;\n }\n return <Package className=\"w-4 h-4\" />;\n };\n\n const getStatusBadgeVariant = (\n orderStatus: string\n ): \"default\" | \"secondary\" | \"destructive\" | \"outline\" => {\n const statusLower = orderStatus.toLowerCase();\n if (\n statusLower === \"paid\" ||\n statusLower === \"completed\" ||\n statusLower === \"tamamlandı\"\n ) {\n return \"default\";\n }\n if (statusLower === \"cancelled\" || statusLower === \"iptal\") {\n return \"destructive\";\n }\n return \"secondary\";\n };\n\n const displayItems = items.slice(0, 3);\n const remainingItems = Math.max((items.length || itemCount || 0) - 3, 0);\n\n return (\n <Card className=\"group overflow-hidden border-0 shadow-sm hover:shadow-lg transition-all duration-300\">\n <CardContent className=\"p-6\">\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <Package className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-lg\">\n {t(\"orderNumber\", \"Order\")} #{orderNumber || id}\n </h3>\n {createdAt && (\n <p className=\"text-sm text-muted-foreground flex items-center gap-1\">\n <Calendar className=\"w-3 h-3\" />\n {new Date(createdAt).toLocaleDateString()}\n </p>\n )}\n </div>\n </div>\n <Badge\n variant={getStatusBadgeVariant(status)}\n className=\"flex items-center gap-1 w-fit\"\n >\n {getStatusIcon(status)}\n <span className=\"capitalize\">{status}</span>\n </Badge>\n </div>\n\n {/* Order Items Preview */}\n {displayItems.length > 0 && (\n <div className=\"mb-4\">\n <div className=\"flex gap-2\">\n {displayItems.map((item, index) =>\n item.image ? (\n <div\n key={item.id || index}\n className=\"w-16 h-16 rounded-lg overflow-hidden bg-muted border\"\n >\n <img\n src={item.image}\n alt={item.name || \"Product\"}\n className=\"w-full h-full object-cover\"\n />\n </div>\n ) : null\n )}\n {remainingItems > 0 && (\n <div className=\"w-16 h-16 rounded-lg bg-muted border flex items-center justify-center\">\n <span className=\"text-sm font-medium text-muted-foreground\">\n +{remainingItems}\n </span>\n </div>\n )}\n </div>\n <div className=\"mt-3 space-y-1 text-sm\">\n {displayItems.map((item, index) => (\n <div\n key={`${item.id || index}-summary`}\n className=\"flex items-center justify-between gap-4\"\n >\n <span className=\"text-muted-foreground line-clamp-1\">\n {item.name || t(\"item\", \"Item\")}\n </span>\n <span className=\"font-medium\">\n {t(\"qty\", \"Qty\")} {item.quantity || 1}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {/* Order Details */}\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4 pt-4 border-t\">\n <div className=\"flex flex-col sm:flex-row gap-4 text-sm\">\n {totalAmount !== undefined && (\n <div>\n <span className=\"text-muted-foreground\">\n {t(\"total\", \"Total\")}:\n </span>\n <span className=\"font-semibold ml-1\">\n {formatPrice(totalAmount, constants.site.currency)}\n </span>\n </div>\n )}\n {(itemCount !== undefined || items.length > 0) && (\n <div>\n <span className=\"text-muted-foreground\">\n {t(\"items\", \"Items\")}:\n </span>\n <span className=\"font-semibold ml-1\">\n {itemCount || items.length}\n </span>\n </div>\n )}\n {paymentMethod && (\n <div className=\"flex items-center gap-1\">\n <CreditCard className=\"w-3 h-3 text-muted-foreground\" />\n <span className=\"capitalize text-muted-foreground\">\n {paymentMethod}\n </span>\n </div>\n )}\n </div>\n\n {(onViewDetails || detailsLink) &&\n (detailsLink ? (\n <Button variant=\"outline\" size=\"sm\" asChild>\n <Link to={detailsLink}>{t(\"viewDetails\", \"View Details\")}</Link>\n </Button>\n ) : (\n <Button variant=\"outline\" size=\"sm\" onClick={onViewDetails}>\n {t(\"viewDetails\", \"View Details\")}\n </Button>\n ))}\n </div>\n </CardContent>\n </Card>\n );\n}\n"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "order-card-compact/lang/en.json",
|
|
25
|
+
"type": "registry:lang",
|
|
26
|
+
"target": "$modules$/order-card-compact/lang/en.json",
|
|
27
|
+
"content": "{\n \"orderNumber\": \"Order\",\n \"total\": \"Total\",\n \"items\": \"Items\",\n \"viewDetails\": \"View Details\"\n}\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "order-card-compact/lang/tr.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/order-card-compact/lang/tr.json",
|
|
33
|
+
"content": "{\n \"orderNumber\": \"Sipariş\",\n \"total\": \"Toplam\",\n \"items\": \"Ürünler\",\n \"viewDetails\": \"Detayları Gör\"\n}\n"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"types": [],
|
|
38
|
+
"variables": [
|
|
39
|
+
"OrderCardCompact"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "order-detail-block",
|
|
3
|
+
"type": "registry:block",
|
|
4
|
+
"title": "Order Detail Block",
|
|
5
|
+
"description": "Complete order confirmation view with order summary, status tracker, items list, delivery information, and payment instructions",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core"
|
|
8
|
+
],
|
|
9
|
+
"usage": "import { OrderDetailBlock } from '@/modules/order-detail-block';\n\n<OrderDetailBlock order={order} />\n\n• Installed at: src/modules/order-detail-block/\n• Displays order summary, status, items, delivery info\n• Use in order confirmation or order history pages",
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "order-detail-block/index.ts",
|
|
13
|
+
"type": "registry:index",
|
|
14
|
+
"target": "$modules$/order-detail-block/index.ts",
|
|
15
|
+
"content": "export * from './order-detail-block';\r\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "order-detail-block/order-detail-block.tsx",
|
|
19
|
+
"type": "registry:block",
|
|
20
|
+
"target": "$modules$/order-detail-block/order-detail-block.tsx",
|
|
21
|
+
"content": "import {\n CheckCircle,\n Package,\n Truck,\n CreditCard,\n MapPin,\n Clock,\n} from \"lucide-react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { useTranslation } from \"react-i18next\";\nimport constants from \"@/constants/constants.json\";\nimport { formatPrice } from \"@/modules/ecommerce-core/format-price\";\n\ninterface OrderItem {\n id: string | number;\n name: string;\n quantity: number;\n price: number;\n image?: string;\n}\n\ninterface OrderDetails {\n orderId?: string;\n orderDate?: string;\n status?: string;\n total: number;\n subtotal?: number;\n shipping?: number;\n tax?: number;\n paymentMethod: string;\n items: OrderItem[];\n customerInfo?: {\n name?: string;\n email?: string;\n phone?: string;\n address?: string;\n notes?: string;\n };\n estimatedDelivery?: string;\n}\n\ninterface OrderDetailBlockProps {\n orderDetails: OrderDetails;\n showSuccessHeader?: boolean;\n}\n\nexport function OrderDetailBlock({\n orderDetails,\n showSuccessHeader = true,\n}: OrderDetailBlockProps) {\n const { t } = useTranslation(\"order-detail-block\");\n\n const { paymentMethod, total, items, customerInfo, subtotal, shipping, tax } =\n orderDetails;\n\n return (\n <div className=\"max-w-4xl mx-auto\">\n {/* Success Header */}\n {showSuccessHeader && (\n <div className=\"text-center mb-8\">\n <div className=\"w-16 h-16 bg-green-500/20 dark:bg-green-500/10 rounded-full flex items-center justify-center mx-auto mb-4\">\n <CheckCircle className=\"w-8 h-8 text-green-600 dark:text-green-400\" />\n </div>\n <h1 className=\"text-3xl font-bold text-green-600 dark:text-green-400 mb-2\">\n {t(\"title\", \"Order Confirmed!\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"thankYou\",\n \"Thank you for your order! We've received your order and will process it shortly.\"\n )}\n </p>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-8\">\n {/* Order Details */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Order Info */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Package className=\"w-5 h-5\" />\n {t(\"orderInformation\", \"Order Information\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {orderDetails.orderId && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"orderId\", \"Order ID\")}:\n </span>\n <span className=\"font-medium\">{orderDetails.orderId}</span>\n </div>\n )}\n {orderDetails.orderDate && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"orderDate\", \"Order Date\")}:\n </span>\n <span className=\"font-medium\">\n {new Date(orderDetails.orderDate).toLocaleDateString()}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"totalAmount\", \"Total Amount\")}:\n </span>\n <span className=\"font-bold text-lg\">\n {formatPrice(total, constants.site.currency)}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"paymentMethod\", \"Payment Method\")}:\n </span>\n <span className=\"font-medium capitalize\">{paymentMethod}</span>\n </div>\n {orderDetails.status && (\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">\n {t(\"orderStatus\", \"Order Status\")}:\n </span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {orderDetails.status}\n </Badge>\n </div>\n )}\n {orderDetails.estimatedDelivery && (\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground flex items-center gap-1\">\n <Clock className=\"w-4 h-4\" />\n {t(\"estimatedDelivery\", \"Estimated Delivery\")}:\n </span>\n <span className=\"font-medium\">\n {orderDetails.estimatedDelivery}\n </span>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Delivery Information */}\n {customerInfo && (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <MapPin className=\"w-5 h-5\" />\n {t(\"deliveryInformation\", \"Delivery Information\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {customerInfo.name && (\n <div>\n <span className=\"text-sm text-muted-foreground block\">\n {t(\"customerName\", \"Name\")}:\n </span>\n <span className=\"font-medium\">{customerInfo.name}</span>\n </div>\n )}\n {customerInfo.email && (\n <div>\n <span className=\"text-sm text-muted-foreground block\">\n {t(\"email\", \"Email\")}:\n </span>\n <span className=\"font-medium\">{customerInfo.email}</span>\n </div>\n )}\n {customerInfo.phone && (\n <div>\n <span className=\"text-sm text-muted-foreground block\">\n {t(\"phone\", \"Phone\")}:\n </span>\n <span className=\"font-medium\">{customerInfo.phone}</span>\n </div>\n )}\n {customerInfo.address && (\n <div>\n <span className=\"text-sm text-muted-foreground block\">\n {t(\"address\", \"Address\")}:\n </span>\n <p className=\"font-medium\">{customerInfo.address}</p>\n </div>\n )}\n {customerInfo.notes && (\n <div>\n <span className=\"text-sm text-muted-foreground block\">\n {t(\"notes\", \"Notes\")}:\n </span>\n <p className=\"text-sm\">{customerInfo.notes}</p>\n </div>\n )}\n </CardContent>\n </Card>\n )}\n\n {/* Order Items */}\n <Card>\n <CardHeader>\n <CardTitle>{t(\"orderItems\", \"Order Items\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {items.map((item, index) => (\n <div key={item.id || index}>\n <div className=\"flex gap-4\">\n {item.image && (\n <div className=\"w-16 h-16 rounded-lg overflow-hidden bg-muted flex-shrink-0\">\n <img\n src={item.image}\n alt={item.name}\n className=\"w-full h-full object-cover\"\n />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium truncate\">{item.name}</h4>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"qty\", \"Qty\")}: {item.quantity}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"font-semibold\">\n {formatPrice(\n item.price * item.quantity,\n constants.site.currency\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {formatPrice(\n item.price,\n constants.site.currency\n )}{\" \"}\n {t(\"each\", \"each\")}\n </p>\n </div>\n </div>\n {index < items.length - 1 && <Separator className=\"mt-4\" />}\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Order Summary Sidebar */}\n <div className=\"space-y-6\">\n {/* Price Breakdown */}\n <Card>\n <CardHeader>\n <CardTitle>{t(\"orderSummary\", \"Order Summary\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {subtotal !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"subtotal\", \"Subtotal\")}:\n </span>\n <span className=\"font-medium\">\n {formatPrice(subtotal, constants.site.currency)}\n </span>\n </div>\n )}\n {shipping !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"shipping\", \"Shipping\")}:\n </span>\n <span className=\"font-medium\">\n {shipping === 0\n ? t(\"free\", \"Free\")\n : formatPrice(\n shipping,\n constants.site.currency\n )}\n </span>\n </div>\n )}\n {tax !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">\n {t(\"tax\", \"Tax\")}:\n </span>\n <span className=\"font-medium\">\n {formatPrice(tax, constants.site.currency)}\n </span>\n </div>\n )}\n <Separator />\n <div className=\"flex justify-between text-lg\">\n <span className=\"font-bold\">{t(\"total\", \"Total\")}:</span>\n <span className=\"font-bold\">\n {formatPrice(total, constants.site.currency)}\n </span>\n </div>\n </CardContent>\n </Card>\n\n {/* Payment Instructions for Bank Transfer */}\n {paymentMethod === \"bank_transfer\" && (\n <Card className=\"border-blue-200 bg-blue-50 dark:bg-blue-950/20\">\n <CardHeader>\n <CardTitle className=\"text-blue-900 dark:text-blue-100 flex items-center gap-2\">\n <CreditCard className=\"w-5 h-5\" />\n {t(\"paymentInstructions\", \"Payment Instructions\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-blue-800 dark:text-blue-200 space-y-2\">\n <p>\n {t(\n \"bankDetailsEmail\",\n \"Bank details will be sent via email.\"\n )}\n </p>\n <p>\n {t(\n \"transferReceiptNote\",\n \"Please send your transfer receipt via WhatsApp after completing the bank transfer.\"\n )}\n </p>\n </CardContent>\n </Card>\n )}\n\n {/* Payment Instructions for COD */}\n {paymentMethod === \"cash_on_delivery\" && (\n <Card className=\"border-amber-200 bg-amber-50 dark:bg-amber-950/20\">\n <CardHeader>\n <CardTitle className=\"text-amber-900 dark:text-amber-100 flex items-center gap-2\">\n <Truck className=\"w-5 h-5\" />\n {t(\"cashOnDeliveryInfo\", \"Cash on Delivery\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-amber-800 dark:text-amber-200\">\n <p>\n {t(\n \"codInstructions\",\n \"Pay {{amount}} when your order arrives. Our delivery team will contact you before delivery.\",\n {\n amount: formatPrice(\n total,\n constants.site.currency\n ),\n }\n )}\n </p>\n </CardContent>\n </Card>\n )}\n\n {/* Contact Information */}\n <Card>\n <CardHeader>\n <CardTitle>{t(\"customerService\", \"Customer Service\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n {constants.email && (\n <div>\n <span className=\"text-muted-foreground\">\n {t(\"email\", \"Email\")}:\n </span>\n <p className=\"font-medium\">{constants.email}</p>\n </div>\n )}\n {constants.phone && (\n <div>\n <span className=\"text-muted-foreground\">\n {t(\"phone\", \"Phone\")}:\n </span>\n <p className=\"font-medium\">{constants.phone}</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "order-detail-block/lang/en.json",
|
|
25
|
+
"type": "registry:lang",
|
|
26
|
+
"target": "$modules$/order-detail-block/lang/en.json",
|
|
27
|
+
"content": "{\n \"title\": \"Order Confirmed!\",\n \"thankYou\": \"Thank you for your order! We've received your order and will process it shortly.\",\n \"orderInformation\": \"Order Information\",\n \"orderId\": \"Order ID\",\n \"orderDate\": \"Order Date\",\n \"totalAmount\": \"Total Amount\",\n \"paymentMethod\": \"Payment Method\",\n \"orderStatus\": \"Order Status\",\n \"estimatedDelivery\": \"Estimated Delivery\",\n \"deliveryInformation\": \"Delivery Information\",\n \"customerName\": \"Name\",\n \"email\": \"Email\",\n \"phone\": \"Phone\",\n \"address\": \"Address\",\n \"notes\": \"Notes\",\n \"orderItems\": \"Order Items\",\n \"qty\": \"Qty\",\n \"each\": \"each\",\n \"orderSummary\": \"Order Summary\",\n \"subtotal\": \"Subtotal\",\n \"shipping\": \"Shipping\",\n \"tax\": \"Tax\",\n \"free\": \"Free\",\n \"total\": \"Total\",\n \"paymentInstructions\": \"Payment Instructions\",\n \"bankDetailsEmail\": \"Bank details will be sent via email.\",\n \"transferReceiptNote\": \"Please send your transfer receipt via WhatsApp after completing the bank transfer.\",\n \"cashOnDeliveryInfo\": \"Cash on Delivery\",\n \"codInstructions\": \"Pay {{amount}} when your order arrives. Our delivery team will contact you before delivery.\",\n \"customerService\": \"Customer Service\"\n}\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "order-detail-block/lang/tr.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/order-detail-block/lang/tr.json",
|
|
33
|
+
"content": "{\n \"title\": \"Sipariş Onaylandı!\",\n \"thankYou\": \"Siparişiniz için teşekkürler! Siparişinizi aldık ve kısa sürede işleme alacağız.\",\n \"orderInformation\": \"Sipariş Bilgileri\",\n \"orderId\": \"Sipariş No\",\n \"orderDate\": \"Sipariş Tarihi\",\n \"totalAmount\": \"Toplam Tutar\",\n \"paymentMethod\": \"Ödeme Yöntemi\",\n \"orderStatus\": \"Sipariş Durumu\",\n \"estimatedDelivery\": \"Tahmini Teslimat\",\n \"deliveryInformation\": \"Teslimat Bilgileri\",\n \"customerName\": \"İsim\",\n \"email\": \"E-posta\",\n \"phone\": \"Telefon\",\n \"address\": \"Adres\",\n \"notes\": \"Notlar\",\n \"orderItems\": \"Sipariş Ürünleri\",\n \"qty\": \"Adet\",\n \"each\": \"adet\",\n \"orderSummary\": \"Sipariş Özeti\",\n \"subtotal\": \"Ara Toplam\",\n \"shipping\": \"Kargo\",\n \"tax\": \"Vergi\",\n \"free\": \"Ücretsiz\",\n \"total\": \"Toplam\",\n \"paymentInstructions\": \"Ödeme Talimatları\",\n \"bankDetailsEmail\": \"Banka bilgileri e-posta ile gönderilecektir.\",\n \"transferReceiptNote\": \"Banka havalesi yaptıktan sonra lütfen dekontu WhatsApp ile gönderin.\",\n \"cashOnDeliveryInfo\": \"Kapıda Ödeme\",\n \"codInstructions\": \"Siparişiniz geldiğinde {{amount}} ödeyeceksiniz. Teslimat ekibimiz teslimat öncesi sizinle iletişime geçecektir.\",\n \"customerService\": \"Müşteri Hizmetleri\"\n}\n"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"types": [],
|
|
38
|
+
"variables": [
|
|
39
|
+
"OrderDetailBlock"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "orders-list-block",
|
|
3
|
+
"type": "registry:block",
|
|
4
|
+
"title": "Orders List Block",
|
|
5
|
+
"description": "User's order history list with loading skeleton, error state, and empty state. Shows order cards with status badge, date, total, items preview. Each card links to order detail.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"ecommerce-core",
|
|
8
|
+
"order-card-compact"
|
|
9
|
+
],
|
|
10
|
+
"usage": "import { OrdersListBlock } from '@/modules/orders-list-block';\n\n<OrdersListBlock orders={orders} loading={loading} />\n\n• Props: orders (Order[]), loading (boolean)\n• Uses OrderCardCompact for each order\n• States: loading, empty, error, list",
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "orders-list-block/index.ts",
|
|
14
|
+
"type": "registry:index",
|
|
15
|
+
"target": "$modules$/orders-list-block/index.ts",
|
|
16
|
+
"content": "export * from './orders-list-block';\r\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "orders-list-block/orders-list-block.tsx",
|
|
20
|
+
"type": "registry:block",
|
|
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"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "orders-list-block/lang/en.json",
|
|
26
|
+
"type": "registry:lang",
|
|
27
|
+
"target": "$modules$/orders-list-block/lang/en.json",
|
|
28
|
+
"content": "{\n \"title\": \"My Orders\",\n \"subtitle\": \"View and track your orders\",\n \"loadingOrders\": \"Loading Orders\",\n \"pleaseWait\": \"Please wait...\",\n \"errorTitle\": \"Something went wrong\",\n \"errorDescription\": \"We couldn't load your orders. Please try again.\",\n \"tryAgain\": \"Try Again\",\n \"noOrders\": \"No Orders Yet\",\n \"noOrdersDescription\": \"You haven't placed any orders yet. Start shopping to see your orders here.\",\n \"startShopping\": \"Start Shopping\",\n \"continueShopping\": \"Continue Shopping\"\n}\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "orders-list-block/lang/tr.json",
|
|
32
|
+
"type": "registry:lang",
|
|
33
|
+
"target": "$modules$/orders-list-block/lang/tr.json",
|
|
34
|
+
"content": "{\n \"title\": \"Siparişlerim\",\n \"subtitle\": \"Siparişlerinizi görüntüleyin ve takip edin\",\n \"loadingOrders\": \"Siparişler Yükleniyor\",\n \"pleaseWait\": \"Lütfen bekleyin...\",\n \"errorTitle\": \"Bir şeyler yanlış gitti\",\n \"errorDescription\": \"Siparişleriniz yüklenemedi. Lütfen tekrar deneyin.\",\n \"tryAgain\": \"Tekrar Dene\",\n \"noOrders\": \"Henüz Sipariş Yok\",\n \"noOrdersDescription\": \"Henüz sipariş vermediniz. Siparişlerinizi burada görmek için alışverişe başlayın.\",\n \"startShopping\": \"Alışverişe Başla\",\n \"continueShopping\": \"Alışverişe Devam Et\"\n}\n"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"exports": {
|
|
38
|
+
"types": [
|
|
39
|
+
"OrdersListBlockProps"
|
|
40
|
+
],
|
|
41
|
+
"variables": [
|
|
42
|
+
"OrdersListBlock"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "payment-success-block",
|
|
3
|
+
"type": "registry:block",
|
|
4
|
+
"title": "Payment Success Block",
|
|
5
|
+
"description": "Payment verification UI with loading, success, and failed states. Displays order details and navigation options",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { PaymentSuccessBlock } from '@/modules/payment-success-block';\n\n<PaymentSuccessBlock status=\"success\" orderId={orderId} />\n\n• Installed at: src/modules/payment-success-block/\n• Handles loading, success, and failed states\n• Use in payment callback/confirmation pages",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "payment-success-block/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/payment-success-block/index.ts",
|
|
13
|
+
"content": "export * from './payment-success-block';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "payment-success-block/payment-success-block.tsx",
|
|
17
|
+
"type": "registry:block",
|
|
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"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "payment-success-block/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/payment-success-block/lang/en.json",
|
|
25
|
+
"content": "{\n \"verifyingPayment\": \"Verifying Payment\",\n \"pleaseWait\": \"Please wait while we verify your payment...\",\n \"paymentFailed\": \"Payment Failed\",\n \"paymentFailedDescription\": \"We couldn't verify your payment. Please try again or contact support.\",\n \"tryAgain\": \"Try Again\",\n \"contactSupport\": \"Contact Support\",\n \"thankYou\": \"Thank You!\",\n \"orderConfirmed\": \"Your order has been confirmed.\",\n \"orderDetails\": \"Order Details\",\n \"orderId\": \"Order ID\",\n \"total\": \"Total\",\n \"paymentMethod\": \"Payment Method\",\n \"paymentStatus\": \"Payment Status\",\n \"orderStatus\": \"Order Status\",\n \"confirmationEmailSent\": \"A confirmation email has been sent to your email address.\",\n \"viewMyOrders\": \"View My Orders\",\n \"continueShopping\": \"Continue Shopping\"\n}\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "payment-success-block/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/payment-success-block/lang/tr.json",
|
|
31
|
+
"content": "{\n \"verifyingPayment\": \"Ödeme Doğrulanıyor\",\n \"pleaseWait\": \"Lütfen ödemenizi doğrularken bekleyin...\",\n \"paymentFailed\": \"Ödeme Başarısız\",\n \"paymentFailedDescription\": \"Ödemenizi doğrulayamadık. Lütfen tekrar deneyin veya destek ile iletişime geçin.\",\n \"tryAgain\": \"Tekrar Dene\",\n \"contactSupport\": \"Destek ile İletişime Geç\",\n \"thankYou\": \"Teşekkürler!\",\n \"orderConfirmed\": \"Siparişiniz onaylandı.\",\n \"orderDetails\": \"Sipariş Detayları\",\n \"orderId\": \"Sipariş No\",\n \"total\": \"Toplam\",\n \"paymentMethod\": \"Ödeme Yöntemi\",\n \"paymentStatus\": \"Ödeme Durumu\",\n \"orderStatus\": \"Sipariş Durumu\",\n \"confirmationEmailSent\": \"Onay e-postası e-posta adresinize gönderildi.\",\n \"viewMyOrders\": \"Siparişlerimi Gör\",\n \"continueShopping\": \"Alışverişe Devam Et\"\n}\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"PaymentSuccessBlock"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "post-card",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Post Card",
|
|
5
|
+
"description": "Blog post card with grid and list layouts. Displays featured image, title, excerpt, categories, author, date, read time, and view count. Includes favorite/bookmark toggle button.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"blog-core"
|
|
8
|
+
],
|
|
9
|
+
"usage": "import { PostCard } from '@/modules/post-card';\n\n<PostCard post={post} layout=\"grid\" />\n\n• Props: post (Post from blog-core), layout (grid|list)\n• Uses useBlog() from blog-core (Zustand) for favorites\n• Features: bookmark toggle, category badges, read time",
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "post-card/index.ts",
|
|
13
|
+
"type": "registry:index",
|
|
14
|
+
"target": "$modules$/post-card/index.ts",
|
|
15
|
+
"content": "export * from './post-card';\r\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "post-card/post-card.tsx",
|
|
19
|
+
"type": "registry:component",
|
|
20
|
+
"target": "$modules$/post-card/post-card.tsx",
|
|
21
|
+
"content": "import { Link } from \"react-router\";\nimport { Calendar, Eye, Clock, ArrowRight } from \"lucide-react\";\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n} from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { useTranslation } from \"react-i18next\";\nimport type { Post } from \"@/modules/blog-core/types\";\n\ninterface PostCardProps {\n post: Post;\n layout?: \"grid\" | \"list\";\n showExcerpt?: boolean;\n className?: string;\n}\n\nexport function PostCard({\n post,\n layout = \"grid\",\n showExcerpt = true,\n className = \"\",\n}: PostCardProps) {\n const { t } = useTranslation(\"post-card\");\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n };\n\n const CardWrapper = ({ children }: { children: React.ReactNode }) => (\n <Card\n className={`group overflow-hidden border-0 p-0 shadow-sm hover:shadow-lg transition-all duration-300 h-full ${className}`}\n >\n {children}\n </Card>\n );\n\n if (layout === \"list\") {\n return (\n <CardWrapper>\n <div className=\"flex flex-col md:flex-row\">\n {post.featured_image && (\n <div className=\"md:w-1/3 flex-shrink-0\">\n <Link to={`/blog/${post.slug}`}>\n <img\n src={post.featured_image}\n alt={post.title}\n className=\"w-full h-48 md:h-full object-cover\"\n onError={(e) => {\n e.currentTarget.src = \"/images/placeholder.png\";\n }}\n />\n </Link>\n </div>\n )}\n\n <div className=\"flex-1 flex flex-col\">\n <CardHeader className=\"pt-6 pb-3\">\n {post.categories && post.categories.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {post.categories.slice(0, 2).map((category) => (\n <Badge\n key={category.slug}\n variant=\"secondary\"\n className=\"text-xs\"\n >\n <Link to={`/blog?categories=${category.slug}`}>\n {category.name}\n </Link>\n </Badge>\n ))}\n </div>\n )}\n\n <h3 className=\"text-xl font-semibold line-clamp-2 group-hover:text-primary transition-colors\">\n <Link to={`/blog/${post.slug}`}>{post.title}</Link>\n </h3>\n </CardHeader>\n\n <CardContent className=\"flex-1 pb-3\">\n {showExcerpt && post.excerpt && (\n <p className=\"text-muted-foreground text-sm line-clamp-3 mb-4\">\n {post.excerpt}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-muted-foreground\">\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n <span>{formatDate(post.published_at)}</span>\n </div>\n\n {post.read_time > 0 && (\n <div className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n <span>\n {post.read_time} {t(\"minRead\", \"min\")}\n </span>\n </div>\n )}\n\n {post.view_count > 0 && (\n <div className=\"flex items-center gap-1\">\n <Eye className=\"h-3 w-3\" />\n <span>{post.view_count.toLocaleString()}</span>\n </div>\n )}\n </div>\n </CardContent>\n\n <CardFooter className=\"pt-0 pb-6\">\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <Link to={`/blog/${post.slug}`}>\n {t(\"readMore\", \"Read More\")}\n <ArrowRight className=\"h-3 w-3 ml-1\" />\n </Link>\n </Button>\n </CardFooter>\n </div>\n </div>\n </CardWrapper>\n );\n }\n\n return (\n <CardWrapper>\n <div className=\"flex flex-col h-full\">\n {post.featured_image && (\n <div className=\"aspect-video overflow-hidden\">\n <Link to={`/blog/${post.slug}`}>\n <img\n src={post.featured_image}\n alt={post.title}\n className=\"w-full h-full object-cover group-hover:scale-105 transition-transform duration-300\"\n onError={(e) => {\n e.currentTarget.src = \"/images/placeholder.png\";\n }}\n />\n </Link>\n </div>\n )}\n\n <div className=\"flex flex-col flex-1\">\n <CardHeader className=\"pt-6 pb-3\">\n {post.categories && post.categories.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {post.categories.slice(0, 2).map((category) => (\n <Badge\n key={category.slug}\n variant=\"secondary\"\n className=\"text-xs\"\n >\n <Link to={`/blog?categories=${category.slug}`}>\n {category.name}\n </Link>\n </Badge>\n ))}\n </div>\n )}\n\n <h3 className=\"text-lg font-semibold line-clamp-2 group-hover:text-primary transition-colors\">\n <Link to={`/blog/${post.slug}`}>{post.title}</Link>\n </h3>\n </CardHeader>\n\n <CardContent className=\"flex-1 pb-3\">\n {showExcerpt && post.excerpt && (\n <p className=\"text-muted-foreground text-sm line-clamp-3 mb-4\">\n {post.excerpt}\n </p>\n )}\n\n <div className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n <span>{formatDate(post.published_at)}</span>\n </div>\n\n {post.read_time > 0 && (\n <div className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n <span>\n {post.read_time} {t(\"minRead\", \"min\")}\n </span>\n </div>\n )}\n </div>\n </CardContent>\n\n <CardFooter className=\"pt-0 pb-6 mt-auto\">\n <div className=\"flex items-center justify-between w-full\">\n <Button variant=\"ghost\" size=\"sm\" asChild>\n <Link to={`/blog/${post.slug}`}>\n {t(\"readMore\", \"Read More\")}\n <ArrowRight className=\"h-3 w-3 ml-1\" />\n </Link>\n </Button>\n\n {post.view_count > 0 && (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Eye className=\"h-3 w-3\" />\n <span>{post.view_count.toLocaleString()}</span>\n </div>\n )}\n </div>\n </CardFooter>\n </div>\n </div>\n </CardWrapper>\n );\n}\n"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "post-card/lang/en.json",
|
|
25
|
+
"type": "registry:lang",
|
|
26
|
+
"target": "$modules$/post-card/lang/en.json",
|
|
27
|
+
"content": "{\r\n \"readMore\": \"Read More\",\r\n \"minRead\": \"min read\",\r\n \"views\": \"views\",\r\n \"published\": \"Published\",\r\n \"category\": \"Category\",\r\n \"tags\": \"Tags\",\r\n \"featured\": \"Featured\"\r\n}\r\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "post-card/lang/tr.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/post-card/lang/tr.json",
|
|
33
|
+
"content": "{\r\n \"readMore\": \"Devamını Oku\",\r\n \"minRead\": \"dk okuma\",\r\n \"views\": \"görüntüleme\",\r\n \"published\": \"Yayınlandı\",\r\n \"category\": \"Kategori\",\r\n \"tags\": \"Etiketler\",\r\n \"featured\": \"Öne Çıkan\"\r\n}\r\n"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"types": [],
|
|
38
|
+
"variables": [
|
|
39
|
+
"PostCard"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "post-detail-block",
|
|
3
|
+
"type": "registry:block",
|
|
4
|
+
"title": "Post Detail Block",
|
|
5
|
+
"description": "Blog post detail view with featured image header, meta info (date, read time, views), rich content area, tags, author bio card, and share/favorite buttons. Responsive typography and reading-optimized layout.",
|
|
6
|
+
"registryDependencies": [
|
|
7
|
+
"blog-core"
|
|
8
|
+
],
|
|
9
|
+
"usage": "import { PostDetailBlock } from '@/modules/post-detail-block';\n\n<PostDetailBlock post={post} />\n\n• Uses useBlog() from blog-core (Zustand)\n• Sections: header, content, tags, author, share\n• Props: post (Post type from blog-core)",
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "post-detail-block/index.ts",
|
|
13
|
+
"type": "registry:index",
|
|
14
|
+
"target": "$modules$/post-detail-block/index.ts",
|
|
15
|
+
"content": "export * from './post-detail-block';\r\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "post-detail-block/post-detail-block.tsx",
|
|
19
|
+
"type": "registry:block",
|
|
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"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "post-detail-block/lang/en.json",
|
|
25
|
+
"type": "registry:lang",
|
|
26
|
+
"target": "$modules$/post-detail-block/lang/en.json",
|
|
27
|
+
"content": "{\n \"backToBlog\": \"Back to Blog\",\n \"minRead\": \"min read\",\n \"views\": \"views\",\n \"share\": \"Share\",\n \"addToFavorites\": \"Add to Favorites\",\n \"removeFromFavorites\": \"Remove from Favorites\",\n \"tags\": \"Tags\"\n}\n"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "post-detail-block/lang/tr.json",
|
|
31
|
+
"type": "registry:lang",
|
|
32
|
+
"target": "$modules$/post-detail-block/lang/tr.json",
|
|
33
|
+
"content": "{\n \"backToBlog\": \"Blog'a Dön\",\n \"minRead\": \"dk okuma\",\n \"views\": \"görüntülenme\",\n \"share\": \"Paylaş\",\n \"addToFavorites\": \"Favorilere Ekle\",\n \"removeFromFavorites\": \"Favorilerden Çıkar\",\n \"tags\": \"Etiketler\"\n}\n"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"exports": {
|
|
37
|
+
"types": [],
|
|
38
|
+
"variables": [
|
|
39
|
+
"PostDetailBlock"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pricing-card",
|
|
3
|
+
"type": "registry:component",
|
|
4
|
+
"title": "Pricing Card",
|
|
5
|
+
"description": "SaaS-style pricing plan card with plan name, price (monthly/yearly toggle support), feature list with check/x icons, highlighted 'Popular' badge option, and CTA button. Features hover scale effect, border highlight for recommended plan, and responsive sizing.",
|
|
6
|
+
"registryDependencies": [],
|
|
7
|
+
"usage": "import { PricingCard } from '@/modules/pricing-card';\n\n<PricingCard\n name=\"Pro\"\n price={29}\n period=\"month\"\n features={['Feature 1', 'Feature 2']}\n popular={true}\n/>\n\n• Installed at: src/modules/pricing-card/\n• Use in grid: grid grid-cols-1 md:grid-cols-3 gap-8",
|
|
8
|
+
"files": [
|
|
9
|
+
{
|
|
10
|
+
"path": "pricing-card/index.ts",
|
|
11
|
+
"type": "registry:index",
|
|
12
|
+
"target": "$modules$/pricing-card/index.ts",
|
|
13
|
+
"content": "export * from './pricing-card';\r\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "pricing-card/pricing-card.tsx",
|
|
17
|
+
"type": "registry:component",
|
|
18
|
+
"target": "$modules$/pricing-card/pricing-card.tsx",
|
|
19
|
+
"content": "import { Card } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Check } from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\n\ninterface PricingPlan {\n id: string | number;\n name: string;\n description: string;\n price: number;\n currency: string;\n period: string;\n features: string[];\n popular?: boolean;\n}\n\ninterface PricingCardProps {\n plan: PricingPlan;\n className?: string;\n onSelect?: (plan: PricingPlan) => void;\n}\n\nexport function PricingCard({ plan, className = '', onSelect }: PricingCardProps) {\n const { t } = useTranslation(\"pricing-card\");\n\n return (\n <Card className={`relative overflow-hidden border-0 shadow-md transition-all duration-300 bg-background/80 backdrop-blur-sm hover:shadow-lg ${plan.popular ? 'ring-2 ring-primary' : ''} ${className}`}>\n {plan.popular && (\n <Badge className=\"absolute top-4 right-4 bg-primary text-primary-foreground\">\n {t('popular', 'Most Popular')}\n </Badge>\n )}\n\n <div className=\"p-8\">\n <h3 className=\"text-xl font-bold text-foreground mb-2\">\n {plan.name}\n </h3>\n\n <p className=\"text-sm text-muted-foreground mb-6\">\n {plan.description}\n </p>\n\n <div className=\"mb-6\">\n <span className=\"text-4xl font-bold text-primary\">\n {plan.currency}{plan.price}\n </span>\n <span className=\"text-foreground ml-1\">\n {plan.period}\n </span>\n </div>\n\n <ul className=\"space-y-3 mb-8\">\n {plan.features.map((feature, index) => (\n <li key={index} className=\"flex items-center text-sm text-muted-foreground\">\n <Check className=\"w-4 h-4 text-primary mr-3 flex-shrink-0\" />\n {feature}\n </li>\n ))}\n </ul>\n\n {onSelect && (\n <Button\n className=\"w-full\"\n variant={plan.popular ? 'default' : 'outline'}\n onClick={() => onSelect(plan)}\n >\n {t('getStarted', 'Get Started')}\n </Button>\n )}\n </div>\n </Card>\n );\n}\n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "pricing-card/lang/en.json",
|
|
23
|
+
"type": "registry:lang",
|
|
24
|
+
"target": "$modules$/pricing-card/lang/en.json",
|
|
25
|
+
"content": "{\r\n \"getStarted\": \"Get Started\",\r\n \"popular\": \"Most Popular\"\r\n}\r\n"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "pricing-card/lang/tr.json",
|
|
29
|
+
"type": "registry:lang",
|
|
30
|
+
"target": "$modules$/pricing-card/lang/tr.json",
|
|
31
|
+
"content": "{\r\n \"getStarted\": \"Başla\",\r\n \"popular\": \"En Popüler\"\r\n}\r\n"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
"types": [],
|
|
36
|
+
"variables": [
|
|
37
|
+
"PricingCard"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|