@promakeai/cli 0.3.2 → 0.3.4
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 +415 -209
- package/dist/registry/about-page.json +3 -3
- package/dist/registry/about-section.json +4 -4
- package/dist/registry/animations.json +2 -2
- package/dist/registry/announcement-bar.json +4 -4
- package/dist/registry/api.json +1 -1
- package/dist/registry/auth-core.json +3 -3
- package/dist/registry/auth.json +70 -0
- package/dist/registry/bento-grid-section.json +4 -4
- package/dist/registry/blog-core.json +5 -5
- package/dist/registry/blog-list-page.json +3 -3
- package/dist/registry/blog-section.json +4 -4
- package/dist/registry/cards-carousel-section.json +4 -4
- package/dist/registry/cart-drawer.json +3 -3
- package/dist/registry/cart-page.json +3 -3
- package/dist/registry/case-study-page.json +3 -3
- package/dist/registry/category-section.json +3 -3
- package/dist/registry/checkout-page.json +3 -3
- package/dist/registry/coming-soon-page-minimal.json +4 -4
- package/dist/registry/coming-soon-page.json +4 -4
- package/dist/registry/contact-info-grid.json +4 -4
- package/dist/registry/contact-page-centered.json +4 -4
- package/dist/registry/contact-page-split.json +4 -4
- package/dist/registry/contact-page.json +3 -3
- package/dist/registry/content-section.json +4 -4
- package/dist/registry/cookie-consent.json +4 -4
- package/dist/registry/cookies-page.json +3 -3
- package/dist/registry/cta-section.json +3 -3
- package/dist/registry/docs/reset-password-page.md +36 -0
- package/dist/registry/ecommerce-core.json +10 -10
- package/dist/registry/empty-page.json +3 -3
- package/dist/registry/faq-categorized.json +4 -4
- package/dist/registry/faq-simple.json +4 -4
- package/dist/registry/favorites-blog-block.json +1 -1
- package/dist/registry/favorites-blog-page.json +4 -4
- package/dist/registry/favorites-ecommerce-block.json +1 -1
- package/dist/registry/favorites-ecommerce-page.json +4 -4
- package/dist/registry/feature-section.json +3 -3
- package/dist/registry/featured-products.json +3 -3
- package/dist/registry/footer-detailed.json +4 -4
- package/dist/registry/footer-minimal.json +3 -3
- package/dist/registry/footer.json +3 -3
- package/dist/registry/forgot-password-page-split.json +4 -4
- package/dist/registry/forgot-password-page.json +4 -4
- package/dist/registry/google-adsense.json +4 -4
- package/dist/registry/google-map.json +2 -2
- package/dist/registry/header-centered-pill.json +4 -4
- package/dist/registry/header-ecommerce.json +3 -3
- package/dist/registry/header-mega.json +4 -4
- package/dist/registry/header-minimal.json +4 -4
- package/dist/registry/header-simple.json +3 -3
- package/dist/registry/hero-carousel.json +3 -3
- package/dist/registry/hero-cta.json +4 -4
- package/dist/registry/hero-gradient.json +4 -4
- package/dist/registry/hero-grid.json +4 -4
- package/dist/registry/hero-profile.json +3 -3
- package/dist/registry/hero.json +3 -3
- package/dist/registry/landing-page-app.json +3 -3
- package/dist/registry/landing-page-saas.json +3 -3
- package/dist/registry/login-page-split.json +4 -4
- package/dist/registry/login-page.json +4 -4
- package/dist/registry/logo-cloud.json +4 -4
- package/dist/registry/masonry-grid.json +3 -3
- package/dist/registry/my-orders-page.json +4 -4
- package/dist/registry/newsletter-section.json +4 -4
- package/dist/registry/order-card-compact.json +1 -1
- package/dist/registry/order-confirmation-page.json +4 -4
- package/dist/registry/order-detail-block.json +1 -1
- package/dist/registry/orders-list-block.json +1 -1
- package/dist/registry/payment-success-block.json +1 -1
- package/dist/registry/portfolio-page.json +4 -4
- package/dist/registry/post-card.json +3 -3
- package/dist/registry/post-detail-block.json +1 -1
- package/dist/registry/post-detail-page.json +4 -4
- package/dist/registry/pricing-card.json +3 -3
- package/dist/registry/pricing-page.json +4 -4
- package/dist/registry/pricing-section.json +4 -4
- package/dist/registry/privacy-page.json +3 -3
- package/dist/registry/product-card-detailed.json +4 -4
- package/dist/registry/product-card-hover.json +4 -4
- package/dist/registry/product-card.json +3 -3
- package/dist/registry/product-detail-block.json +1 -1
- package/dist/registry/product-detail-page.json +4 -4
- package/dist/registry/product-detail-section.json +4 -4
- package/dist/registry/product-quick-view.json +4 -4
- package/dist/registry/products-page.json +3 -3
- package/dist/registry/reading-progress.json +4 -4
- package/dist/registry/register-page-split.json +4 -4
- package/dist/registry/register-page.json +4 -4
- package/dist/registry/related-posts-block.json +1 -1
- package/dist/registry/related-products-block.json +1 -1
- package/dist/registry/reset-password-page-split.json +4 -4
- package/dist/registry/reset-password-page.json +39 -0
- package/dist/registry/service-card.json +1 -1
- package/dist/registry/share-buttons.json +4 -4
- package/dist/registry/skill-card.json +1 -1
- package/dist/registry/team-page.json +4 -4
- package/dist/registry/terms-page.json +3 -3
- package/dist/registry/testimonials-carousel.json +4 -4
- package/dist/registry/testimonials-grid.json +4 -4
- package/dist/registry/timeline-section.json +4 -4
- package/dist/registry/video-hero.json +4 -4
- package/dist/registry/youtube-embed.json +4 -4
- package/package.json +2 -2
- package/template/.env +6 -6
- package/template/public/_redirects +1 -1
- package/template/public/robots.txt +14 -14
- package/template/src/components/GoogleAnalytics.tsx +34 -34
- package/template/src/components/LanguageSwitcher.tsx +53 -53
- package/template/src/components/ScriptInjector.tsx +62 -62
- package/template/src/lib/env.ts +19 -19
- package/template/src/router.tsx +14 -14
- package/template/src/vite-env.d.ts +1 -1
|
@@ -13,25 +13,25 @@
|
|
|
13
13
|
"path": "footer-detailed/index.ts",
|
|
14
14
|
"type": "registry:index",
|
|
15
15
|
"target": "$modules$/footer-detailed/index.ts",
|
|
16
|
-
"content": "export * from './footer-detailed';\n"
|
|
16
|
+
"content": "export * from './footer-detailed';\r\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "footer-detailed/footer-detailed.tsx",
|
|
20
20
|
"type": "registry:component",
|
|
21
21
|
"target": "$modules$/footer-detailed/footer-detailed.tsx",
|
|
22
|
-
"content": "import { useState, useMemo } from \"react\";\nimport { Link } from \"react-router\";\nimport {\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n Youtube,\n Mail,\n Phone,\n ArrowUp,\n Send,\n CreditCard,\n} from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Logo } from \"@/components/Logo\";\nimport constants from \"@/constants/constants.json\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n youtube: Youtube,\n};\n\ninterface FooterDetailedProps {\n className?: string;\n}\n\nexport function FooterDetailed({ className }: FooterDetailedProps) {\n const { t } = useTranslation(\"footer-detailed\");\n const [email, setEmail] = useState(\"\");\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as Record<string, string> | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({ platform, url, Icon: socialIcons[platform] }));\n }, []);\n\n const handleNewsletterSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n // Newsletter subscription logic\n console.log(\"Newsletter subscription:\", email);\n setEmail(\"\");\n };\n\n const scrollToTop = () => {\n window.scrollTo({ top: 0, behavior: \"smooth\" });\n };\n\n const currentYear = new Date().getFullYear();\n\n const linkSections = [\n {\n title: t(\"shop\", \"Shop\"),\n links: [\n { text: t(\"allProducts\", \"All Products\"), url: \"/products\" },\n { text: t(\"featured\", \"Featured\"), url: \"/products?featured=true\" },\n { text: t(\"newArrivals\", \"New Arrivals\"), url: \"/products?new=true\" },\n { text: t(\"onSale\", \"On Sale\"), url: \"/products?sale=true\" },\n ],\n },\n {\n title: t(\"company\", \"Company\"),\n links: [\n { text: t(\"about\", \"About Us\"), url: \"/about\" },\n { text: t(\"careers\", \"Careers\"), url: \"/careers\" },\n { text: t(\"blog\", \"Blog\"), url: \"/blog\" },\n { text: t(\"press\", \"Press\"), url: \"/press\" },\n ],\n },\n {\n title: t(\"support\", \"Support\"),\n links: [\n { text: t(\"helpCenter\", \"Help Center\"), url: \"/help\" },\n { text: t(\"faq\", \"FAQ\"), url: \"/faq\" },\n { text: t(\"contact\", \"Contact Us\"), url: \"/contact\" },\n { text: t(\"returns\", \"Returns\"), url: \"/returns\" },\n ],\n },\n {\n title: t(\"legal\", \"Legal\"),\n links: [\n { text: t(\"privacy\", \"Privacy Policy\"), url: \"/privacy\" },\n { text: t(\"terms\", \"Terms of Service\"), url: \"/terms\" },\n { text: t(\"cookies\", \"Cookie Policy\"), url: \"/cookies\" },\n { text: t(\"refund\", \"Refund Policy\"), url: \"/refund\" },\n ],\n },\n ];\n\n return (\n <footer className={cn(\"border-t bg-muted/30\", className)}>\n {/* Main Footer */}\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-12 lg:py-16\">\n <div className=\"grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-6\">\n {/* Brand & Newsletter - spans 2 columns */}\n <div className=\"sm:col-span-2 space-y-6\">\n <Logo size=\"lg\" />\n <p className=\"text-sm text-muted-foreground leading-relaxed max-w-sm\">\n {t(\"description\", \"Your trusted destination for quality products. We deliver excellence with every order.\")}\n </p>\n\n {/* Newsletter */}\n <div className=\"space-y-3\">\n <h4 className=\"font-semibold text-sm\">\n {t(\"newsletter\", \"Subscribe to our newsletter\")}\n </h4>\n <form onSubmit={handleNewsletterSubmit} className=\"flex gap-2\">\n <Input\n type=\"email\"\n placeholder={t(\"emailPlaceholder\", \"Enter your email\")}\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n className=\"flex-1 h-10\"\n required\n />\n <Button type=\"submit\" size=\"sm\" className=\"h-10 px-4\">\n <Send className=\"h-4 w-4\" />\n </Button>\n </form>\n <p className=\"text-xs text-muted-foreground\">\n {t(\"newsletterNote\", \"Get updates on new products and exclusive offers.\")}\n </p>\n </div>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"flex gap-1 pt-2\">\n {socialLinks.map(({ platform, url, Icon }) => (\n <a\n key={platform}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"h-10 w-10 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\"\n >\n <Icon className=\"h-5 w-5\" />\n </a>\n ))}\n </div>\n )}\n </div>\n\n {/* Link Sections */}\n {linkSections.map((section, idx) => (\n <div key={idx} className=\"space-y-4\">\n <h4 className=\"font-semibold\">{section.title}</h4>\n <ul className=\"space-y-2.5\">\n {section.links.map((link, linkIdx) => (\n <li key={linkIdx}>\n <Link\n to={link.url}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n {link.text}\n </Link>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n </div>\n\n {/* Bottom Bar */}\n <div className=\"border-t bg-muted/50\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-6\">\n <div className=\"flex flex-col lg:flex-row justify-between items-center gap-6\">\n {/* Copyright & Contact */}\n <div className=\"flex flex-col sm:flex-row items-center gap-4 sm:gap-6 text-sm text-muted-foreground\">\n <span>© {currentYear} {constants.site.name}. {t(\"allRightsReserved\", \"All rights reserved.\")}</span>\n <div className=\"hidden sm:block w-px h-4 bg-border\" />\n <div className=\"flex items-center gap-4\">\n <a href={`tel:${constants.phone}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\n <Phone className=\"h-3.5 w-3.5\" />\n <span>{constants.phone}</span>\n </a>\n <a href={`mailto:${constants.email}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\n <Mail className=\"h-3.5 w-3.5\" />\n <span>{constants.email}</span>\n </a>\n </div>\n </div>\n\n {/* Payment & Back to Top */}\n <div className=\"flex items-center gap-6\">\n {/* Payment Methods */}\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <CreditCard className=\"h-5 w-5\" />\n <span className=\"text-xs\">{t(\"securePayment\", \"Secure Payment\")}</span>\n </div>\n\n {/* Back to Top */}\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={scrollToTop}\n className=\"gap-2\"\n >\n <ArrowUp className=\"h-4 w-4\" />\n {t(\"backToTop\", \"Top\")}\n </Button>\n </div>\n </div>\n </div>\n </div>\n </footer>\n );\n}\n"
|
|
22
|
+
"content": "import { useState, useMemo } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport {\r\n Facebook,\r\n Twitter,\r\n Instagram,\r\n Linkedin,\r\n Youtube,\r\n Mail,\r\n Phone,\r\n ArrowUp,\r\n Send,\r\n CreditCard,\r\n} from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\nconst socialIcons: Record<string, React.ElementType> = {\r\n facebook: Facebook,\r\n twitter: Twitter,\r\n instagram: Instagram,\r\n linkedin: Linkedin,\r\n youtube: Youtube,\r\n};\r\n\r\ninterface FooterDetailedProps {\r\n className?: string;\r\n}\r\n\r\nexport function FooterDetailed({ className }: FooterDetailedProps) {\r\n const { t } = useTranslation(\"footer-detailed\");\r\n const [email, setEmail] = useState(\"\");\r\n\r\n const socialLinks = useMemo(() => {\r\n const socialMedia = constants.socialMedia as Record<string, string> | undefined;\r\n if (!socialMedia) return [];\r\n return Object.entries(socialMedia)\r\n .filter(([platform, url]) => url && socialIcons[platform])\r\n .map(([platform, url]) => ({ platform, url, Icon: socialIcons[platform] }));\r\n }, []);\r\n\r\n const handleNewsletterSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n // Newsletter subscription logic\r\n console.log(\"Newsletter subscription:\", email);\r\n setEmail(\"\");\r\n };\r\n\r\n const scrollToTop = () => {\r\n window.scrollTo({ top: 0, behavior: \"smooth\" });\r\n };\r\n\r\n const currentYear = new Date().getFullYear();\r\n\r\n const linkSections = [\r\n {\r\n title: t(\"shop\", \"Shop\"),\r\n links: [\r\n { text: t(\"allProducts\", \"All Products\"), url: \"/products\" },\r\n { text: t(\"featured\", \"Featured\"), url: \"/products?featured=true\" },\r\n { text: t(\"newArrivals\", \"New Arrivals\"), url: \"/products?new=true\" },\r\n { text: t(\"onSale\", \"On Sale\"), url: \"/products?sale=true\" },\r\n ],\r\n },\r\n {\r\n title: t(\"company\", \"Company\"),\r\n links: [\r\n { text: t(\"about\", \"About Us\"), url: \"/about\" },\r\n { text: t(\"careers\", \"Careers\"), url: \"/careers\" },\r\n { text: t(\"blog\", \"Blog\"), url: \"/blog\" },\r\n { text: t(\"press\", \"Press\"), url: \"/press\" },\r\n ],\r\n },\r\n {\r\n title: t(\"support\", \"Support\"),\r\n links: [\r\n { text: t(\"helpCenter\", \"Help Center\"), url: \"/help\" },\r\n { text: t(\"faq\", \"FAQ\"), url: \"/faq\" },\r\n { text: t(\"contact\", \"Contact Us\"), url: \"/contact\" },\r\n { text: t(\"returns\", \"Returns\"), url: \"/returns\" },\r\n ],\r\n },\r\n {\r\n title: t(\"legal\", \"Legal\"),\r\n links: [\r\n { text: t(\"privacy\", \"Privacy Policy\"), url: \"/privacy\" },\r\n { text: t(\"terms\", \"Terms of Service\"), url: \"/terms\" },\r\n { text: t(\"cookies\", \"Cookie Policy\"), url: \"/cookies\" },\r\n { text: t(\"refund\", \"Refund Policy\"), url: \"/refund\" },\r\n ],\r\n },\r\n ];\r\n\r\n return (\r\n <footer className={cn(\"border-t bg-muted/30\", className)}>\r\n {/* Main Footer */}\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-12 lg:py-16\">\r\n <div className=\"grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-6\">\r\n {/* Brand & Newsletter - spans 2 columns */}\r\n <div className=\"sm:col-span-2 space-y-6\">\r\n <Logo size=\"lg\" />\r\n <p className=\"text-sm text-muted-foreground leading-relaxed max-w-sm\">\r\n {t(\"description\", \"Your trusted destination for quality products. We deliver excellence with every order.\")}\r\n </p>\r\n\r\n {/* Newsletter */}\r\n <div className=\"space-y-3\">\r\n <h4 className=\"font-semibold text-sm\">\r\n {t(\"newsletter\", \"Subscribe to our newsletter\")}\r\n </h4>\r\n <form onSubmit={handleNewsletterSubmit} className=\"flex gap-2\">\r\n <Input\r\n type=\"email\"\r\n placeholder={t(\"emailPlaceholder\", \"Enter your email\")}\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n className=\"flex-1 h-10\"\r\n required\r\n />\r\n <Button type=\"submit\" size=\"sm\" className=\"h-10 px-4\">\r\n <Send className=\"h-4 w-4\" />\r\n </Button>\r\n </form>\r\n <p className=\"text-xs text-muted-foreground\">\r\n {t(\"newsletterNote\", \"Get updates on new products and exclusive offers.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Social Links */}\r\n {socialLinks.length > 0 && (\r\n <div className=\"flex gap-1 pt-2\">\r\n {socialLinks.map(({ platform, url, Icon }) => (\r\n <a\r\n key={platform}\r\n href={url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"h-10 w-10 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\"\r\n >\r\n <Icon className=\"h-5 w-5\" />\r\n </a>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {/* Link Sections */}\r\n {linkSections.map((section, idx) => (\r\n <div key={idx} className=\"space-y-4\">\r\n <h4 className=\"font-semibold\">{section.title}</h4>\r\n <ul className=\"space-y-2.5\">\r\n {section.links.map((link, linkIdx) => (\r\n <li key={linkIdx}>\r\n <Link\r\n to={link.url}\r\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\r\n >\r\n {link.text}\r\n </Link>\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n {/* Bottom Bar */}\r\n <div className=\"border-t bg-muted/50\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 py-6\">\r\n <div className=\"flex flex-col lg:flex-row justify-between items-center gap-6\">\r\n {/* Copyright & Contact */}\r\n <div className=\"flex flex-col sm:flex-row items-center gap-4 sm:gap-6 text-sm text-muted-foreground\">\r\n <span>© {currentYear} {constants.site.name}. {t(\"allRightsReserved\", \"All rights reserved.\")}</span>\r\n <div className=\"hidden sm:block w-px h-4 bg-border\" />\r\n <div className=\"flex items-center gap-4\">\r\n <a href={`tel:${constants.phone}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\r\n <Phone className=\"h-3.5 w-3.5\" />\r\n <span>{constants.phone}</span>\r\n </a>\r\n <a href={`mailto:${constants.email}`} className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\">\r\n <Mail className=\"h-3.5 w-3.5\" />\r\n <span>{constants.email}</span>\r\n </a>\r\n </div>\r\n </div>\r\n\r\n {/* Payment & Back to Top */}\r\n <div className=\"flex items-center gap-6\">\r\n {/* Payment Methods */}\r\n <div className=\"flex items-center gap-2 text-muted-foreground\">\r\n <CreditCard className=\"h-5 w-5\" />\r\n <span className=\"text-xs\">{t(\"securePayment\", \"Secure Payment\")}</span>\r\n </div>\r\n\r\n {/* Back to Top */}\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={scrollToTop}\r\n className=\"gap-2\"\r\n >\r\n <ArrowUp className=\"h-4 w-4\" />\r\n {t(\"backToTop\", \"Top\")}\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </footer>\r\n );\r\n}\r\n"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "footer-detailed/lang/en.json",
|
|
26
26
|
"type": "registry:lang",
|
|
27
27
|
"target": "$modules$/footer-detailed/lang/en.json",
|
|
28
|
-
"content": "{\n \"description\": \"Your trusted destination for quality products. We deliver excellence with every order.\",\n \"newsletter\": \"Subscribe to our newsletter\",\n \"emailPlaceholder\": \"Enter your email\",\n \"newsletterNote\": \"Get updates on new products and exclusive offers.\",\n \"shop\": \"Shop\",\n \"allProducts\": \"All Products\",\n \"featured\": \"Featured\",\n \"newArrivals\": \"New Arrivals\",\n \"onSale\": \"On Sale\",\n \"company\": \"Company\",\n \"about\": \"About Us\",\n \"careers\": \"Careers\",\n \"blog\": \"Blog\",\n \"press\": \"Press\",\n \"support\": \"Support\",\n \"helpCenter\": \"Help Center\",\n \"faq\": \"FAQ\",\n \"contact\": \"Contact Us\",\n \"returns\": \"Returns\",\n \"legal\": \"Legal\",\n \"privacy\": \"Privacy Policy\",\n \"terms\": \"Terms of Service\",\n \"cookies\": \"Cookie Policy\",\n \"refund\": \"Refund Policy\",\n \"securePayment\": \"Secure Payment\",\n \"backToTop\": \"Top\",\n \"allRightsReserved\": \"All rights reserved.\"\n}\n"
|
|
28
|
+
"content": "{\r\n \"description\": \"Your trusted destination for quality products. We deliver excellence with every order.\",\r\n \"newsletter\": \"Subscribe to our newsletter\",\r\n \"emailPlaceholder\": \"Enter your email\",\r\n \"newsletterNote\": \"Get updates on new products and exclusive offers.\",\r\n \"shop\": \"Shop\",\r\n \"allProducts\": \"All Products\",\r\n \"featured\": \"Featured\",\r\n \"newArrivals\": \"New Arrivals\",\r\n \"onSale\": \"On Sale\",\r\n \"company\": \"Company\",\r\n \"about\": \"About Us\",\r\n \"careers\": \"Careers\",\r\n \"blog\": \"Blog\",\r\n \"press\": \"Press\",\r\n \"support\": \"Support\",\r\n \"helpCenter\": \"Help Center\",\r\n \"faq\": \"FAQ\",\r\n \"contact\": \"Contact Us\",\r\n \"returns\": \"Returns\",\r\n \"legal\": \"Legal\",\r\n \"privacy\": \"Privacy Policy\",\r\n \"terms\": \"Terms of Service\",\r\n \"cookies\": \"Cookie Policy\",\r\n \"refund\": \"Refund Policy\",\r\n \"securePayment\": \"Secure Payment\",\r\n \"backToTop\": \"Top\",\r\n \"allRightsReserved\": \"All rights reserved.\"\r\n}\r\n"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "footer-detailed/lang/tr.json",
|
|
32
32
|
"type": "registry:lang",
|
|
33
33
|
"target": "$modules$/footer-detailed/lang/tr.json",
|
|
34
|
-
"content": "{\n \"description\": \"Kaliteli ürünler için güvenilir adresiniz. Her siparişte mükemmellik sunuyoruz.\",\n \"newsletter\": \"Bültenimize abone olun\",\n \"emailPlaceholder\": \"E-posta adresiniz\",\n \"newsletterNote\": \"Yeni ürünler ve özel tekliflerden haberdar olun.\",\n \"shop\": \"Mağaza\",\n \"allProducts\": \"Tüm Ürünler\",\n \"featured\": \"Öne Çıkanlar\",\n \"newArrivals\": \"Yeni Gelenler\",\n \"onSale\": \"İndirimli\",\n \"company\": \"Şirket\",\n \"about\": \"Hakkımızda\",\n \"careers\": \"Kariyer\",\n \"blog\": \"Blog\",\n \"press\": \"Basın\",\n \"support\": \"Destek\",\n \"helpCenter\": \"Yardım Merkezi\",\n \"faq\": \"SSS\",\n \"contact\": \"İletişim\",\n \"returns\": \"İade\",\n \"legal\": \"Yasal\",\n \"privacy\": \"Gizlilik Politikası\",\n \"terms\": \"Kullanım Şartları\",\n \"cookies\": \"Çerez Politikası\",\n \"refund\": \"İade Politikası\",\n \"securePayment\": \"Güvenli Ödeme\",\n \"backToTop\": \"Yukarı\",\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\n}\n"
|
|
34
|
+
"content": "{\r\n \"description\": \"Kaliteli ürünler için güvenilir adresiniz. Her siparişte mükemmellik sunuyoruz.\",\r\n \"newsletter\": \"Bültenimize abone olun\",\r\n \"emailPlaceholder\": \"E-posta adresiniz\",\r\n \"newsletterNote\": \"Yeni ürünler ve özel tekliflerden haberdar olun.\",\r\n \"shop\": \"Mağaza\",\r\n \"allProducts\": \"Tüm Ürünler\",\r\n \"featured\": \"Öne Çıkanlar\",\r\n \"newArrivals\": \"Yeni Gelenler\",\r\n \"onSale\": \"İndirimli\",\r\n \"company\": \"Şirket\",\r\n \"about\": \"Hakkımızda\",\r\n \"careers\": \"Kariyer\",\r\n \"blog\": \"Blog\",\r\n \"press\": \"Basın\",\r\n \"support\": \"Destek\",\r\n \"helpCenter\": \"Yardım Merkezi\",\r\n \"faq\": \"SSS\",\r\n \"contact\": \"İletişim\",\r\n \"returns\": \"İade\",\r\n \"legal\": \"Yasal\",\r\n \"privacy\": \"Gizlilik Politikası\",\r\n \"terms\": \"Kullanım Şartları\",\r\n \"cookies\": \"Çerez Politikası\",\r\n \"refund\": \"İade Politikası\",\r\n \"securePayment\": \"Güvenli Ödeme\",\r\n \"backToTop\": \"Yukarı\",\r\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\r\n}\r\n"
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
"exports": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"path": "footer-minimal/index.ts",
|
|
11
11
|
"type": "registry:index",
|
|
12
12
|
"target": "$modules$/footer-minimal/index.ts",
|
|
13
|
-
"content": "export * from './footer-minimal';\n"
|
|
13
|
+
"content": "export * from './footer-minimal';\r\n"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "footer-minimal/footer-minimal.tsx",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"path": "footer-minimal/lang/en.json",
|
|
23
23
|
"type": "registry:lang",
|
|
24
24
|
"target": "$modules$/footer-minimal/lang/en.json",
|
|
25
|
-
"content": "{\n \"privacy\": \"Privacy\",\n \"terms\": \"Terms\",\n \"allRightsReserved\": \"All rights reserved.\"\n}\n"
|
|
25
|
+
"content": "{\r\n \"privacy\": \"Privacy\",\r\n \"terms\": \"Terms\",\r\n \"allRightsReserved\": \"All rights reserved.\"\r\n}\r\n"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "footer-minimal/lang/tr.json",
|
|
29
29
|
"type": "registry:lang",
|
|
30
30
|
"target": "$modules$/footer-minimal/lang/tr.json",
|
|
31
|
-
"content": "{\n \"privacy\": \"Gizlilik\",\n \"terms\": \"Şartlar\",\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\n}\n"
|
|
31
|
+
"content": "{\r\n \"privacy\": \"Gizlilik\",\r\n \"terms\": \"Şartlar\",\r\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\r\n}\r\n"
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"exports": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"path": "footer/index.ts",
|
|
11
11
|
"type": "registry:index",
|
|
12
12
|
"target": "$modules$/footer/index.ts",
|
|
13
|
-
"content": "export * from './footer';\n"
|
|
13
|
+
"content": "export * from './footer';\r\n"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "footer/footer.tsx",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"path": "footer/lang/en.json",
|
|
23
23
|
"type": "registry:lang",
|
|
24
24
|
"target": "$modules$/footer/lang/en.json",
|
|
25
|
-
"content": "{\n \"description\": \"Let Promake personalize this footer description for your brand and purpose.\",\n \"quickLinks\": \"Quick Links\",\n \"about\": \"About\",\n \"contact\": \"Contact\",\n \"legal\": \"Legal\",\n \"privacy\": \"Privacy Policy\",\n \"terms\": \"Terms of Service\",\n \"cookies\": \"Cookie Policy\",\n \"contactTitle\": \"Contact\",\n \"allRightsReserved\": \"All rights reserved.\"\n}\n"
|
|
25
|
+
"content": "{\r\n \"description\": \"Let Promake personalize this footer description for your brand and purpose.\",\r\n \"quickLinks\": \"Quick Links\",\r\n \"about\": \"About\",\r\n \"contact\": \"Contact\",\r\n \"legal\": \"Legal\",\r\n \"privacy\": \"Privacy Policy\",\r\n \"terms\": \"Terms of Service\",\r\n \"cookies\": \"Cookie Policy\",\r\n \"contactTitle\": \"Contact\",\r\n \"allRightsReserved\": \"All rights reserved.\"\r\n}\r\n"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "footer/lang/tr.json",
|
|
29
29
|
"type": "registry:lang",
|
|
30
30
|
"target": "$modules$/footer/lang/tr.json",
|
|
31
|
-
"content": "{\n \"description\": \"Promake ile bu footer açıklamasını markanız ve amacınız için kişiselleştirin.\",\n \"quickLinks\": \"Hızlı Bağlantılar\",\n \"about\": \"Hakkımızda\",\n \"contact\": \"İletişim\",\n \"legal\": \"Yasal\",\n \"privacy\": \"Gizlilik Politikası\",\n \"terms\": \"Kullanım Şartları\",\n \"cookies\": \"Çerez Politikası\",\n \"contactTitle\": \"İletişim\",\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\n}\n"
|
|
31
|
+
"content": "{\r\n \"description\": \"Promake ile bu footer açıklamasını markanız ve amacınız için kişiselleştirin.\",\r\n \"quickLinks\": \"Hızlı Bağlantılar\",\r\n \"about\": \"Hakkımızda\",\r\n \"contact\": \"İletişim\",\r\n \"legal\": \"Yasal\",\r\n \"privacy\": \"Gizlilik Politikası\",\r\n \"terms\": \"Kullanım Şartları\",\r\n \"cookies\": \"Çerez Politikası\",\r\n \"contactTitle\": \"İletişim\",\r\n \"allRightsReserved\": \"Tüm hakları saklıdır.\"\r\n}\r\n"
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"exports": {
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"path": "forgot-password-page-split/index.ts",
|
|
18
18
|
"type": "registry:index",
|
|
19
19
|
"target": "$modules$/forgot-password-page-split/index.ts",
|
|
20
|
-
"content": "export * from './forgot-password-page-split';\nexport { default } from './forgot-password-page-split';\n"
|
|
20
|
+
"content": "export * from './forgot-password-page-split';\r\nexport { default } from './forgot-password-page-split';\r\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "forgot-password-page-split/forgot-password-page-split.tsx",
|
|
24
24
|
"type": "registry:page",
|
|
25
25
|
"target": "$modules$/forgot-password-page-split/forgot-password-page-split.tsx",
|
|
26
|
-
"content": "import { useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { toast } from \"sonner\";\nimport { useTranslation } from \"react-i18next\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Logo } from \"@/components/Logo\";\nimport { Mail, ArrowLeft, CheckCircle } from \"lucide-react\";\nimport { useAuth } from \"@/modules/auth-core\";\nimport { getErrorMessage } from \"@/modules/api\";\n\ninterface ForgotPasswordPageSplitProps {\n image?: string;\n}\n\nexport function ForgotPasswordPageSplit({\n image = \"/images/placeholder.png\",\n}: ForgotPasswordPageSplitProps) {\n const { t } = useTranslation(\"forgot-password-page-split\");\n usePageTitle({ title: t(\"title\", \"Forgot Password\") });\n const { forgotPassword } = useAuth();\n\n const [username, setUsername] = useState(\"\");\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [isEmailSent, setIsEmailSent] = useState(false);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n setIsLoading(true);\n\n try {\n await forgotPassword(username);\n\n setIsEmailSent(true);\n toast.success(t(\"emailSent\", \"Reset link sent!\"), {\n description: t(\"checkInbox\", \"Please check your email inbox.\"),\n });\n } catch (err) {\n const errorMessage = getErrorMessage(\n err,\n t(\"forgotPasswordError\", \"Failed to send reset link. Please try again.\")\n );\n setError(errorMessage);\n } finally {\n setIsLoading(false);\n }\n };\n\n // Success state - email sent\n if (isEmailSent) {\n return (\n <section className=\"w-full md:grid md:min-h-screen md:grid-cols-2\">\n <div className=\"flex items-center justify-center px-4 py-12\">\n <div className=\"mx-auto grid w-full max-w-sm gap-6 text-center\">\n <Logo />\n <hr />\n\n <div className=\"flex justify-center\">\n <div className=\"w-16 h-16 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center\">\n <CheckCircle className=\"w-8 h-8 text-green-600 dark:text-green-400\" />\n </div>\n </div>\n\n <div>\n <h1 className=\"text-xl font-bold tracking-tight\">\n {t(\"checkYourEmail\", \"Check your email\")}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-2\">\n {t(\"emailSentTo\", \"We've sent a password reset link to\")}\n </p>\n <p className=\"text-sm font-medium mt-1\">{username}</p>\n </div>\n\n <div className=\"text-sm text-muted-foreground\">\n <p>{t(\"didntReceive\", \"Didn't receive the email?\")}</p>\n <Button\n variant=\"link\"\n className=\"p-0 h-auto\"\n onClick={() => {\n setIsEmailSent(false);\n setError(null);\n }}\n >\n {t(\"tryAgain\", \"Click here to try again\")}\n </Button>\n </div>\n\n <Link\n to=\"/login\"\n className=\"inline-flex items-center justify-center gap-2 text-sm text-muted-foreground hover:text-foreground\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {t(\"backToLogin\", \"Back to login\")}\n </Link>\n\n <hr />\n <p className=\"text-sm text-muted-foreground\">\n © {new Date().getFullYear()} {t(\"copyright\", \"All rights reserved.\")}\n </p>\n </div>\n </div>\n <div className=\"hidden p-4 md:block\">\n <img\n loading=\"lazy\"\n decoding=\"async\"\n width=\"1920\"\n height=\"1080\"\n alt={t(\"imageAlt\", \"Forgot password background\")}\n src={image}\n className=\"size-full rounded-lg border bg-muted object-cover object-center\"\n />\n </div>\n </section>\n );\n }\n\n return (\n <section className=\"w-full md:grid md:min-h-screen md:grid-cols-2\">\n <div className=\"flex items-center justify-center px-4 py-12\">\n <div className=\"mx-auto grid w-full max-w-sm gap-6\">\n <Logo />\n <hr />\n\n <div className=\"flex justify-center\">\n <div className=\"w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center\">\n <Mail className=\"w-6 h-6 text-primary\" />\n </div>\n </div>\n\n <div className=\"text-center\">\n <h1 className=\"text-xl font-bold tracking-tight\">\n {t(\"title\", \"Forgot Password\")}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\"subtitle\", \"Enter your username to reset your password\")}\n </p>\n </div>\n\n {error && (\n <div className=\"p-3 text-sm text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400 rounded-md\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"grid gap-4\">\n <div className=\"grid gap-2\">\n <Label htmlFor=\"username\">{t(\"username\", \"Username\")}</Label>\n <Input\n required\n id=\"username\"\n type=\"text\"\n autoComplete=\"username\"\n placeholder={t(\"usernamePlaceholder\", \"Enter your username\")}\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n disabled={isLoading}\n />\n </div>\n\n <Button type=\"submit\" className=\"w-full\" disabled={isLoading}>\n {isLoading ? (\n <>\n <div className=\"w-4 h-4 border-2 border-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\", \"Sending...\")}\n </>\n ) : (\n t(\"sendLink\", \"Send Reset Link\")\n )}\n </Button>\n </form>\n\n <Link\n to=\"/login\"\n className=\"inline-flex items-center justify-center gap-2 text-sm text-muted-foreground hover:text-foreground\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {t(\"backToLogin\", \"Back to login\")}\n </Link>\n\n <hr />\n <p className=\"text-sm text-muted-foreground\">\n © {new Date().getFullYear()} {t(\"copyright\", \"All rights reserved.\")}\n </p>\n </div>\n </div>\n <div className=\"hidden p-4 md:block\">\n <img\n loading=\"lazy\"\n decoding=\"async\"\n width=\"1920\"\n height=\"1080\"\n alt={t(\"imageAlt\", \"Forgot password background\")}\n src={image}\n className=\"size-full rounded-lg border bg-muted object-cover object-center\"\n />\n </div>\n </section>\n );\n}\n\nexport default ForgotPasswordPageSplit;\n"
|
|
26
|
+
"content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { toast } from \"sonner\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { Mail, ArrowLeft, CheckCircle } from \"lucide-react\";\r\nimport { useAuth } from \"@/modules/auth-core\";\r\nimport { getErrorMessage } from \"@/modules/api\";\r\n\r\ninterface ForgotPasswordPageSplitProps {\r\n image?: string;\r\n}\r\n\r\nexport function ForgotPasswordPageSplit({\r\n image = \"/images/placeholder.png\",\r\n}: ForgotPasswordPageSplitProps) {\r\n const { t } = useTranslation(\"forgot-password-page-split\");\r\n usePageTitle({ title: t(\"title\", \"Forgot Password\") });\r\n const { forgotPassword } = useAuth();\r\n\r\n const [username, setUsername] = useState(\"\");\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [isEmailSent, setIsEmailSent] = useState(false);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setError(null);\r\n setIsLoading(true);\r\n\r\n try {\r\n await forgotPassword(username);\r\n\r\n setIsEmailSent(true);\r\n toast.success(t(\"emailSent\", \"Reset link sent!\"), {\r\n description: t(\"checkInbox\", \"Please check your email inbox.\"),\r\n });\r\n } catch (err) {\r\n const errorMessage = getErrorMessage(\r\n err,\r\n t(\"forgotPasswordError\", \"Failed to send reset link. Please try again.\")\r\n );\r\n setError(errorMessage);\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n // Success state - email sent\r\n if (isEmailSent) {\r\n return (\r\n <section className=\"w-full md:grid md:min-h-screen md:grid-cols-2\">\r\n <div className=\"flex items-center justify-center px-4 py-12\">\r\n <div className=\"mx-auto grid w-full max-w-sm gap-6 text-center\">\r\n <Logo />\r\n <hr />\r\n\r\n <div className=\"flex justify-center\">\r\n <div className=\"w-16 h-16 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center\">\r\n <CheckCircle className=\"w-8 h-8 text-green-600 dark:text-green-400\" />\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <h1 className=\"text-xl font-bold tracking-tight\">\r\n {t(\"checkYourEmail\", \"Check your email\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground mt-2\">\r\n {t(\"emailSentTo\", \"We've sent a password reset link to\")}\r\n </p>\r\n <p className=\"text-sm font-medium mt-1\">{username}</p>\r\n </div>\r\n\r\n <div className=\"text-sm text-muted-foreground\">\r\n <p>{t(\"didntReceive\", \"Didn't receive the email?\")}</p>\r\n <Button\r\n variant=\"link\"\r\n className=\"p-0 h-auto\"\r\n onClick={() => {\r\n setIsEmailSent(false);\r\n setError(null);\r\n }}\r\n >\r\n {t(\"tryAgain\", \"Click here to try again\")}\r\n </Button>\r\n </div>\r\n\r\n <Link\r\n to=\"/login\"\r\n className=\"inline-flex items-center justify-center gap-2 text-sm text-muted-foreground hover:text-foreground\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t(\"backToLogin\", \"Back to login\")}\r\n </Link>\r\n\r\n <hr />\r\n <p className=\"text-sm text-muted-foreground\">\r\n © {new Date().getFullYear()} {t(\"copyright\", \"All rights reserved.\")}\r\n </p>\r\n </div>\r\n </div>\r\n <div className=\"hidden p-4 md:block\">\r\n <img\r\n loading=\"lazy\"\r\n decoding=\"async\"\r\n width=\"1920\"\r\n height=\"1080\"\r\n alt={t(\"imageAlt\", \"Forgot password background\")}\r\n src={image}\r\n className=\"size-full rounded-lg border bg-muted object-cover object-center\"\r\n />\r\n </div>\r\n </section>\r\n );\r\n }\r\n\r\n return (\r\n <section className=\"w-full md:grid md:min-h-screen md:grid-cols-2\">\r\n <div className=\"flex items-center justify-center px-4 py-12\">\r\n <div className=\"mx-auto grid w-full max-w-sm gap-6\">\r\n <Logo />\r\n <hr />\r\n\r\n <div className=\"flex justify-center\">\r\n <div className=\"w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center\">\r\n <Mail className=\"w-6 h-6 text-primary\" />\r\n </div>\r\n </div>\r\n\r\n <div className=\"text-center\">\r\n <h1 className=\"text-xl font-bold tracking-tight\">\r\n {t(\"title\", \"Forgot Password\")}\r\n </h1>\r\n <p className=\"text-sm text-muted-foreground mt-1\">\r\n {t(\"subtitle\", \"Enter your username to reset your password\")}\r\n </p>\r\n </div>\r\n\r\n {error && (\r\n <div className=\"p-3 text-sm text-red-600 bg-red-50 dark:bg-red-950 dark:text-red-400 rounded-md\">\r\n {error}\r\n </div>\r\n )}\r\n\r\n <form onSubmit={handleSubmit} className=\"grid gap-4\">\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"username\">{t(\"username\", \"Username\")}</Label>\r\n <Input\r\n required\r\n id=\"username\"\r\n type=\"text\"\r\n autoComplete=\"username\"\r\n placeholder={t(\"usernamePlaceholder\", \"Enter your username\")}\r\n value={username}\r\n onChange={(e) => setUsername(e.target.value)}\r\n disabled={isLoading}\r\n />\r\n </div>\r\n\r\n <Button type=\"submit\" className=\"w-full\" disabled={isLoading}>\r\n {isLoading ? (\r\n <>\r\n <div className=\"w-4 h-4 border-2 border-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\r\n {t(\"sending\", \"Sending...\")}\r\n </>\r\n ) : (\r\n t(\"sendLink\", \"Send Reset Link\")\r\n )}\r\n </Button>\r\n </form>\r\n\r\n <Link\r\n to=\"/login\"\r\n className=\"inline-flex items-center justify-center gap-2 text-sm text-muted-foreground hover:text-foreground\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t(\"backToLogin\", \"Back to login\")}\r\n </Link>\r\n\r\n <hr />\r\n <p className=\"text-sm text-muted-foreground\">\r\n © {new Date().getFullYear()} {t(\"copyright\", \"All rights reserved.\")}\r\n </p>\r\n </div>\r\n </div>\r\n <div className=\"hidden p-4 md:block\">\r\n <img\r\n loading=\"lazy\"\r\n decoding=\"async\"\r\n width=\"1920\"\r\n height=\"1080\"\r\n alt={t(\"imageAlt\", \"Forgot password background\")}\r\n src={image}\r\n className=\"size-full rounded-lg border bg-muted object-cover object-center\"\r\n />\r\n </div>\r\n </section>\r\n );\r\n}\r\n\r\nexport default ForgotPasswordPageSplit;\r\n"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"path": "forgot-password-page-split/lang/en.json",
|
|
30
30
|
"type": "registry:lang",
|
|
31
31
|
"target": "$modules$/forgot-password-page-split/lang/en.json",
|
|
32
|
-
"content": "{\n \"title\": \"Forgot Password\",\n \"subtitle\": \"Enter your username to reset your password\",\n \"username\": \"Username\",\n \"usernamePlaceholder\": \"Enter your username\",\n \"email\": \"Email\",\n \"emailPlaceholder\": \"you@example.com\",\n \"sendLink\": \"Send Reset Link\",\n \"sending\": \"Sending...\",\n \"emailSent\": \"Reset link sent!\",\n \"checkInbox\": \"Please check your email inbox.\",\n \"forgotPasswordError\": \"Failed to send reset link. Please try again.\",\n \"checkYourEmail\": \"Check your email\",\n \"emailSentTo\": \"We've sent a password reset link to\",\n \"didntReceive\": \"Didn't receive the email?\",\n \"tryAgain\": \"Click here to try again\",\n \"backToLogin\": \"Back to login\",\n \"copyright\": \"All rights reserved.\",\n \"imageAlt\": \"Forgot password background\"\n}\n"
|
|
32
|
+
"content": "{\r\n \"title\": \"Forgot Password\",\r\n \"subtitle\": \"Enter your username to reset your password\",\r\n \"username\": \"Username\",\r\n \"usernamePlaceholder\": \"Enter your username\",\r\n \"email\": \"Email\",\r\n \"emailPlaceholder\": \"you@example.com\",\r\n \"sendLink\": \"Send Reset Link\",\r\n \"sending\": \"Sending...\",\r\n \"emailSent\": \"Reset link sent!\",\r\n \"checkInbox\": \"Please check your email inbox.\",\r\n \"forgotPasswordError\": \"Failed to send reset link. Please try again.\",\r\n \"checkYourEmail\": \"Check your email\",\r\n \"emailSentTo\": \"We've sent a password reset link to\",\r\n \"didntReceive\": \"Didn't receive the email?\",\r\n \"tryAgain\": \"Click here to try again\",\r\n \"backToLogin\": \"Back to login\",\r\n \"copyright\": \"All rights reserved.\",\r\n \"imageAlt\": \"Forgot password background\"\r\n}\r\n"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "forgot-password-page-split/lang/tr.json",
|
|
36
36
|
"type": "registry:lang",
|
|
37
37
|
"target": "$modules$/forgot-password-page-split/lang/tr.json",
|
|
38
|
-
"content": "{\n \"title\": \"Şifremi Unuttum\",\n \"subtitle\": \"Şifrenizi sıfırlamak için kullanıcı adınızı girin\",\n \"username\": \"Kullanıcı Adı\",\n \"usernamePlaceholder\": \"Kullanıcı adınızı girin\",\n \"email\": \"E-posta\",\n \"emailPlaceholder\": \"ornek@email.com\",\n \"sendLink\": \"Sıfırlama Linki Gönder\",\n \"sending\": \"Gönderiliyor...\",\n \"emailSent\": \"Sıfırlama linki gönderildi!\",\n \"checkInbox\": \"Lütfen e-posta gelen kutunuzu kontrol edin.\",\n \"forgotPasswordError\": \"Sıfırlama linki gönderilemedi. Lütfen tekrar deneyin.\",\n \"checkYourEmail\": \"E-postanızı kontrol edin\",\n \"emailSentTo\": \"Şifre sıfırlama linki gönderildi:\",\n \"didntReceive\": \"E-posta almadınız mı?\",\n \"tryAgain\": \"Tekrar denemek için tıklayın\",\n \"backToLogin\": \"Girişe dön\",\n \"copyright\": \"Tüm hakları saklıdır.\",\n \"imageAlt\": \"Şifre sıfırlama arka planı\"\n}\n"
|
|
38
|
+
"content": "{\r\n \"title\": \"Şifremi Unuttum\",\r\n \"subtitle\": \"Şifrenizi sıfırlamak için kullanıcı adınızı girin\",\r\n \"username\": \"Kullanıcı Adı\",\r\n \"usernamePlaceholder\": \"Kullanıcı adınızı girin\",\r\n \"email\": \"E-posta\",\r\n \"emailPlaceholder\": \"ornek@email.com\",\r\n \"sendLink\": \"Sıfırlama Linki Gönder\",\r\n \"sending\": \"Gönderiliyor...\",\r\n \"emailSent\": \"Sıfırlama linki gönderildi!\",\r\n \"checkInbox\": \"Lütfen e-posta gelen kutunuzu kontrol edin.\",\r\n \"forgotPasswordError\": \"Sıfırlama linki gönderilemedi. Lütfen tekrar deneyin.\",\r\n \"checkYourEmail\": \"E-postanızı kontrol edin\",\r\n \"emailSentTo\": \"Şifre sıfırlama linki gönderildi:\",\r\n \"didntReceive\": \"E-posta almadınız mı?\",\r\n \"tryAgain\": \"Tekrar denemek için tıklayın\",\r\n \"backToLogin\": \"Girişe dön\",\r\n \"copyright\": \"Tüm hakları saklıdır.\",\r\n \"imageAlt\": \"Şifre sıfırlama arka planı\"\r\n}\r\n"
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
41
|
"exports": {
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"path": "forgot-password-page/index.ts",
|
|
18
18
|
"type": "registry:index",
|
|
19
19
|
"target": "$modules$/forgot-password-page/index.ts",
|
|
20
|
-
"content": "export * from './forgot-password-page';\nexport { default } from './forgot-password-page';\n"
|
|
20
|
+
"content": "export * from './forgot-password-page';\r\nexport { default } from './forgot-password-page';\r\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "forgot-password-page/forgot-password-page.tsx",
|
|
24
24
|
"type": "registry:page",
|
|
25
25
|
"target": "$modules$/forgot-password-page/forgot-password-page.tsx",
|
|
26
|
-
"content": "import { useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { toast } from \"sonner\";\nimport { Layout } from \"@/components/Layout\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { useAuth } from \"@/modules/auth-core\";\nimport { getErrorMessage } from \"@/modules/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n CardDescription,\n} from \"@/components/ui/card\";\nimport { KeyRound, ArrowLeft, Eye, EyeOff, CheckCircle2 } from \"lucide-react\";\n\ntype Step = \"request\" | \"reset\" | \"success\";\n\nexport function ForgotPasswordPage() {\n const { t } = useTranslation(\"forgot-password-page\");\n usePageTitle({ title: t(\"title\", \"Forgot Password\") });\n\n const { forgotPassword, resetPassword } = useAuth();\n\n const [step, setStep] = useState<Step>(\"request\");\n const [username, setUsername] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [newPassword, setNewPassword] = useState(\"\");\n const [confirmPassword, setConfirmPassword] = useState(\"\");\n const [showPassword, setShowPassword] = useState(false);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleRequestCode = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setError(null);\n\n try {\n await forgotPassword(username);\n toast.success(t(\"codeSentTitle\", \"Code Sent!\"), {\n description: t(\n \"codeSentDesc\",\n \"A password reset code has been sent to your email.\",\n ),\n });\n setStep(\"reset\");\n } catch (err) {\n const errorMessage = getErrorMessage(\n err,\n t(\"errorGeneric\", \"Failed to send reset code. Please try again.\"),\n );\n setError(errorMessage);\n toast.error(t(\"errorTitle\", \"Error\"), {\n description: errorMessage,\n });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleResetPassword = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n // Validate passwords match\n if (newPassword !== confirmPassword) {\n setError(t(\"passwordMismatch\", \"Passwords do not match\"));\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n await resetPassword(username, code, newPassword);\n toast.success(t(\"resetSuccessTitle\", \"Password Reset!\"), {\n description: t(\n \"resetSuccessDesc\",\n \"Your password has been successfully reset.\",\n ),\n });\n setStep(\"success\");\n } catch (err) {\n const errorMessage = getErrorMessage(\n err,\n t(\"errorResetGeneric\", \"Failed to reset password. Please try again.\"),\n );\n setError(errorMessage);\n toast.error(t(\"errorTitle\", \"Error\"), {\n description: errorMessage,\n });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n // Success step\n if (step === \"success\") {\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 <div className=\"max-w-md mx-auto\">\n <Card>\n <CardContent className=\"pt-8 pb-8 text-center\">\n <CheckCircle2 className=\"w-16 h-16 text-green-500 mx-auto mb-4\" />\n <h1 className=\"text-2xl font-bold mb-2\">\n {t(\"successTitle\", \"Password Reset Successfully!\")}\n </h1>\n <p className=\"text-muted-foreground mb-6\">\n {t(\n \"successDescription\",\n \"Your password has been changed. You can now login with your new password.\",\n )}\n </p>\n <Button asChild className=\"w-full\">\n <Link to=\"/login\">{t(\"goToLogin\", \"Go to Login\")}</Link>\n </Button>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </Layout>\n );\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 {/* Hero Section */}\n <div className=\"text-center mb-12\">\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\", \"Forgot Password\")}\n </h1>\n <div className=\"w-16 h-1 bg-primary mx-auto mb-6\"></div>\n <p className=\"text-lg text-muted-foreground max-w-xl mx-auto\">\n {step === \"request\"\n ? t(\n \"descriptionRequest\",\n \"Enter your username and we'll send you a code to reset your password.\",\n )\n : t(\n \"descriptionReset\",\n \"Enter the code sent to your email and your new password.\",\n )}\n </p>\n </div>\n\n <div className=\"max-w-md mx-auto\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <KeyRound className=\"w-5 h-5 text-primary\" />\n {step === \"request\"\n ? t(\"cardTitleRequest\", \"Request Reset Code\")\n : t(\"cardTitleReset\", \"Reset Password\")}\n </CardTitle>\n <CardDescription>\n {step === \"request\"\n ? t(\"cardDescRequest\", \"Step 1 of 2: Request a reset code\")\n : t(\n \"cardDescReset\",\n \"Step 2 of 2: Enter code and new password\",\n )}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {step === \"request\" ? (\n // Step 1: Request Code\n <form onSubmit={handleRequestCode} className=\"space-y-6\">\n <div>\n <Label htmlFor=\"username\">\n {t(\"username\", \"Username\")} *\n </Label>\n <Input\n id=\"username\"\n type=\"text\"\n value={username}\n onChange={(e) => setUsername(e.target.value)}\n placeholder={t(\n \"usernamePlaceholder\",\n \"Enter your username\",\n )}\n required\n className=\"mt-1\"\n autoComplete=\"username\"\n />\n </div>\n\n {error && (\n <div className=\"p-4 bg-red-50 border border-red-200 rounded-lg\">\n <p className=\"text-red-800 text-sm font-medium\">\n {error}\n </p>\n </div>\n )}\n\n <Button\n type=\"submit\"\n size=\"lg\"\n className=\"w-full\"\n disabled={isSubmitting}\n >\n {isSubmitting ? (\n <>\n <div className=\"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\", \"Sending...\")}\n </>\n ) : (\n t(\"sendCode\", \"Send Reset Code\")\n )}\n </Button>\n\n <div className=\"text-center\">\n <Link\n to=\"/login\"\n className=\"text-sm text-muted-foreground hover:text-primary inline-flex items-center gap-1\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {t(\"backToLogin\", \"Back to Login\")}\n </Link>\n </div>\n </form>\n ) : (\n // Step 2: Reset Password\n <form onSubmit={handleResetPassword} className=\"space-y-6\">\n <div className=\"p-3 bg-muted rounded-lg text-sm\">\n <span className=\"text-muted-foreground\">\n {t(\"codeFor\", \"Reset code for:\")}{\" \"}\n </span>\n <span className=\"font-medium\">{username}</span>\n </div>\n\n <div>\n <Label htmlFor=\"code\">{t(\"code\", \"Reset Code\")} *</Label>\n <Input\n id=\"code\"\n type=\"text\"\n value={code}\n onChange={(e) => setCode(e.target.value)}\n placeholder={t(\"codePlaceholder\", \"Enter 6-digit code\")}\n required\n className=\"mt-1\"\n maxLength={6}\n />\n </div>\n\n <div>\n <Label htmlFor=\"newPassword\">\n {t(\"newPassword\", \"New Password\")} *\n </Label>\n <div className=\"relative\">\n <Input\n id=\"newPassword\"\n type={showPassword ? \"text\" : \"password\"}\n value={newPassword}\n onChange={(e) => setNewPassword(e.target.value)}\n placeholder={t(\n \"newPasswordPlaceholder\",\n \"Enter new password\",\n )}\n required\n className=\"mt-1 pr-10\"\n autoComplete=\"new-password\"\n />\n <button\n type=\"button\"\n onClick={() => setShowPassword(!showPassword)}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\n >\n {showPassword ? (\n <EyeOff className=\"w-4 h-4\" />\n ) : (\n <Eye className=\"w-4 h-4\" />\n )}\n </button>\n </div>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t(\n \"passwordRequirements\",\n \"At least 8 characters, 1 letter and 1 number\",\n )}\n </p>\n </div>\n\n <div>\n <Label htmlFor=\"confirmPassword\">\n {t(\"confirmPassword\", \"Confirm Password\")} *\n </Label>\n <Input\n id=\"confirmPassword\"\n type={showPassword ? \"text\" : \"password\"}\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n placeholder={t(\n \"confirmPasswordPlaceholder\",\n \"Confirm new password\",\n )}\n required\n className=\"mt-1\"\n autoComplete=\"new-password\"\n />\n </div>\n\n {error && (\n <div className=\"p-4 bg-red-50 border border-red-200 rounded-lg\">\n <p className=\"text-red-800 text-sm font-medium\">\n {error}\n </p>\n </div>\n )}\n\n <Button\n type=\"submit\"\n size=\"lg\"\n className=\"w-full\"\n disabled={isSubmitting}\n >\n {isSubmitting ? (\n <>\n <div className=\"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"resetting\", \"Resetting...\")}\n </>\n ) : (\n t(\"resetPassword\", \"Reset Password\")\n )}\n </Button>\n\n <div className=\"flex justify-between\">\n <button\n type=\"button\"\n onClick={() => {\n setStep(\"request\");\n setCode(\"\");\n setNewPassword(\"\");\n setConfirmPassword(\"\");\n setError(null);\n }}\n className=\"text-sm text-muted-foreground hover:text-primary\"\n >\n {t(\"changeUsername\", \"Change username\")}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n handleRequestCode({\n preventDefault: () => {},\n } as React.FormEvent)\n }\n className=\"text-sm text-primary hover:underline\"\n disabled={isSubmitting}\n >\n {t(\"resendCode\", \"Resend code\")}\n </button>\n </div>\n </form>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default ForgotPasswordPage;\n"
|
|
26
|
+
"content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { toast } from \"sonner\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { useAuth } from \"@/modules/auth-core\";\r\nimport { getErrorMessage } from \"@/modules/api\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport {\r\n Card,\r\n CardContent,\r\n CardHeader,\r\n CardTitle,\r\n CardDescription,\r\n} from \"@/components/ui/card\";\r\nimport { KeyRound, ArrowLeft, Eye, EyeOff, CheckCircle2 } from \"lucide-react\";\r\n\r\ntype Step = \"request\" | \"reset\" | \"success\";\r\n\r\nexport function ForgotPasswordPage() {\r\n const { t } = useTranslation(\"forgot-password-page\");\r\n usePageTitle({ title: t(\"title\", \"Forgot Password\") });\r\n\r\n const { forgotPassword, resetPassword } = useAuth();\r\n\r\n const [step, setStep] = useState<Step>(\"request\");\r\n const [username, setUsername] = useState(\"\");\r\n const [code, setCode] = useState(\"\");\r\n const [newPassword, setNewPassword] = useState(\"\");\r\n const [confirmPassword, setConfirmPassword] = useState(\"\");\r\n const [showPassword, setShowPassword] = useState(false);\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n\r\n const handleRequestCode = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n setError(null);\r\n\r\n try {\r\n await forgotPassword(username);\r\n toast.success(t(\"codeSentTitle\", \"Code Sent!\"), {\r\n description: t(\r\n \"codeSentDesc\",\r\n \"A password reset code has been sent to your email.\",\r\n ),\r\n });\r\n setStep(\"reset\");\r\n } catch (err) {\r\n const errorMessage = getErrorMessage(\r\n err,\r\n t(\"errorGeneric\", \"Failed to send reset code. Please try again.\"),\r\n );\r\n setError(errorMessage);\r\n toast.error(t(\"errorTitle\", \"Error\"), {\r\n description: errorMessage,\r\n });\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n const handleResetPassword = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setError(null);\r\n\r\n // Validate passwords match\r\n if (newPassword !== confirmPassword) {\r\n setError(t(\"passwordMismatch\", \"Passwords do not match\"));\r\n return;\r\n }\r\n\r\n setIsSubmitting(true);\r\n\r\n try {\r\n await resetPassword(username, code, newPassword);\r\n toast.success(t(\"resetSuccessTitle\", \"Password Reset!\"), {\r\n description: t(\r\n \"resetSuccessDesc\",\r\n \"Your password has been successfully reset.\",\r\n ),\r\n });\r\n setStep(\"success\");\r\n } catch (err) {\r\n const errorMessage = getErrorMessage(\r\n err,\r\n t(\"errorResetGeneric\", \"Failed to reset password. Please try again.\"),\r\n );\r\n setError(errorMessage);\r\n toast.error(t(\"errorTitle\", \"Error\"), {\r\n description: errorMessage,\r\n });\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n // Success step\r\n if (step === \"success\") {\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen bg-muted/30 py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"max-w-md mx-auto\">\r\n <Card>\r\n <CardContent className=\"pt-8 pb-8 text-center\">\r\n <CheckCircle2 className=\"w-16 h-16 text-green-500 mx-auto mb-4\" />\r\n <h1 className=\"text-2xl font-bold mb-2\">\r\n {t(\"successTitle\", \"Password Reset Successfully!\")}\r\n </h1>\r\n <p className=\"text-muted-foreground mb-6\">\r\n {t(\r\n \"successDescription\",\r\n \"Your password has been changed. You can now login with your new password.\",\r\n )}\r\n </p>\r\n <Button asChild className=\"w-full\">\r\n <Link to=\"/login\">{t(\"goToLogin\", \"Go to Login\")}</Link>\r\n </Button>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n }\r\n\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen bg-muted/30 py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {/* Hero Section */}\r\n <div className=\"text-center mb-12\">\r\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\r\n {t(\"title\", \"Forgot Password\")}\r\n </h1>\r\n <div className=\"w-16 h-1 bg-primary mx-auto mb-6\"></div>\r\n <p className=\"text-lg text-muted-foreground max-w-xl mx-auto\">\r\n {step === \"request\"\r\n ? t(\r\n \"descriptionRequest\",\r\n \"Enter your username and we'll send you a code to reset your password.\",\r\n )\r\n : t(\r\n \"descriptionReset\",\r\n \"Enter the code sent to your email and your new password.\",\r\n )}\r\n </p>\r\n </div>\r\n\r\n <div className=\"max-w-md mx-auto\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <KeyRound className=\"w-5 h-5 text-primary\" />\r\n {step === \"request\"\r\n ? t(\"cardTitleRequest\", \"Request Reset Code\")\r\n : t(\"cardTitleReset\", \"Reset Password\")}\r\n </CardTitle>\r\n <CardDescription>\r\n {step === \"request\"\r\n ? t(\"cardDescRequest\", \"Step 1 of 2: Request a reset code\")\r\n : t(\r\n \"cardDescReset\",\r\n \"Step 2 of 2: Enter code and new password\",\r\n )}\r\n </CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n {step === \"request\" ? (\r\n // Step 1: Request Code\r\n <form onSubmit={handleRequestCode} className=\"space-y-6\">\r\n <div>\r\n <Label htmlFor=\"username\">\r\n {t(\"username\", \"Username\")} *\r\n </Label>\r\n <Input\r\n id=\"username\"\r\n type=\"text\"\r\n value={username}\r\n onChange={(e) => setUsername(e.target.value)}\r\n placeholder={t(\r\n \"usernamePlaceholder\",\r\n \"Enter your username\",\r\n )}\r\n required\r\n className=\"mt-1\"\r\n autoComplete=\"username\"\r\n />\r\n </div>\r\n\r\n {error && (\r\n <div className=\"p-4 bg-red-50 border border-red-200 rounded-lg\">\r\n <p className=\"text-red-800 text-sm font-medium\">\r\n {error}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button\r\n type=\"submit\"\r\n size=\"lg\"\r\n className=\"w-full\"\r\n disabled={isSubmitting}\r\n >\r\n {isSubmitting ? (\r\n <>\r\n <div className=\"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2\" />\r\n {t(\"sending\", \"Sending...\")}\r\n </>\r\n ) : (\r\n t(\"sendCode\", \"Send Reset Code\")\r\n )}\r\n </Button>\r\n\r\n <div className=\"text-center\">\r\n <Link\r\n to=\"/login\"\r\n className=\"text-sm text-muted-foreground hover:text-primary inline-flex items-center gap-1\"\r\n >\r\n <ArrowLeft className=\"w-4 h-4\" />\r\n {t(\"backToLogin\", \"Back to Login\")}\r\n </Link>\r\n </div>\r\n </form>\r\n ) : (\r\n // Step 2: Reset Password\r\n <form onSubmit={handleResetPassword} className=\"space-y-6\">\r\n <div className=\"p-3 bg-muted rounded-lg text-sm\">\r\n <span className=\"text-muted-foreground\">\r\n {t(\"codeFor\", \"Reset code for:\")}{\" \"}\r\n </span>\r\n <span className=\"font-medium\">{username}</span>\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"code\">{t(\"code\", \"Reset Code\")} *</Label>\r\n <Input\r\n id=\"code\"\r\n type=\"text\"\r\n value={code}\r\n onChange={(e) => setCode(e.target.value)}\r\n placeholder={t(\"codePlaceholder\", \"Enter 6-digit code\")}\r\n required\r\n className=\"mt-1\"\r\n maxLength={6}\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"newPassword\">\r\n {t(\"newPassword\", \"New Password\")} *\r\n </Label>\r\n <div className=\"relative\">\r\n <Input\r\n id=\"newPassword\"\r\n type={showPassword ? \"text\" : \"password\"}\r\n value={newPassword}\r\n onChange={(e) => setNewPassword(e.target.value)}\r\n placeholder={t(\r\n \"newPasswordPlaceholder\",\r\n \"Enter new password\",\r\n )}\r\n required\r\n className=\"mt-1 pr-10\"\r\n autoComplete=\"new-password\"\r\n />\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowPassword(!showPassword)}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\r\n >\r\n {showPassword ? (\r\n <EyeOff className=\"w-4 h-4\" />\r\n ) : (\r\n <Eye className=\"w-4 h-4\" />\r\n )}\r\n </button>\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {t(\r\n \"passwordRequirements\",\r\n \"At least 8 characters, 1 letter and 1 number\",\r\n )}\r\n </p>\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"confirmPassword\">\r\n {t(\"confirmPassword\", \"Confirm Password\")} *\r\n </Label>\r\n <Input\r\n id=\"confirmPassword\"\r\n type={showPassword ? \"text\" : \"password\"}\r\n value={confirmPassword}\r\n onChange={(e) => setConfirmPassword(e.target.value)}\r\n placeholder={t(\r\n \"confirmPasswordPlaceholder\",\r\n \"Confirm new password\",\r\n )}\r\n required\r\n className=\"mt-1\"\r\n autoComplete=\"new-password\"\r\n />\r\n </div>\r\n\r\n {error && (\r\n <div className=\"p-4 bg-red-50 border border-red-200 rounded-lg\">\r\n <p className=\"text-red-800 text-sm font-medium\">\r\n {error}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button\r\n type=\"submit\"\r\n size=\"lg\"\r\n className=\"w-full\"\r\n disabled={isSubmitting}\r\n >\r\n {isSubmitting ? (\r\n <>\r\n <div className=\"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2\" />\r\n {t(\"resetting\", \"Resetting...\")}\r\n </>\r\n ) : (\r\n t(\"resetPassword\", \"Reset Password\")\r\n )}\r\n </Button>\r\n\r\n <div className=\"flex justify-between\">\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setStep(\"request\");\r\n setCode(\"\");\r\n setNewPassword(\"\");\r\n setConfirmPassword(\"\");\r\n setError(null);\r\n }}\r\n className=\"text-sm text-muted-foreground hover:text-primary\"\r\n >\r\n {t(\"changeUsername\", \"Change username\")}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() =>\r\n handleRequestCode({\r\n preventDefault: () => {},\r\n } as React.FormEvent)\r\n }\r\n className=\"text-sm text-primary hover:underline\"\r\n disabled={isSubmitting}\r\n >\r\n {t(\"resendCode\", \"Resend code\")}\r\n </button>\r\n </div>\r\n </form>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n\r\nexport default ForgotPasswordPage;\r\n"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"path": "forgot-password-page/lang/en.json",
|
|
30
30
|
"type": "registry:lang",
|
|
31
31
|
"target": "$modules$/forgot-password-page/lang/en.json",
|
|
32
|
-
"content": "{\n \"title\": \"Forgot Password\",\n \"descriptionRequest\": \"Enter your username and we'll send you a code to reset your password.\",\n \"descriptionReset\": \"Enter the code sent to your email and your new password.\",\n \"cardTitleRequest\": \"Request Reset Code\",\n \"cardTitleReset\": \"Reset Password\",\n \"cardDescRequest\": \"Step 1 of 2: Request a reset code\",\n \"cardDescReset\": \"Step 2 of 2: Enter code and new password\",\n \"username\": \"Username\",\n \"usernamePlaceholder\": \"Enter your username\",\n \"code\": \"Reset Code\",\n \"codePlaceholder\": \"Enter 6-digit code\",\n \"codeFor\": \"Reset code for:\",\n \"newPassword\": \"New Password\",\n \"newPasswordPlaceholder\": \"Enter new password\",\n \"confirmPassword\": \"Confirm Password\",\n \"confirmPasswordPlaceholder\": \"Confirm new password\",\n \"passwordRequirements\": \"At least 8 characters, 1 letter and 1 number\",\n \"passwordMismatch\": \"Passwords do not match\",\n \"sendCode\": \"Send Reset Code\",\n \"sending\": \"Sending...\",\n \"resetPassword\": \"Reset Password\",\n \"resetting\": \"Resetting...\",\n \"backToLogin\": \"Back to Login\",\n \"changeUsername\": \"Change username\",\n \"resendCode\": \"Resend code\",\n \"codeSentTitle\": \"Code Sent!\",\n \"codeSentDesc\": \"A password reset code has been sent to your email.\",\n \"errorTitle\": \"Error\",\n \"errorGeneric\": \"Failed to send reset code. Please try again.\",\n \"errorResetGeneric\": \"Failed to reset password. Please try again.\",\n \"resetSuccessTitle\": \"Password Reset!\",\n \"resetSuccessDesc\": \"Your password has been successfully reset.\",\n \"successTitle\": \"Password Reset Successfully!\",\n \"successDescription\": \"Your password has been changed. You can now login with your new password.\",\n \"goToLogin\": \"Go to Login\"\n}\n"
|
|
32
|
+
"content": "{\r\n \"title\": \"Forgot Password\",\r\n \"descriptionRequest\": \"Enter your username and we'll send you a code to reset your password.\",\r\n \"descriptionReset\": \"Enter the code sent to your email and your new password.\",\r\n \"cardTitleRequest\": \"Request Reset Code\",\r\n \"cardTitleReset\": \"Reset Password\",\r\n \"cardDescRequest\": \"Step 1 of 2: Request a reset code\",\r\n \"cardDescReset\": \"Step 2 of 2: Enter code and new password\",\r\n \"username\": \"Username\",\r\n \"usernamePlaceholder\": \"Enter your username\",\r\n \"code\": \"Reset Code\",\r\n \"codePlaceholder\": \"Enter 6-digit code\",\r\n \"codeFor\": \"Reset code for:\",\r\n \"newPassword\": \"New Password\",\r\n \"newPasswordPlaceholder\": \"Enter new password\",\r\n \"confirmPassword\": \"Confirm Password\",\r\n \"confirmPasswordPlaceholder\": \"Confirm new password\",\r\n \"passwordRequirements\": \"At least 8 characters, 1 letter and 1 number\",\r\n \"passwordMismatch\": \"Passwords do not match\",\r\n \"sendCode\": \"Send Reset Code\",\r\n \"sending\": \"Sending...\",\r\n \"resetPassword\": \"Reset Password\",\r\n \"resetting\": \"Resetting...\",\r\n \"backToLogin\": \"Back to Login\",\r\n \"changeUsername\": \"Change username\",\r\n \"resendCode\": \"Resend code\",\r\n \"codeSentTitle\": \"Code Sent!\",\r\n \"codeSentDesc\": \"A password reset code has been sent to your email.\",\r\n \"errorTitle\": \"Error\",\r\n \"errorGeneric\": \"Failed to send reset code. Please try again.\",\r\n \"errorResetGeneric\": \"Failed to reset password. Please try again.\",\r\n \"resetSuccessTitle\": \"Password Reset!\",\r\n \"resetSuccessDesc\": \"Your password has been successfully reset.\",\r\n \"successTitle\": \"Password Reset Successfully!\",\r\n \"successDescription\": \"Your password has been changed. You can now login with your new password.\",\r\n \"goToLogin\": \"Go to Login\"\r\n}\r\n"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "forgot-password-page/lang/tr.json",
|
|
36
36
|
"type": "registry:lang",
|
|
37
37
|
"target": "$modules$/forgot-password-page/lang/tr.json",
|
|
38
|
-
"content": "{\n \"title\": \"Şifremi Unuttum\",\n \"descriptionRequest\": \"Kullanıcı adınızı girin, size şifre sıfırlama kodu göndereceğiz.\",\n \"descriptionReset\": \"E-postanıza gönderilen kodu ve yeni şifrenizi girin.\",\n \"cardTitleRequest\": \"Sıfırlama Kodu İste\",\n \"cardTitleReset\": \"Şifre Sıfırla\",\n \"cardDescRequest\": \"Adım 1/2: Sıfırlama kodu isteyin\",\n \"cardDescReset\": \"Adım 2/2: Kodu ve yeni şifreyi girin\",\n \"username\": \"Kullanıcı Adı\",\n \"usernamePlaceholder\": \"Kullanıcı adınızı girin\",\n \"code\": \"Sıfırlama Kodu\",\n \"codePlaceholder\": \"6 haneli kodu girin\",\n \"codeFor\": \"Sıfırlama kodu:\",\n \"newPassword\": \"Yeni Şifre\",\n \"newPasswordPlaceholder\": \"Yeni şifre girin\",\n \"confirmPassword\": \"Şifre Onayı\",\n \"confirmPasswordPlaceholder\": \"Yeni şifreyi onaylayın\",\n \"passwordRequirements\": \"En az 8 karakter, 1 harf ve 1 rakam\",\n \"passwordMismatch\": \"Şifreler eşleşmiyor\",\n \"sendCode\": \"Sıfırlama Kodu Gönder\",\n \"sending\": \"Gönderiliyor...\",\n \"resetPassword\": \"Şifreyi Sıfırla\",\n \"resetting\": \"Sıfırlanıyor...\",\n \"backToLogin\": \"Girişe Dön\",\n \"changeUsername\": \"Kullanıcı adını değiştir\",\n \"resendCode\": \"Kodu tekrar gönder\",\n \"codeSentTitle\": \"Kod Gönderildi!\",\n \"codeSentDesc\": \"E-postanıza şifre sıfırlama kodu gönderildi.\",\n \"errorTitle\": \"Hata\",\n \"errorGeneric\": \"Sıfırlama kodu gönderilemedi. Lütfen tekrar deneyin.\",\n \"errorResetGeneric\": \"Şifre sıfırlanamadı. Lütfen tekrar deneyin.\",\n \"resetSuccessTitle\": \"Şifre Sıfırlandı!\",\n \"resetSuccessDesc\": \"Şifreniz başarıyla sıfırlandı.\",\n \"successTitle\": \"Şifre Başarıyla Sıfırlandı!\",\n \"successDescription\": \"Şifreniz değiştirildi. Artık yeni şifrenizle giriş yapabilirsiniz.\",\n \"goToLogin\": \"Girişe Git\"\n}\n"
|
|
38
|
+
"content": "{\r\n \"title\": \"Şifremi Unuttum\",\r\n \"descriptionRequest\": \"Kullanıcı adınızı girin, size şifre sıfırlama kodu göndereceğiz.\",\r\n \"descriptionReset\": \"E-postanıza gönderilen kodu ve yeni şifrenizi girin.\",\r\n \"cardTitleRequest\": \"Sıfırlama Kodu İste\",\r\n \"cardTitleReset\": \"Şifre Sıfırla\",\r\n \"cardDescRequest\": \"Adım 1/2: Sıfırlama kodu isteyin\",\r\n \"cardDescReset\": \"Adım 2/2: Kodu ve yeni şifreyi girin\",\r\n \"username\": \"Kullanıcı Adı\",\r\n \"usernamePlaceholder\": \"Kullanıcı adınızı girin\",\r\n \"code\": \"Sıfırlama Kodu\",\r\n \"codePlaceholder\": \"6 haneli kodu girin\",\r\n \"codeFor\": \"Sıfırlama kodu:\",\r\n \"newPassword\": \"Yeni Şifre\",\r\n \"newPasswordPlaceholder\": \"Yeni şifre girin\",\r\n \"confirmPassword\": \"Şifre Onayı\",\r\n \"confirmPasswordPlaceholder\": \"Yeni şifreyi onaylayın\",\r\n \"passwordRequirements\": \"En az 8 karakter, 1 harf ve 1 rakam\",\r\n \"passwordMismatch\": \"Şifreler eşleşmiyor\",\r\n \"sendCode\": \"Sıfırlama Kodu Gönder\",\r\n \"sending\": \"Gönderiliyor...\",\r\n \"resetPassword\": \"Şifreyi Sıfırla\",\r\n \"resetting\": \"Sıfırlanıyor...\",\r\n \"backToLogin\": \"Girişe Dön\",\r\n \"changeUsername\": \"Kullanıcı adını değiştir\",\r\n \"resendCode\": \"Kodu tekrar gönder\",\r\n \"codeSentTitle\": \"Kod Gönderildi!\",\r\n \"codeSentDesc\": \"E-postanıza şifre sıfırlama kodu gönderildi.\",\r\n \"errorTitle\": \"Hata\",\r\n \"errorGeneric\": \"Sıfırlama kodu gönderilemedi. Lütfen tekrar deneyin.\",\r\n \"errorResetGeneric\": \"Şifre sıfırlanamadı. Lütfen tekrar deneyin.\",\r\n \"resetSuccessTitle\": \"Şifre Sıfırlandı!\",\r\n \"resetSuccessDesc\": \"Şifreniz başarıyla sıfırlandı.\",\r\n \"successTitle\": \"Şifre Başarıyla Sıfırlandı!\",\r\n \"successDescription\": \"Şifreniz değiştirildi. Artık yeni şifrenizle giriş yapabilirsiniz.\",\r\n \"goToLogin\": \"Girişe Git\"\r\n}\r\n"
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
41
|
"exports": {
|
|
@@ -10,25 +10,25 @@
|
|
|
10
10
|
"path": "google-adsense/index.ts",
|
|
11
11
|
"type": "registry:index",
|
|
12
12
|
"target": "$modules$/google-adsense/index.ts",
|
|
13
|
-
"content": "export * from './google-adsense';\n"
|
|
13
|
+
"content": "export * from './google-adsense';\r\n"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "google-adsense/google-adsense.tsx",
|
|
17
17
|
"type": "registry:component",
|
|
18
18
|
"target": "$modules$/google-adsense/google-adsense.tsx",
|
|
19
|
-
"content": "import { useEffect, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { cn } from \"@/lib/utils\";\n\ndeclare global {\n interface Window {\n adsbygoogle: unknown[];\n }\n}\n\ninterface GoogleAdsenseProps {\n client?: string;\n slot: string;\n format?: \"auto\" | \"fluid\" | \"rectangle\" | \"vertical\" | \"horizontal\";\n responsive?: boolean;\n className?: string;\n style?: React.CSSProperties;\n testMode?: boolean;\n}\n\nconst ADSENSE_SCRIPT_ID = \"google-adsense-script\";\n\nexport function GoogleAdsense({\n client,\n slot,\n format = \"auto\",\n responsive = true,\n className,\n style,\n testMode = false,\n}: GoogleAdsenseProps) {\n const { t } = useTranslation(\"google-adsense\");\n const adRef = useRef<HTMLModElement>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n const [hasError, setHasError] = useState(false);\n\n const adClient = client || import.meta.env.VITE_ADSENSE_CLIENT;\n const isDev = import.meta.env.DEV;\n\n // Load AdSense script once\n useEffect(() => {\n if (isDev && !testMode) return;\n if (!adClient) {\n setHasError(true);\n return;\n }\n\n const existingScript = document.getElementById(ADSENSE_SCRIPT_ID);\n if (existingScript) {\n setIsLoaded(true);\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = ADSENSE_SCRIPT_ID;\n script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adClient}`;\n script.async = true;\n script.crossOrigin = \"anonymous\";\n\n script.onload = () => setIsLoaded(true);\n script.onerror = () => setHasError(true);\n\n document.head.appendChild(script);\n }, [adClient, isDev, testMode]);\n\n // Push ad when script is loaded\n useEffect(() => {\n if (!isLoaded || !adRef.current || (isDev && !testMode)) return;\n\n const adElement = adRef.current;\n if (adElement.dataset.adStatus) return; // Already initialized\n\n try {\n (window.adsbygoogle = window.adsbygoogle || []).push({});\n } catch (err) {\n console.error(\"AdSense error:\", err);\n setHasError(true);\n }\n }, [isLoaded, isDev, testMode]);\n\n // Development placeholder\n if (isDev && !testMode) {\n return (\n <div\n className={cn(\n \"flex items-center justify-center bg-muted/50 border-2 border-dashed border-muted-foreground/25 rounded-lg\",\n className\n )}\n style={{ minHeight: \"90px\", ...style }}\n >\n <div className=\"text-center p-4\">\n <p className=\"text-sm font-medium text-muted-foreground\">\n {t(\"devPlaceholder\", \"AdSense Placeholder\")}\n </p>\n <p className=\"text-xs text-muted-foreground/70 mt-1\">\n {t(\"devNote\", \"Ads will appear in production\")}\n </p>\n </div>\n </div>\n );\n }\n\n // Error state\n if (hasError || !adClient) {\n return (\n <div\n className={cn(\n \"flex items-center justify-center bg-muted/30 rounded-lg\",\n className\n )}\n style={{ minHeight: \"90px\", ...style }}\n >\n <p className=\"text-xs text-muted-foreground\">\n {t(\"error\", \"Ad could not be loaded\")}\n </p>\n </div>\n );\n }\n\n return (\n <ins\n ref={adRef}\n className={cn(\"adsbygoogle block\", className)}\n style={{ display: \"block\", minHeight: \"90px\", ...style }}\n data-ad-client={adClient}\n data-ad-slot={slot}\n data-ad-format={format}\n data-full-width-responsive={responsive ? \"true\" : \"false\"}\n {...(testMode && { \"data-adtest\": \"on\" })}\n />\n );\n}\n"
|
|
19
|
+
"content": "import { useEffect, useRef, useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ndeclare global {\r\n interface Window {\r\n adsbygoogle: unknown[];\r\n }\r\n}\r\n\r\ninterface GoogleAdsenseProps {\r\n client?: string;\r\n slot: string;\r\n format?: \"auto\" | \"fluid\" | \"rectangle\" | \"vertical\" | \"horizontal\";\r\n responsive?: boolean;\r\n className?: string;\r\n style?: React.CSSProperties;\r\n testMode?: boolean;\r\n}\r\n\r\nconst ADSENSE_SCRIPT_ID = \"google-adsense-script\";\r\n\r\nexport function GoogleAdsense({\r\n client,\r\n slot,\r\n format = \"auto\",\r\n responsive = true,\r\n className,\r\n style,\r\n testMode = false,\r\n}: GoogleAdsenseProps) {\r\n const { t } = useTranslation(\"google-adsense\");\r\n const adRef = useRef<HTMLModElement>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n const [hasError, setHasError] = useState(false);\r\n\r\n const adClient = client || import.meta.env.VITE_ADSENSE_CLIENT;\r\n const isDev = import.meta.env.DEV;\r\n\r\n // Load AdSense script once\r\n useEffect(() => {\r\n if (isDev && !testMode) return;\r\n if (!adClient) {\r\n setHasError(true);\r\n return;\r\n }\r\n\r\n const existingScript = document.getElementById(ADSENSE_SCRIPT_ID);\r\n if (existingScript) {\r\n setIsLoaded(true);\r\n return;\r\n }\r\n\r\n const script = document.createElement(\"script\");\r\n script.id = ADSENSE_SCRIPT_ID;\r\n script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adClient}`;\r\n script.async = true;\r\n script.crossOrigin = \"anonymous\";\r\n\r\n script.onload = () => setIsLoaded(true);\r\n script.onerror = () => setHasError(true);\r\n\r\n document.head.appendChild(script);\r\n }, [adClient, isDev, testMode]);\r\n\r\n // Push ad when script is loaded\r\n useEffect(() => {\r\n if (!isLoaded || !adRef.current || (isDev && !testMode)) return;\r\n\r\n const adElement = adRef.current;\r\n if (adElement.dataset.adStatus) return; // Already initialized\r\n\r\n try {\r\n (window.adsbygoogle = window.adsbygoogle || []).push({});\r\n } catch (err) {\r\n console.error(\"AdSense error:\", err);\r\n setHasError(true);\r\n }\r\n }, [isLoaded, isDev, testMode]);\r\n\r\n // Development placeholder\r\n if (isDev && !testMode) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex items-center justify-center bg-muted/50 border-2 border-dashed border-muted-foreground/25 rounded-lg\",\r\n className\r\n )}\r\n style={{ minHeight: \"90px\", ...style }}\r\n >\r\n <div className=\"text-center p-4\">\r\n <p className=\"text-sm font-medium text-muted-foreground\">\r\n {t(\"devPlaceholder\", \"AdSense Placeholder\")}\r\n </p>\r\n <p className=\"text-xs text-muted-foreground/70 mt-1\">\r\n {t(\"devNote\", \"Ads will appear in production\")}\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n // Error state\r\n if (hasError || !adClient) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex items-center justify-center bg-muted/30 rounded-lg\",\r\n className\r\n )}\r\n style={{ minHeight: \"90px\", ...style }}\r\n >\r\n <p className=\"text-xs text-muted-foreground\">\r\n {t(\"error\", \"Ad could not be loaded\")}\r\n </p>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <ins\r\n ref={adRef}\r\n className={cn(\"adsbygoogle block\", className)}\r\n style={{ display: \"block\", minHeight: \"90px\", ...style }}\r\n data-ad-client={adClient}\r\n data-ad-slot={slot}\r\n data-ad-format={format}\r\n data-full-width-responsive={responsive ? \"true\" : \"false\"}\r\n {...(testMode && { \"data-adtest\": \"on\" })}\r\n />\r\n );\r\n}\r\n"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "google-adsense/lang/en.json",
|
|
23
23
|
"type": "registry:lang",
|
|
24
24
|
"target": "$modules$/google-adsense/lang/en.json",
|
|
25
|
-
"content": "{\n \"devPlaceholder\": \"AdSense Placeholder\",\n \"devNote\": \"Ads will appear in production\",\n \"error\": \"Ad could not be loaded\"\n}\n"
|
|
25
|
+
"content": "{\r\n \"devPlaceholder\": \"AdSense Placeholder\",\r\n \"devNote\": \"Ads will appear in production\",\r\n \"error\": \"Ad could not be loaded\"\r\n}\r\n"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "google-adsense/lang/tr.json",
|
|
29
29
|
"type": "registry:lang",
|
|
30
30
|
"target": "$modules$/google-adsense/lang/tr.json",
|
|
31
|
-
"content": "{\n \"devPlaceholder\": \"AdSense Yer Tutucusu\",\n \"devNote\": \"Reklamlar production'da görünecek\",\n \"error\": \"Reklam yüklenemedi\"\n}\n"
|
|
31
|
+
"content": "{\r\n \"devPlaceholder\": \"AdSense Yer Tutucusu\",\r\n \"devNote\": \"Reklamlar production'da görünecek\",\r\n \"error\": \"Reklam yüklenemedi\"\r\n}\r\n"
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"envVars": {
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"path": "google-map/index.ts",
|
|
11
11
|
"type": "registry:index",
|
|
12
12
|
"target": "$modules$/google-map/index.ts",
|
|
13
|
-
"content": "export * from './google-map';\n"
|
|
13
|
+
"content": "export * from './google-map';\r\n"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "google-map/google-map.tsx",
|
|
17
17
|
"type": "registry:component",
|
|
18
18
|
"target": "$modules$/google-map/google-map.tsx",
|
|
19
|
-
"content": "import { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { MapPin } from \"lucide-react\";\n\ninterface GoogleMapProps {\n latitude: number;\n longitude: number;\n zoom?: number;\n apiKey?: string;\n height?: string;\n className?: string;\n title?: string;\n}\n\nexport function GoogleMap({\n latitude,\n longitude,\n zoom = 15,\n apiKey,\n height = \"400px\",\n className,\n title,\n}: GoogleMapProps) {\n const [isLoading, setIsLoading] = useState(true);\n const [hasError, setHasError] = useState(false);\n\n // Build the embed URL\n const buildMapUrl = () => {\n const baseUrl = apiKey\n ? \"https://www.google.com/maps/embed/v1/place\"\n : \"https://maps.google.com/maps\";\n\n if (apiKey) {\n // With API key - use official Embed API\n const params = new URLSearchParams({\n key: apiKey,\n q: `${latitude},${longitude}`,\n zoom: zoom.toString(),\n });\n return `${baseUrl}?${params.toString()}`;\n } else {\n // Without API key - use basic embed\n const params = new URLSearchParams({\n q: `${latitude},${longitude}`,\n z: zoom.toString(),\n output: \"embed\",\n });\n return `${baseUrl}?${params.toString()}`;\n }\n };\n\n const handleLoad = () => {\n setIsLoading(false);\n };\n\n const handleError = () => {\n setIsLoading(false);\n setHasError(true);\n };\n\n if (hasError) {\n return (\n <div\n className={cn(\n \"flex flex-col items-center justify-center bg-muted rounded-lg border\",\n className\n )}\n style={{ height }}\n >\n <MapPin className=\"h-12 w-12 text-muted-foreground mb-3\" />\n <p className=\"text-sm text-muted-foreground\">Failed to load map</p>\n <a\n href={`https://www.google.com/maps?q=${latitude},${longitude}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-sm text-primary hover:underline mt-2\"\n >\n Open in Google Maps\n </a>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\"relative rounded-lg overflow-hidden border\", className)}\n style={{ height }}\n >\n {isLoading && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted\">\n <div className=\"flex flex-col items-center gap-3\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n <span className=\"text-sm text-muted-foreground\">Loading map...</span>\n </div>\n </div>\n )}\n <iframe\n src={buildMapUrl()}\n width=\"100%\"\n height=\"100%\"\n style={{ border: 0 }}\n allowFullScreen\n loading=\"lazy\"\n referrerPolicy=\"no-referrer-when-downgrade\"\n title={title || `Map location: ${latitude}, ${longitude}`}\n onLoad={handleLoad}\n onError={handleError}\n className={cn(isLoading && \"invisible\")}\n />\n </div>\n );\n}\n"
|
|
19
|
+
"content": "import { useState } from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { MapPin } from \"lucide-react\";\r\n\r\ninterface GoogleMapProps {\r\n latitude: number;\r\n longitude: number;\r\n zoom?: number;\r\n apiKey?: string;\r\n height?: string;\r\n className?: string;\r\n title?: string;\r\n}\r\n\r\nexport function GoogleMap({\r\n latitude,\r\n longitude,\r\n zoom = 15,\r\n apiKey,\r\n height = \"400px\",\r\n className,\r\n title,\r\n}: GoogleMapProps) {\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [hasError, setHasError] = useState(false);\r\n\r\n // Build the embed URL\r\n const buildMapUrl = () => {\r\n const baseUrl = apiKey\r\n ? \"https://www.google.com/maps/embed/v1/place\"\r\n : \"https://maps.google.com/maps\";\r\n\r\n if (apiKey) {\r\n // With API key - use official Embed API\r\n const params = new URLSearchParams({\r\n key: apiKey,\r\n q: `${latitude},${longitude}`,\r\n zoom: zoom.toString(),\r\n });\r\n return `${baseUrl}?${params.toString()}`;\r\n } else {\r\n // Without API key - use basic embed\r\n const params = new URLSearchParams({\r\n q: `${latitude},${longitude}`,\r\n z: zoom.toString(),\r\n output: \"embed\",\r\n });\r\n return `${baseUrl}?${params.toString()}`;\r\n }\r\n };\r\n\r\n const handleLoad = () => {\r\n setIsLoading(false);\r\n };\r\n\r\n const handleError = () => {\r\n setIsLoading(false);\r\n setHasError(true);\r\n };\r\n\r\n if (hasError) {\r\n return (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center justify-center bg-muted rounded-lg border\",\r\n className\r\n )}\r\n style={{ height }}\r\n >\r\n <MapPin className=\"h-12 w-12 text-muted-foreground mb-3\" />\r\n <p className=\"text-sm text-muted-foreground\">Failed to load map</p>\r\n <a\r\n href={`https://www.google.com/maps?q=${latitude},${longitude}`}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-sm text-primary hover:underline mt-2\"\r\n >\r\n Open in Google Maps\r\n </a>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div\r\n className={cn(\"relative rounded-lg overflow-hidden border\", className)}\r\n style={{ height }}\r\n >\r\n {isLoading && (\r\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted\">\r\n <div className=\"flex flex-col items-center gap-3\">\r\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\r\n <span className=\"text-sm text-muted-foreground\">Loading map...</span>\r\n </div>\r\n </div>\r\n )}\r\n <iframe\r\n src={buildMapUrl()}\r\n width=\"100%\"\r\n height=\"100%\"\r\n style={{ border: 0 }}\r\n allowFullScreen\r\n loading=\"lazy\"\r\n referrerPolicy=\"no-referrer-when-downgrade\"\r\n title={title || `Map location: ${latitude}, ${longitude}`}\r\n onLoad={handleLoad}\r\n onError={handleError}\r\n className={cn(isLoading && \"invisible\")}\r\n />\r\n </div>\r\n );\r\n}\r\n"
|
|
20
20
|
}
|
|
21
21
|
],
|
|
22
22
|
"envVars": {
|
|
@@ -15,25 +15,25 @@
|
|
|
15
15
|
"path": "header-centered-pill/index.ts",
|
|
16
16
|
"type": "registry:index",
|
|
17
17
|
"target": "$modules$/header-centered-pill/index.ts",
|
|
18
|
-
"content": "export * from './header-centered-pill';\n"
|
|
18
|
+
"content": "export * from './header-centered-pill';\r\n"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "header-centered-pill/header-centered-pill.tsx",
|
|
22
22
|
"type": "registry:component",
|
|
23
23
|
"target": "$modules$/header-centered-pill/header-centered-pill.tsx",
|
|
24
|
-
"content": "import { useState } from \"react\";\nimport { Link } from \"react-router\";\nimport { Menu } from \"lucide-react\";\nimport { Button, buttonVariants } from \"@/components/ui/button\";\nimport {\n Sheet,\n SheetHeader,\n SheetTitle,\n SheetContent,\n SheetTrigger,\n} from \"@/components/ui/sheet\";\nimport { Logo } from \"@/components/Logo\";\nimport { cn } from \"@/lib/utils\";\nimport { useTranslation } from \"react-i18next\";\n\nexport function HeaderCenteredPill() {\n const [mobileMenuOpen, setMobileMenuOpen] = useState(false);\n const { t } = useTranslation(\"header-centered-pill\");\n\n const navigation = [\n { name: t(\"features\", \"Features\"), href: \"/features\" },\n { name: t(\"pricing\", \"Pricing\"), href: \"/pricing\" },\n { name: t(\"blog\", \"Blog\"), href: \"/blog\" },\n { name: t(\"company\", \"Company\"), href: \"/about\" },\n { name: t(\"signIn\", \"Sign in\"), href: \"/login\" },\n ];\n\n return (\n <header className=\"p-4\">\n <div className=\"mx-auto flex max-w-2xl items-center justify-between space-x-4 rounded-full border bg-background p-1.5 pl-4\">\n <Link to=\"/\">\n <Logo size=\"sm\" />\n </Link>\n\n {/* Desktop Navigation */}\n <div className=\"hidden md:inline-flex\">\n {navigation.map((item) => (\n <Link\n key={item.name}\n to={item.href}\n className={buttonVariants({ variant: \"ghost\", size: \"sm\" })}\n >\n {item.name}\n </Link>\n ))}\n </div>\n\n {/* Desktop CTA */}\n <div className=\"hidden md:inline-flex\">\n <Link\n to=\"/register\"\n className={cn(buttonVariants({ size: \"sm\" }), \"rounded-full\")}\n >\n {t(\"getStarted\", \"Get Started\")}\n </Link>\n </div>\n\n {/* Mobile Menu */}\n <div className=\"md:hidden\">\n <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>\n <SheetTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className=\"rounded-full\">\n <Menu className=\"h-5 w-5\" />\n <span className=\"sr-only\">{t(\"menu\", \"Menu\")}</span>\n </Button>\n </SheetTrigger>\n <SheetContent side=\"right\" className=\"w-[300px] px-6\">\n <SheetHeader>\n <SheetTitle>{t(\"menu\", \"Menu\")}</SheetTitle>\n </SheetHeader>\n <div className=\"flex flex-col space-y-4 mt-8\">\n {navigation.map((item) => (\n <Link\n key={item.name}\n to={item.href}\n className=\"text-lg font-medium hover:text-primary transition-colors\"\n onClick={() => setMobileMenuOpen(false)}\n >\n {item.name}\n </Link>\n ))}\n <Link\n to=\"/register\"\n className={cn(buttonVariants(), \"rounded-full mt-4\")}\n onClick={() => setMobileMenuOpen(false)}\n >\n {t(\"getStarted\", \"Get Started\")}\n </Link>\n </div>\n </SheetContent>\n </Sheet>\n </div>\n </div>\n </header>\n );\n}\n"
|
|
24
|
+
"content": "import { useState } from \"react\";\r\nimport { Link } from \"react-router\";\r\nimport { Menu } from \"lucide-react\";\r\nimport { Button, buttonVariants } from \"@/components/ui/button\";\r\nimport {\r\n Sheet,\r\n SheetHeader,\r\n SheetTitle,\r\n SheetContent,\r\n SheetTrigger,\r\n} from \"@/components/ui/sheet\";\r\nimport { Logo } from \"@/components/Logo\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { useTranslation } from \"react-i18next\";\r\n\r\nexport function HeaderCenteredPill() {\r\n const [mobileMenuOpen, setMobileMenuOpen] = useState(false);\r\n const { t } = useTranslation(\"header-centered-pill\");\r\n\r\n const navigation = [\r\n { name: t(\"features\", \"Features\"), href: \"/features\" },\r\n { name: t(\"pricing\", \"Pricing\"), href: \"/pricing\" },\r\n { name: t(\"blog\", \"Blog\"), href: \"/blog\" },\r\n { name: t(\"company\", \"Company\"), href: \"/about\" },\r\n { name: t(\"signIn\", \"Sign in\"), href: \"/login\" },\r\n ];\r\n\r\n return (\r\n <header className=\"p-4\">\r\n <div className=\"mx-auto flex max-w-2xl items-center justify-between space-x-4 rounded-full border bg-background p-1.5 pl-4\">\r\n <Link to=\"/\">\r\n <Logo size=\"sm\" />\r\n </Link>\r\n\r\n {/* Desktop Navigation */}\r\n <div className=\"hidden md:inline-flex\">\r\n {navigation.map((item) => (\r\n <Link\r\n key={item.name}\r\n to={item.href}\r\n className={buttonVariants({ variant: \"ghost\", size: \"sm\" })}\r\n >\r\n {item.name}\r\n </Link>\r\n ))}\r\n </div>\r\n\r\n {/* Desktop CTA */}\r\n <div className=\"hidden md:inline-flex\">\r\n <Link\r\n to=\"/register\"\r\n className={cn(buttonVariants({ size: \"sm\" }), \"rounded-full\")}\r\n >\r\n {t(\"getStarted\", \"Get Started\")}\r\n </Link>\r\n </div>\r\n\r\n {/* Mobile Menu */}\r\n <div className=\"md:hidden\">\r\n <Sheet open={mobileMenuOpen} onOpenChange={setMobileMenuOpen}>\r\n <SheetTrigger asChild>\r\n <Button variant=\"ghost\" size=\"icon\" className=\"rounded-full\">\r\n <Menu className=\"h-5 w-5\" />\r\n <span className=\"sr-only\">{t(\"menu\", \"Menu\")}</span>\r\n </Button>\r\n </SheetTrigger>\r\n <SheetContent side=\"right\" className=\"w-[300px] px-6\">\r\n <SheetHeader>\r\n <SheetTitle>{t(\"menu\", \"Menu\")}</SheetTitle>\r\n </SheetHeader>\r\n <div className=\"flex flex-col space-y-4 mt-8\">\r\n {navigation.map((item) => (\r\n <Link\r\n key={item.name}\r\n to={item.href}\r\n className=\"text-lg font-medium hover:text-primary transition-colors\"\r\n onClick={() => setMobileMenuOpen(false)}\r\n >\r\n {item.name}\r\n </Link>\r\n ))}\r\n <Link\r\n to=\"/register\"\r\n className={cn(buttonVariants(), \"rounded-full mt-4\")}\r\n onClick={() => setMobileMenuOpen(false)}\r\n >\r\n {t(\"getStarted\", \"Get Started\")}\r\n </Link>\r\n </div>\r\n </SheetContent>\r\n </Sheet>\r\n </div>\r\n </div>\r\n </header>\r\n );\r\n}\r\n"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": "header-centered-pill/lang/en.json",
|
|
28
28
|
"type": "registry:lang",
|
|
29
29
|
"target": "$modules$/header-centered-pill/lang/en.json",
|
|
30
|
-
"content": "{\n \"menu\": \"Menu\",\n \"features\": \"Features\",\n \"pricing\": \"Pricing\",\n \"blog\": \"Blog\",\n \"company\": \"Company\",\n \"signIn\": \"Sign in\",\n \"getStarted\": \"Get Started\"\n}\n"
|
|
30
|
+
"content": "{\r\n \"menu\": \"Menu\",\r\n \"features\": \"Features\",\r\n \"pricing\": \"Pricing\",\r\n \"blog\": \"Blog\",\r\n \"company\": \"Company\",\r\n \"signIn\": \"Sign in\",\r\n \"getStarted\": \"Get Started\"\r\n}\r\n"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"path": "header-centered-pill/lang/tr.json",
|
|
34
34
|
"type": "registry:lang",
|
|
35
35
|
"target": "$modules$/header-centered-pill/lang/tr.json",
|
|
36
|
-
"content": "{\n \"menu\": \"Menü\",\n \"features\": \"Özellikler\",\n \"pricing\": \"Fiyatlandırma\",\n \"blog\": \"Blog\",\n \"company\": \"Şirket\",\n \"signIn\": \"Giriş Yap\",\n \"getStarted\": \"Başla\"\n}\n"
|
|
36
|
+
"content": "{\r\n \"menu\": \"Menü\",\r\n \"features\": \"Özellikler\",\r\n \"pricing\": \"Fiyatlandırma\",\r\n \"blog\": \"Blog\",\r\n \"company\": \"Şirket\",\r\n \"signIn\": \"Giriş Yap\",\r\n \"getStarted\": \"Başla\"\r\n}\r\n"
|
|
37
37
|
}
|
|
38
38
|
],
|
|
39
39
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"path": "header-ecommerce/index.ts",
|
|
15
15
|
"type": "registry:index",
|
|
16
16
|
"target": "$modules$/header-ecommerce/index.ts",
|
|
17
|
-
"content": "export * from './header-ecommerce';\n"
|
|
17
|
+
"content": "export * from './header-ecommerce';\r\n"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"path": "header-ecommerce/header-ecommerce.tsx",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"path": "header-ecommerce/lang/en.json",
|
|
27
27
|
"type": "registry:lang",
|
|
28
28
|
"target": "$modules$/header-ecommerce/lang/en.json",
|
|
29
|
-
"content": "{\n \"menu\": \"Menu\",\n \"home\": \"Home\",\n \"products\": \"Products\",\n \"about\": \"About\",\n \"contact\": \"Contact\",\n \"cart\": \"Cart\",\n \"favorites\": \"Favorites\",\n \"searchProducts\": \"Search Products\",\n \"searchPlaceholder\": \"Search for products...\",\n \"searchButton\": \"Search\",\n \"result\": \"result\",\n \"results\": \"results\",\n \"noResults\": \"No products found\",\n \"tryDifferent\": \"Try different keywords\",\n \"viewAllResults\": \"View all results\"\n}\n"
|
|
29
|
+
"content": "{\r\n \"menu\": \"Menu\",\r\n \"home\": \"Home\",\r\n \"products\": \"Products\",\r\n \"about\": \"About\",\r\n \"contact\": \"Contact\",\r\n \"cart\": \"Cart\",\r\n \"favorites\": \"Favorites\",\r\n \"searchProducts\": \"Search Products\",\r\n \"searchPlaceholder\": \"Search for products...\",\r\n \"searchButton\": \"Search\",\r\n \"result\": \"result\",\r\n \"results\": \"results\",\r\n \"noResults\": \"No products found\",\r\n \"tryDifferent\": \"Try different keywords\",\r\n \"viewAllResults\": \"View all results\"\r\n}\r\n"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"path": "header-ecommerce/lang/tr.json",
|
|
33
33
|
"type": "registry:lang",
|
|
34
34
|
"target": "$modules$/header-ecommerce/lang/tr.json",
|
|
35
|
-
"content": "{\n \"menu\": \"Menü\",\n \"home\": \"Ana Sayfa\",\n \"products\": \"Ürünler\",\n \"about\": \"Hakkımızda\",\n \"contact\": \"İletişim\",\n \"cart\": \"Sepet\",\n \"favorites\": \"Favoriler\",\n \"searchProducts\": \"Ürün Ara\",\n \"searchPlaceholder\": \"Ürün ara...\",\n \"searchButton\": \"Ara\",\n \"result\": \"sonuç\",\n \"results\": \"sonuç\",\n \"noResults\": \"Ürün bulunamadı\",\n \"tryDifferent\": \"Farklı anahtar kelimeler deneyin\",\n \"viewAllResults\": \"Tüm sonuçları gör\"\n}\n"
|
|
35
|
+
"content": "{\r\n \"menu\": \"Menü\",\r\n \"home\": \"Ana Sayfa\",\r\n \"products\": \"Ürünler\",\r\n \"about\": \"Hakkımızda\",\r\n \"contact\": \"İletişim\",\r\n \"cart\": \"Sepet\",\r\n \"favorites\": \"Favoriler\",\r\n \"searchProducts\": \"Ürün Ara\",\r\n \"searchPlaceholder\": \"Ürün ara...\",\r\n \"searchButton\": \"Ara\",\r\n \"result\": \"sonuç\",\r\n \"results\": \"sonuç\",\r\n \"noResults\": \"Ürün bulunamadı\",\r\n \"tryDifferent\": \"Farklı anahtar kelimeler deneyin\",\r\n \"viewAllResults\": \"Tüm sonuçları gör\"\r\n}\r\n"
|
|
36
36
|
}
|
|
37
37
|
],
|
|
38
38
|
"exports": {
|