@promakeai/cli 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/index.js +214 -135
  2. package/dist/registry/about-page.json +5 -3
  3. package/dist/registry/about-section.json +2 -2
  4. package/dist/registry/announcement-bar.json +43 -0
  5. package/dist/registry/api.json +55 -0
  6. package/dist/registry/auth-core.json +43 -0
  7. package/dist/registry/auth.json +70 -0
  8. package/dist/registry/bento-grid-section.json +1 -1
  9. package/dist/registry/blog-list-page.json +3 -2
  10. package/dist/registry/blog-section.json +2 -2
  11. package/dist/registry/cart-drawer.json +1 -1
  12. package/dist/registry/cart-page.json +5 -4
  13. package/dist/registry/case-study-page.json +48 -0
  14. package/dist/registry/category-section.json +1 -1
  15. package/dist/registry/checkout-page.json +7 -5
  16. package/dist/registry/coming-soon-page-minimal.json +45 -0
  17. package/dist/registry/coming-soon-page.json +47 -0
  18. package/dist/registry/contact-info-grid.json +2 -2
  19. package/dist/registry/contact-page-centered.json +2 -2
  20. package/dist/registry/contact-page-map-overlay.json +4 -3
  21. package/dist/registry/contact-page-map-split.json +4 -3
  22. package/dist/registry/contact-page-split.json +3 -3
  23. package/dist/registry/contact-page.json +5 -3
  24. package/dist/registry/cookie-consent.json +43 -0
  25. package/dist/registry/cookies-page.json +4 -2
  26. package/dist/registry/cta-section.json +2 -2
  27. package/dist/registry/db.json +129 -0
  28. package/dist/registry/docs/about-page.md +5 -0
  29. package/dist/registry/docs/announcement-bar.md +38 -0
  30. package/dist/registry/docs/auth-core.md +64 -0
  31. package/dist/registry/docs/blog-list-page.md +1 -0
  32. package/dist/registry/docs/cart-page.md +1 -0
  33. package/dist/registry/docs/case-study-page.md +39 -0
  34. package/dist/registry/docs/checkout-page.md +3 -1
  35. package/dist/registry/docs/coming-soon-page-minimal.md +32 -0
  36. package/dist/registry/docs/coming-soon-page.md +37 -0
  37. package/dist/registry/docs/contact-page-map-overlay.md +2 -2
  38. package/dist/registry/docs/contact-page-map-split.md +2 -2
  39. package/dist/registry/docs/contact-page.md +5 -0
  40. package/dist/registry/docs/cookie-consent.md +37 -0
  41. package/dist/registry/docs/cookies-page.md +5 -0
  42. package/dist/registry/docs/ecommerce-core.md +4 -3
  43. package/dist/registry/docs/forgot-password-page-split.md +45 -0
  44. package/dist/registry/docs/forgot-password-page.md +46 -0
  45. package/dist/registry/docs/header-ecommerce.md +2 -0
  46. package/dist/registry/docs/hero-carousel.md +37 -0
  47. package/dist/registry/docs/landing-page-app.md +43 -0
  48. package/dist/registry/docs/landing-page-saas.md +41 -0
  49. package/dist/registry/docs/login-page-split.md +13 -4
  50. package/dist/registry/docs/login-page.md +17 -4
  51. package/dist/registry/docs/logo-cloud.md +33 -0
  52. package/dist/registry/docs/masonry-grid.md +37 -0
  53. package/dist/registry/docs/my-orders-page.md +44 -0
  54. package/dist/registry/docs/order-confirmation-page.md +41 -0
  55. package/dist/registry/docs/portfolio-page.md +38 -0
  56. package/dist/registry/docs/pricing-page.md +38 -0
  57. package/dist/registry/docs/privacy-page.md +5 -0
  58. package/dist/registry/docs/product-quick-view.md +37 -0
  59. package/dist/registry/docs/products-page.md +1 -0
  60. package/dist/registry/docs/reading-progress.md +31 -0
  61. package/dist/registry/docs/register-page-split.md +45 -0
  62. package/dist/registry/docs/register-page.md +46 -0
  63. package/dist/registry/docs/reset-password-page-split.md +45 -0
  64. package/dist/registry/docs/reset-password-page.md +36 -0
  65. package/dist/registry/docs/share-buttons.md +37 -0
  66. package/dist/registry/docs/team-page.md +38 -0
  67. package/dist/registry/docs/terms-page.md +5 -0
  68. package/dist/registry/docs/timeline-section.md +37 -0
  69. package/dist/registry/docs/video-hero.md +41 -0
  70. package/dist/registry/ecommerce-core.json +18 -2
  71. package/dist/registry/empty-page.json +1 -1
  72. package/dist/registry/faq-categorized.json +2 -2
  73. package/dist/registry/faq-simple.json +2 -2
  74. package/dist/registry/favorites-blog-block.json +1 -1
  75. package/dist/registry/favorites-ecommerce-block.json +1 -1
  76. package/dist/registry/feature-section.json +2 -2
  77. package/dist/registry/featured-products.json +1 -1
  78. package/dist/registry/footer-detailed.json +1 -1
  79. package/dist/registry/footer-minimal.json +3 -3
  80. package/dist/registry/footer.json +2 -2
  81. package/dist/registry/forgot-password-page-split.json +50 -0
  82. package/dist/registry/forgot-password-page.json +51 -0
  83. package/dist/registry/header-ecommerce.json +4 -2
  84. package/dist/registry/header-mega.json +2 -2
  85. package/dist/registry/header-minimal.json +1 -1
  86. package/dist/registry/header-simple.json +1 -1
  87. package/dist/registry/hero-carousel.json +45 -0
  88. package/dist/registry/hero-cta.json +2 -2
  89. package/dist/registry/hero-gradient.json +2 -2
  90. package/dist/registry/hero-profile.json +1 -1
  91. package/dist/registry/hero.json +2 -2
  92. package/dist/registry/index.json +24 -1
  93. package/dist/registry/landing-page-app.json +47 -0
  94. package/dist/registry/landing-page-saas.json +47 -0
  95. package/dist/registry/login-page-split.json +11 -7
  96. package/dist/registry/login-page.json +4 -4
  97. package/dist/registry/logo-cloud.json +41 -0
  98. package/dist/registry/masonry-grid.json +43 -0
  99. package/dist/registry/my-orders-page.json +52 -0
  100. package/dist/registry/order-confirmation-page.json +49 -0
  101. package/dist/registry/orders-list-block.json +1 -1
  102. package/dist/registry/payment-success-block.json +1 -1
  103. package/dist/registry/portfolio-page.json +45 -0
  104. package/dist/registry/post-detail-block.json +1 -1
  105. package/dist/registry/pricing-page.json +47 -0
  106. package/dist/registry/pricing-section.json +2 -2
  107. package/dist/registry/privacy-page.json +4 -2
  108. package/dist/registry/product-detail-block.json +1 -1
  109. package/dist/registry/product-quick-view.json +46 -0
  110. package/dist/registry/products-page.json +5 -4
  111. package/dist/registry/reading-progress.json +43 -0
  112. package/dist/registry/register-page-split.json +50 -0
  113. package/dist/registry/register-page.json +51 -0
  114. package/dist/registry/related-posts-block.json +1 -1
  115. package/dist/registry/reset-password-page-split.json +50 -0
  116. package/dist/registry/reset-password-page.json +39 -0
  117. package/dist/registry/share-buttons.json +46 -0
  118. package/dist/registry/team-page.json +47 -0
  119. package/dist/registry/terms-page.json +4 -2
  120. package/dist/registry/testimonials-carousel.json +2 -2
  121. package/dist/registry/testimonials-grid.json +2 -2
  122. package/dist/registry/timeline-section.json +43 -0
  123. package/dist/registry/video-hero.json +42 -0
  124. package/package.json +1 -1
  125. package/template/index.html +5 -5
  126. package/template/src/App.tsx +7 -24
  127. package/template/src/components/GoogleAnalytics.tsx +34 -0
  128. package/template/src/components/Layout.tsx +1 -5
  129. package/template/src/components/ScriptInjector.tsx +62 -0
  130. package/template/src/constants/constants.json +8 -2
  131. package/template/src/index.css +1 -0
  132. package/template/src/lang/en/index.json +1 -28
  133. package/template/src/lang/tr/index.json +1 -28
  134. package/template/src/pages/Index.tsx +1 -98
  135. package/template/src/components/Footer.tsx +0 -100
  136. package/template/src/components/Header.tsx +0 -79
  137. package/template/src/components/Hero.tsx +0 -69
  138. package/template/src/modules/api/USAGE.md +0 -515
  139. package/template/src/modules/api/customer-client.ts +0 -20
  140. package/template/src/modules/api/get-error-message.ts +0 -18
  141. package/template/src/modules/api/validation/en.json +0 -29
  142. package/template/src/modules/api/validation/tr.json +0 -29
  143. package/template/src/modules/auth/USAGE.md +0 -248
  144. package/template/src/modules/auth/auth-header-menu.tsx +0 -123
  145. package/template/src/modules/auth/auth-store.ts +0 -57
  146. package/template/src/modules/auth/forgot-password-page.tsx +0 -371
  147. package/template/src/modules/auth/login-page.tsx +0 -183
  148. package/template/src/modules/auth/register-page.tsx +0 -252
  149. package/template/src/modules/auth/use-auth.ts +0 -273
  150. package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
  151. package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
  152. package/template/src/modules/db/adapters/index.ts +0 -2
  153. package/template/src/modules/db/config.ts +0 -59
  154. package/template/src/modules/db/core/DataManager.ts +0 -125
  155. package/template/src/modules/db/core/types.ts +0 -101
  156. package/template/src/modules/db/index.ts +0 -42
  157. package/template/src/modules/db/react/QueryProvider.tsx +0 -16
  158. package/template/src/modules/db/react/index.ts +0 -23
  159. package/template/src/modules/db/react/queryClient.ts +0 -64
  160. package/template/src/modules/db/react/useRepository.ts +0 -400
  161. package/template/src/modules/db/utils/parsers.ts +0 -96
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "coming-soon-page",
3
+ "type": "registry:page",
4
+ "title": "Coming Soon Page",
5
+ "description": "Full-featured coming soon page with countdown timer, email signup form, social links, and gradient background. Includes FadeIn and ScaleUp animations. Perfect for product launches and pre-release marketing.",
6
+ "registryDependencies": [
7
+ "animations"
8
+ ],
9
+ "route": {
10
+ "path": "/coming-soon",
11
+ "componentName": "ComingSoonPage"
12
+ },
13
+ "usage": "import { ComingSoonPage } from '@/modules/coming-soon-page';\n\n<Route path=\"/coming-soon\" element={<ComingSoonPage />} />\n\n• Customizable launch date via launchDate prop\n• Email signup with success state\n• Social links (Twitter, Instagram, GitHub, LinkedIn)",
14
+ "files": [
15
+ {
16
+ "path": "coming-soon-page/index.ts",
17
+ "type": "registry:index",
18
+ "target": "$modules$/coming-soon-page/index.ts",
19
+ "content": "export * from \"./coming-soon-page\";\r\nexport { default } from \"./coming-soon-page\";\r\n"
20
+ },
21
+ {
22
+ "path": "coming-soon-page/coming-soon-page.tsx",
23
+ "type": "registry:page",
24
+ "target": "$modules$/coming-soon-page/coming-soon-page.tsx",
25
+ "content": "import { useState, useEffect, useMemo } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Mail, Twitter, Instagram, Github, Linkedin } from \"lucide-react\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { FadeIn, ScaleUp } from \"@/modules/animations\";\r\n\r\ninterface ComingSoonPageProps {\r\n launchDate?: Date;\r\n className?: string;\r\n}\r\n\r\ninterface TimeLeft {\r\n days: number;\r\n hours: number;\r\n minutes: number;\r\n seconds: number;\r\n}\r\n\r\n// Default launch date: 30 days from initial load\r\nconst DEFAULT_LAUNCH_DATE = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);\r\n\r\nexport function ComingSoonPage({\r\n launchDate: launchDateProp,\r\n className\r\n}: ComingSoonPageProps) {\r\n const { t } = useTranslation(\"coming-soon-page\");\r\n usePageTitle({ title: t(\"title\", \"Coming Soon\") });\r\n\r\n const launchDate = useMemo(() => launchDateProp ?? DEFAULT_LAUNCH_DATE, [launchDateProp]);\r\n\r\n const [email, setEmail] = useState(\"\");\r\n const [isSubmitted, setIsSubmitted] = useState(false);\r\n const [timeLeft, setTimeLeft] = useState<TimeLeft>({ days: 0, hours: 0, minutes: 0, seconds: 0 });\r\n\r\n useEffect(() => {\r\n const calculateTimeLeft = () => {\r\n const difference = launchDate.getTime() - new Date().getTime();\r\n\r\n if (difference > 0) {\r\n setTimeLeft({\r\n days: Math.floor(difference / (1000 * 60 * 60 * 24)),\r\n hours: Math.floor((difference / (1000 * 60 * 60)) % 24),\r\n minutes: Math.floor((difference / 1000 / 60) % 60),\r\n seconds: Math.floor((difference / 1000) % 60),\r\n });\r\n }\r\n };\r\n\r\n calculateTimeLeft();\r\n const timer = setInterval(calculateTimeLeft, 1000);\r\n return () => clearInterval(timer);\r\n }, [launchDate]);\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n // Handle email submission\r\n setIsSubmitted(true);\r\n setEmail(\"\");\r\n };\r\n\r\n const socialLinks = [\r\n { icon: Twitter, href: \"#\", label: \"Twitter\" },\r\n { icon: Instagram, href: \"#\", label: \"Instagram\" },\r\n { icon: Github, href: \"#\", label: \"GitHub\" },\r\n { icon: Linkedin, href: \"#\", label: \"LinkedIn\" },\r\n ];\r\n\r\n const timeUnits = [\r\n { value: timeLeft.days, label: t(\"days\", \"Days\") },\r\n { value: timeLeft.hours, label: t(\"hours\", \"Hours\") },\r\n { value: timeLeft.minutes, label: t(\"minutes\", \"Minutes\") },\r\n { value: timeLeft.seconds, label: t(\"seconds\", \"Seconds\") },\r\n ];\r\n\r\n return (\r\n <div className={cn(\r\n \"min-h-screen flex flex-col items-center justify-center relative overflow-hidden\",\r\n \"bg-gradient-to-br from-background via-muted/50 to-background\",\r\n className\r\n )}>\r\n {/* Background decoration */}\r\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\r\n <div className=\"absolute -top-1/2 -left-1/2 w-full h-full bg-primary/5 rounded-full blur-3xl\" />\r\n <div className=\"absolute -bottom-1/2 -right-1/2 w-full h-full bg-primary/10 rounded-full blur-3xl\" />\r\n </div>\r\n\r\n <div className=\"relative z-10 w-full max-w-[var(--container-max-width)] mx-auto px-4 py-12 text-center\">\r\n {/* Heading */}\r\n <FadeIn className=\"mb-6\">\r\n <h1 className=\"text-4xl md:text-5xl lg:text-6xl font-bold text-foreground mb-4\">\r\n {t(\"heading\", \"Something Amazing is Coming\")}\r\n </h1>\r\n <p className=\"text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"description\", \"We're working hard to bring you something special. Stay tuned and be the first to know when we launch.\")}\r\n </p>\r\n </FadeIn>\r\n\r\n {/* Countdown */}\r\n <ScaleUp delay={0.1} className=\"mb-12\">\r\n <div className=\"flex justify-center gap-4 md:gap-8\">\r\n {timeUnits.map((unit, index) => (\r\n <div key={index} className=\"flex flex-col items-center\">\r\n <div className=\"w-16 h-16 md:w-24 md:h-24 bg-card border border-border rounded-2xl flex items-center justify-center shadow-lg\">\r\n <span className=\"text-2xl md:text-4xl font-bold text-foreground\">\r\n {String(unit.value).padStart(2, \"0\")}\r\n </span>\r\n </div>\r\n <span className=\"mt-2 text-xs md:text-sm text-muted-foreground uppercase tracking-wide\">\r\n {unit.label}\r\n </span>\r\n </div>\r\n ))}\r\n </div>\r\n </ScaleUp>\r\n\r\n {/* Email signup */}\r\n <FadeIn delay={0.2} className=\"mb-12\">\r\n {!isSubmitted ? (\r\n <form onSubmit={handleSubmit} className=\"flex flex-col sm:flex-row gap-3 max-w-md mx-auto\">\r\n <div className=\"relative flex-1\">\r\n <Mail className=\"absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground\" />\r\n <Input\r\n type=\"email\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n placeholder={t(\"emailPlaceholder\", \"Enter your email\")}\r\n required\r\n className=\"pl-10 h-12\"\r\n />\r\n </div>\r\n <Button type=\"submit\" size=\"lg\" className=\"h-12\">\r\n {t(\"notify\", \"Notify Me\")}\r\n </Button>\r\n </form>\r\n ) : (\r\n <div className=\"bg-primary/10 border border-primary/20 rounded-lg p-4 max-w-md mx-auto\">\r\n <p className=\"text-primary font-medium\">\r\n {t(\"success\", \"Thank you! We'll notify you when we launch.\")}\r\n </p>\r\n </div>\r\n )}\r\n </FadeIn>\r\n\r\n {/* Social links */}\r\n <FadeIn delay={0.3}>\r\n <p className=\"text-sm text-muted-foreground mb-4\">\r\n {t(\"followUs\", \"Follow us for updates\")}\r\n </p>\r\n <div className=\"flex justify-center gap-4\">\r\n {socialLinks.map((social, index) => (\r\n <a\r\n key={index}\r\n href={social.href}\r\n aria-label={social.label}\r\n className=\"w-10 h-10 rounded-full bg-muted hover:bg-primary/10 flex items-center justify-center transition-colors\"\r\n >\r\n <social.icon className=\"h-5 w-5 text-muted-foreground hover:text-primary transition-colors\" />\r\n </a>\r\n ))}\r\n </div>\r\n </FadeIn>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nexport default ComingSoonPage;\r\n"
26
+ },
27
+ {
28
+ "path": "coming-soon-page/lang/en.json",
29
+ "type": "registry:lang",
30
+ "target": "$modules$/coming-soon-page/lang/en.json",
31
+ "content": "{\r\n \"title\": \"Coming Soon\",\r\n \"heading\": \"Something Amazing is Coming\",\r\n \"description\": \"Ask Promake to customize this description based on your upcoming launch. Lorem ipsum dolor sit amet.\",\r\n \"days\": \"Days\",\r\n \"hours\": \"Hours\",\r\n \"minutes\": \"Minutes\",\r\n \"seconds\": \"Seconds\",\r\n \"emailPlaceholder\": \"Enter your email\",\r\n \"notify\": \"Notify Me\",\r\n \"success\": \"Thank you! We'll notify you when we launch.\",\r\n \"followUs\": \"Follow us for updates\"\r\n}\r\n"
32
+ },
33
+ {
34
+ "path": "coming-soon-page/lang/tr.json",
35
+ "type": "registry:lang",
36
+ "target": "$modules$/coming-soon-page/lang/tr.json",
37
+ "content": "{\r\n \"title\": \"Yakında\",\r\n \"heading\": \"Harika Bir Şey Geliyor\",\r\n \"description\": \"Size özel bir şey sunmak için çalışıyoruz. Lansman yapıldığında ilk öğrenen siz olun.\",\r\n \"days\": \"Gün\",\r\n \"hours\": \"Saat\",\r\n \"minutes\": \"Dakika\",\r\n \"seconds\": \"Saniye\",\r\n \"emailPlaceholder\": \"E-posta adresiniz\",\r\n \"notify\": \"Beni Bilgilendir\",\r\n \"success\": \"Teşekkürler! Lansman yapıldığında sizi bilgilendireceğiz.\",\r\n \"followUs\": \"Güncellemeler için bizi takip edin\"\r\n}\r\n"
38
+ }
39
+ ],
40
+ "exports": {
41
+ "types": [],
42
+ "variables": [
43
+ "ComingSoonPage",
44
+ "default"
45
+ ]
46
+ }
47
+ }
@@ -16,13 +16,13 @@
16
16
  "path": "contact-info-grid/contact-info-grid.tsx",
17
17
  "type": "registry:component",
18
18
  "target": "$modules$/contact-info-grid/contact-info-grid.tsx",
19
- "content": "import { Mail, MapPin, MessageCircle, Phone } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactInfoGridProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactInfoGrid({ className }: ContactInfoGridProps) {\r\n const { t } = useTranslation(\"contact-info-grid\");\r\n\r\n const contactItems = [\r\n {\r\n icon: Mail,\r\n label: t(\"emailLabel\", \"Email\"),\r\n description: t(\"emailDesc\", \"We respond to all emails within 24 hours.\"),\r\n value: constants.email || \"hello@example.com\",\r\n href: `mailto:${constants.email || \"hello@example.com\"}`,\r\n },\r\n {\r\n icon: MapPin,\r\n label: t(\"officeLabel\", \"Office\"),\r\n description: t(\"officeDesc\", \"Drop by our office for a chat.\"),\r\n value: `${constants.address?.line1 || \"\"}, ${constants.address?.city || \"\"}`,\r\n href: \"#\",\r\n },\r\n {\r\n icon: Phone,\r\n label: t(\"phoneLabel\", \"Phone\"),\r\n description: t(\"phoneDesc\", \"We're available Mon-Fri, 9am-5pm.\"),\r\n value: constants.phone || \"+1 234 567 890\",\r\n href: `tel:${constants.phone || \"+1234567890\"}`,\r\n },\r\n {\r\n icon: MessageCircle,\r\n label: t(\"chatLabel\", \"Live Chat\"),\r\n description: t(\"chatDesc\", \"Get instant help from our support team.\"),\r\n value: t(\"startChat\", \"Start Chat\"),\r\n href: \"#\",\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4\">\r\n <div className=\"mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Get in Touch\")}\r\n </h2>\r\n <p className=\"text-lg text-muted-foreground max-w-xl\">\r\n {t(\"subtitle\", \"Have questions? We'd love to hear from you. Choose your preferred way to reach us.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid sm:grid-cols-2 lg:grid-cols-4 gap-6\">\r\n {contactItems.map((item, index) => (\r\n <div\r\n key={index}\r\n className=\"rounded-xl bg-muted/50 p-6 hover:bg-muted transition-colors\"\r\n >\r\n <span className=\"mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/10\">\r\n <item.icon className=\"h-6 w-6 text-primary\" />\r\n </span>\r\n <h3 className=\"mb-2 text-lg font-semibold\">{item.label}</h3>\r\n <p className=\"mb-3 text-sm text-muted-foreground\">\r\n {item.description}\r\n </p>\r\n <a\r\n href={item.href}\r\n className=\"text-sm font-semibold text-primary hover:underline\"\r\n >\r\n {item.value}\r\n </a>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
19
+ "content": "import { Mail, MapPin, MessageCircle, Phone } from \"lucide-react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactInfoGridProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactInfoGrid({ className }: ContactInfoGridProps) {\r\n const { t } = useTranslation(\"contact-info-grid\");\r\n\r\n const contactItems = [\r\n {\r\n icon: Mail,\r\n label: t(\"emailLabel\", \"Email\"),\r\n description: t(\"emailDesc\", \"We respond to all emails within 24 hours.\"),\r\n value: constants.email || \"hello@example.com\",\r\n href: `mailto:${constants.email || \"hello@example.com\"}`,\r\n },\r\n {\r\n icon: MapPin,\r\n label: t(\"officeLabel\", \"Office\"),\r\n description: t(\"officeDesc\", \"Drop by our office for a chat.\"),\r\n value: `${constants.address?.line1 || \"\"}, ${constants.address?.city || \"\"}`,\r\n href: \"#\",\r\n },\r\n {\r\n icon: Phone,\r\n label: t(\"phoneLabel\", \"Phone\"),\r\n description: t(\"phoneDesc\", \"We're available Mon-Fri, 9am-5pm.\"),\r\n value: constants.phone || \"+1 234 567 890\",\r\n href: `tel:${constants.phone || \"+1234567890\"}`,\r\n },\r\n {\r\n icon: MessageCircle,\r\n label: t(\"chatLabel\", \"Live Chat\"),\r\n description: t(\"chatDesc\", \"Get instant help from our support team.\"),\r\n value: t(\"startChat\", \"Start Chat\"),\r\n href: \"#\",\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Get in Touch\")}\r\n </h2>\r\n <p className=\"text-lg text-muted-foreground max-w-xl\">\r\n {t(\"subtitle\", \"Have questions? We'd love to hear from you. Choose your preferred way to reach us.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid sm:grid-cols-2 lg:grid-cols-4 gap-6\">\r\n {contactItems.map((item, index) => (\r\n <div\r\n key={index}\r\n className=\"rounded-xl bg-muted/50 p-6 hover:bg-muted transition-colors\"\r\n >\r\n <span className=\"mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/10\">\r\n <item.icon className=\"h-6 w-6 text-primary\" />\r\n </span>\r\n <h3 className=\"mb-2 text-lg font-semibold\">{item.label}</h3>\r\n <p className=\"mb-3 text-sm text-muted-foreground\">\r\n {item.description}\r\n </p>\r\n <a\r\n href={item.href}\r\n className=\"text-sm font-semibold text-primary hover:underline\"\r\n >\r\n {item.value}\r\n </a>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
20
20
  },
21
21
  {
22
22
  "path": "contact-info-grid/lang/en.json",
23
23
  "type": "registry:lang",
24
24
  "target": "$modules$/contact-info-grid/lang/en.json",
25
- "content": "{\r\n \"title\": \"Get in Touch\",\r\n \"subtitle\": \"AI will customize this contact subtitle based on your site. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"Email\",\r\n \"emailDesc\": \"We respond to all emails within 24 hours.\",\r\n \"officeLabel\": \"Office\",\r\n \"officeDesc\": \"Drop by our office for a chat.\",\r\n \"phoneLabel\": \"Phone\",\r\n \"phoneDesc\": \"We're available Mon-Fri, 9am-5pm.\",\r\n \"chatLabel\": \"Live Chat\",\r\n \"chatDesc\": \"Get instant help from our support team.\",\r\n \"startChat\": \"Start Chat\"\r\n}\r\n"
25
+ "content": "{\r\n \"title\": \"Get in Touch\",\r\n \"subtitle\": \"Ask Promake to customize this contact subtitle based on your site. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"Email\",\r\n \"emailDesc\": \"We respond to all emails within 24 hours.\",\r\n \"officeLabel\": \"Office\",\r\n \"officeDesc\": \"Drop by our office for a chat.\",\r\n \"phoneLabel\": \"Phone\",\r\n \"phoneDesc\": \"We're available Mon-Fri, 9am-5pm.\",\r\n \"chatLabel\": \"Live Chat\",\r\n \"chatDesc\": \"Get instant help from our support team.\",\r\n \"startChat\": \"Start Chat\"\r\n}\r\n"
26
26
  },
27
27
  {
28
28
  "path": "contact-info-grid/lang/tr.json",
@@ -25,13 +25,13 @@
25
25
  "path": "contact-page-centered/contact-page-centered.tsx",
26
26
  "type": "registry:component",
27
27
  "target": "$modules$/contact-page-centered/contact-page-centered.tsx",
28
- "content": "import React, { useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { Mail, Phone, MapPin } from \"lucide-react\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { useApiService } from \"@/lib/api\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactPageCenteredProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactPageCentered({ className }: ContactPageCenteredProps) {\r\n const { t } = useTranslation(\"contact-page-centered\");\r\n const apiService = useApiService();\r\n\r\n const [formData, setFormData] = useState({\r\n name: \"\",\r\n email: \"\",\r\n message: \"\",\r\n });\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [submitStatus, setSubmitStatus] = useState<\"idle\" | \"success\" | \"error\">(\"idle\");\r\n\r\n const contactCards = [\r\n {\r\n icon: Mail,\r\n title: t(\"emailTitle\", \"Email\"),\r\n value: constants.email || \"hello@example.com\",\r\n href: `mailto:${constants.email || \"hello@example.com\"}`,\r\n },\r\n {\r\n icon: Phone,\r\n title: t(\"phoneTitle\", \"Phone\"),\r\n value: constants.phone || \"+1 234 567 890\",\r\n href: `tel:${constants.phone || \"+1234567890\"}`,\r\n },\r\n {\r\n icon: MapPin,\r\n title: t(\"addressTitle\", \"Address\"),\r\n value: constants.address?.city || \"New York, USA\",\r\n href: \"#\",\r\n },\r\n ];\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n setSubmitStatus(\"idle\");\r\n\r\n try {\r\n await apiService.submitForm(\r\n formData,\r\n {\r\n email_subject1: \"Thank you for contacting us\",\r\n email_subject2: \"New Contact Form Submission\",\r\n fields: [\r\n { name: \"name\", required: true },\r\n { name: \"email\", required: true },\r\n { name: \"message\", required: true },\r\n ],\r\n },\r\n constants.site.defaultLanguage\r\n );\r\n\r\n setSubmitStatus(\"success\");\r\n setFormData({ name: \"\", email: \"\", message: \"\" });\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } catch {\r\n setSubmitStatus(\"error\");\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\r\n setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));\r\n };\r\n\r\n return (\r\n <Layout>\r\n <div className={cn(\"min-h-screen bg-muted/30 py-16 md:py-24\", className)}>\r\n <div className=\"container mx-auto px-4 max-w-4xl\">\r\n {/* Header */}\r\n <div className=\"text-center mb-12\">\r\n <h1 className=\"text-4xl font-bold mb-4\">{t(\"title\", \"Contact Us\")}</h1>\r\n <p className=\"text-lg text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"We'd love to hear from you. Send us a message and we'll respond as soon as possible.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Contact Cards */}\r\n <div className=\"grid sm:grid-cols-3 gap-4 mb-12\">\r\n {contactCards.map((card, index) => (\r\n <Card key={index} className=\"text-center\">\r\n <CardContent className=\"pt-6\">\r\n <div className=\"mx-auto w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mb-4\">\r\n <card.icon className=\"h-6 w-6 text-primary\" />\r\n </div>\r\n <h3 className=\"font-semibold mb-1\">{card.title}</h3>\r\n <a\r\n href={card.href}\r\n className=\"text-sm text-muted-foreground hover:text-primary transition-colors\"\r\n >\r\n {card.value}\r\n </a>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n\r\n {/* Form */}\r\n <Card>\r\n <CardContent className=\"pt-6\">\r\n <form onSubmit={handleSubmit} className=\"space-y-6\">\r\n <div className=\"grid sm:grid-cols-2 gap-4\">\r\n <div>\r\n <Label htmlFor=\"name\">{t(\"nameLabel\", \"Name\")} *</Label>\r\n <Input\r\n id=\"name\"\r\n name=\"name\"\r\n value={formData.name}\r\n onChange={handleChange}\r\n placeholder={t(\"namePlaceholder\", \"Your name\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n <div>\r\n <Label htmlFor=\"email\">{t(\"emailLabel\", \"Email\")} *</Label>\r\n <Input\r\n id=\"email\"\r\n name=\"email\"\r\n type=\"email\"\r\n value={formData.email}\r\n onChange={handleChange}\r\n placeholder={t(\"emailPlaceholder\", \"your@email.com\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"message\">{t(\"messageLabel\", \"Message\")} *</Label>\r\n <Textarea\r\n id=\"message\"\r\n name=\"message\"\r\n value={formData.message}\r\n onChange={handleChange}\r\n placeholder={t(\"messagePlaceholder\", \"How can we help you?\")}\r\n required\r\n rows={6}\r\n className=\"mt-1 resize-none\"\r\n />\r\n </div>\r\n\r\n {submitStatus === \"success\" && (\r\n <div className=\"p-4 bg-green-500/10 border border-green-500/30 rounded-lg\">\r\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\r\n {t(\"success\", \"Message sent successfully! We'll get back to you soon.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {submitStatus === \"error\" && (\r\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\r\n <p className=\"text-destructive text-sm font-medium\">\r\n {t(\"error\", \"Something went wrong. Please try again.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button type=\"submit\" size=\"lg\" className=\"w-full\" disabled={isSubmitting}>\r\n {isSubmitting ? t(\"sending\", \"Sending...\") : t(\"submit\", \"Send Message\")}\r\n </Button>\r\n </form>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n"
28
+ "content": "import React, { useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Mail, Phone, MapPin } from \"lucide-react\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { useApiService } from \"@/lib/api\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Card, CardContent } from \"@/components/ui/card\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactPageCenteredProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactPageCentered({ className }: ContactPageCenteredProps) {\r\n const { t } = useTranslation(\"contact-page-centered\");\r\n usePageTitle({ title: t(\"title\", \"Contact Us\") });\r\n const apiService = useApiService();\r\n\r\n const [formData, setFormData] = useState({\r\n name: \"\",\r\n email: \"\",\r\n message: \"\",\r\n });\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [submitStatus, setSubmitStatus] = useState<\"idle\" | \"success\" | \"error\">(\"idle\");\r\n\r\n const contactCards = [\r\n {\r\n icon: Mail,\r\n title: t(\"emailTitle\", \"Email\"),\r\n value: constants.email || \"hello@example.com\",\r\n href: `mailto:${constants.email || \"hello@example.com\"}`,\r\n },\r\n {\r\n icon: Phone,\r\n title: t(\"phoneTitle\", \"Phone\"),\r\n value: constants.phone || \"+1 234 567 890\",\r\n href: `tel:${constants.phone || \"+1234567890\"}`,\r\n },\r\n {\r\n icon: MapPin,\r\n title: t(\"addressTitle\", \"Address\"),\r\n value: constants.address?.city || \"New York, USA\",\r\n href: \"#\",\r\n },\r\n ];\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n setSubmitStatus(\"idle\");\r\n\r\n try {\r\n await apiService.submitForm(\r\n formData,\r\n {\r\n email_subject1: \"Thank you for contacting us\",\r\n email_subject2: \"New Contact Form Submission\",\r\n fields: [\r\n { name: \"name\", required: true },\r\n { name: \"email\", required: true },\r\n { name: \"message\", required: true },\r\n ],\r\n },\r\n constants.site.defaultLanguage\r\n );\r\n\r\n setSubmitStatus(\"success\");\r\n setFormData({ name: \"\", email: \"\", message: \"\" });\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } catch {\r\n setSubmitStatus(\"error\");\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\r\n setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));\r\n };\r\n\r\n return (\r\n <Layout>\r\n <div className={cn(\"min-h-screen bg-muted/30 py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 max-w-4xl\">\r\n {/* Header */}\r\n <div className=\"text-center mb-12\">\r\n <h1 className=\"text-4xl font-bold mb-4\">{t(\"title\", \"Contact Us\")}</h1>\r\n <p className=\"text-lg text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"We'd love to hear from you. Send us a message and we'll respond as soon as possible.\")}\r\n </p>\r\n </div>\r\n\r\n {/* Contact Cards */}\r\n <div className=\"grid sm:grid-cols-3 gap-4 mb-12\">\r\n {contactCards.map((card, index) => (\r\n <Card key={index} className=\"text-center\">\r\n <CardContent className=\"pt-6\">\r\n <div className=\"mx-auto w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center mb-4\">\r\n <card.icon className=\"h-6 w-6 text-primary\" />\r\n </div>\r\n <h3 className=\"font-semibold mb-1\">{card.title}</h3>\r\n <a\r\n href={card.href}\r\n className=\"text-sm text-muted-foreground hover:text-primary transition-colors\"\r\n >\r\n {card.value}\r\n </a>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n\r\n {/* Form */}\r\n <Card>\r\n <CardContent className=\"pt-6\">\r\n <form onSubmit={handleSubmit} className=\"space-y-6\">\r\n <div className=\"grid sm:grid-cols-2 gap-4\">\r\n <div>\r\n <Label htmlFor=\"name\">{t(\"nameLabel\", \"Name\")} *</Label>\r\n <Input\r\n id=\"name\"\r\n name=\"name\"\r\n value={formData.name}\r\n onChange={handleChange}\r\n placeholder={t(\"namePlaceholder\", \"Your name\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n <div>\r\n <Label htmlFor=\"email\">{t(\"emailLabel\", \"Email\")} *</Label>\r\n <Input\r\n id=\"email\"\r\n name=\"email\"\r\n type=\"email\"\r\n value={formData.email}\r\n onChange={handleChange}\r\n placeholder={t(\"emailPlaceholder\", \"your@email.com\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"message\">{t(\"messageLabel\", \"Message\")} *</Label>\r\n <Textarea\r\n id=\"message\"\r\n name=\"message\"\r\n value={formData.message}\r\n onChange={handleChange}\r\n placeholder={t(\"messagePlaceholder\", \"How can we help you?\")}\r\n required\r\n rows={6}\r\n className=\"mt-1 resize-none\"\r\n />\r\n </div>\r\n\r\n {submitStatus === \"success\" && (\r\n <div className=\"p-4 bg-green-500/10 border border-green-500/30 rounded-lg\">\r\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\r\n {t(\"success\", \"Message sent successfully! We'll get back to you soon.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {submitStatus === \"error\" && (\r\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\r\n <p className=\"text-destructive text-sm font-medium\">\r\n {t(\"error\", \"Something went wrong. Please try again.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button type=\"submit\" size=\"lg\" className=\"w-full\" disabled={isSubmitting}>\r\n {isSubmitting ? t(\"sending\", \"Sending...\") : t(\"submit\", \"Send Message\")}\r\n </Button>\r\n </form>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n\r\nexport default ContactPageCentered;\r\n"
29
29
  },
30
30
  {
31
31
  "path": "contact-page-centered/lang/en.json",
32
32
  "type": "registry:lang",
33
33
  "target": "$modules$/contact-page-centered/lang/en.json",
34
- "content": "{\r\n \"title\": \"Contact Us\",\r\n \"subtitle\": \"AI will customize this contact subtitle. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.\",\r\n \"emailTitle\": \"Email\",\r\n \"phoneTitle\": \"Phone\",\r\n \"addressTitle\": \"Address\",\r\n \"nameLabel\": \"Name\",\r\n \"namePlaceholder\": \"Your name\",\r\n \"emailLabel\": \"Email\",\r\n \"emailPlaceholder\": \"your@email.com\",\r\n \"messageLabel\": \"Message\",\r\n \"messagePlaceholder\": \"How can we help you?\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Message sent successfully! We'll get back to you soon.\",\r\n \"error\": \"Something went wrong. Please try again.\"\r\n}\r\n"
34
+ "content": "{\r\n \"title\": \"Contact Us\",\r\n \"subtitle\": \"Ask Promake to customize this contact subtitle. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor.\",\r\n \"emailTitle\": \"Email\",\r\n \"phoneTitle\": \"Phone\",\r\n \"addressTitle\": \"Address\",\r\n \"nameLabel\": \"Name\",\r\n \"namePlaceholder\": \"Your name\",\r\n \"emailLabel\": \"Email\",\r\n \"emailPlaceholder\": \"your@email.com\",\r\n \"messageLabel\": \"Message\",\r\n \"messagePlaceholder\": \"How can we help you?\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Message sent successfully! We'll get back to you soon.\",\r\n \"error\": \"Something went wrong. Please try again.\"\r\n}\r\n"
35
35
  },
36
36
  {
37
37
  "path": "contact-page-centered/lang/tr.json",
@@ -24,13 +24,13 @@
24
24
  "path": "contact-page-map-overlay/index.ts",
25
25
  "type": "registry:index",
26
26
  "target": "$modules$/contact-page-map-overlay/index.ts",
27
- "content": "export * from \"./contact-page-map-overlay\";\n"
27
+ "content": "export * from \"./contact-page-map-overlay\";\nexport { default } from \"./contact-page-map-overlay\";\n"
28
28
  },
29
29
  {
30
30
  "path": "contact-page-map-overlay/contact-page-map-overlay.tsx",
31
31
  "type": "registry:component",
32
32
  "target": "$modules$/contact-page-map-overlay/contact-page-map-overlay.tsx",
33
- "content": "import React, { useState, useMemo, useEffect } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n Mail,\n Phone,\n MapPin,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n Send,\n ExternalLink,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { GoogleMap } from \"@/modules/google-map\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPageMapOverlay() {\n const { t } = useTranslation(\"contact-page-map-overlay\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n // Auto-reset status after 5 seconds with proper cleanup\n useEffect(() => {\n if (submitStatus === \"idle\") return;\n const timer = setTimeout(() => setSubmitStatus(\"idle\"), 5000);\n return () => clearTimeout(timer);\n }, [submitStatus]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n message: \"\",\n });\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n // Default coordinates (can be customized via constants)\n const mapLatitude = (constants as any).location?.latitude || 41.0082;\n const mapLongitude = (constants as any).location?.longitude || 28.9784;\n\n return (\n <Layout>\n <div className=\"relative min-h-screen\">\n {/* Full-screen Map Background */}\n <div className=\"absolute inset-0\">\n <GoogleMap\n latitude={mapLatitude}\n longitude={mapLongitude}\n zoom={14}\n height=\"100%\"\n className=\"rounded-none border-0 h-full\"\n title={t(\"mapTitle\")}\n />\n {/* Dark overlay for better readability */}\n <div className=\"absolute inset-0 bg-black/30 pointer-events-none\" />\n </div>\n\n {/* Content Overlay */}\n <div className=\"relative z-10 min-h-screen flex items-center py-12 px-4\">\n <div className=\"container mx-auto max-w-6xl\">\n <div className=\"grid lg:grid-cols-5 gap-8 items-center\">\n {/* Form Card - Glassmorphism */}\n <Card className=\"lg:col-span-3 backdrop-blur-xl bg-background/85 dark:bg-background/90 border-white/20 shadow-2xl\">\n <CardHeader>\n <CardTitle className=\"text-2xl lg:text-3xl\">\n {t(\"title\")}\n </CardTitle>\n <p className=\"text-muted-foreground mt-2\">\n {t(\"description\")}\n </p>\n </CardHeader>\n <CardContent>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <Label htmlFor=\"name\" className=\"text-sm font-medium\">\n {t(\"fullName\")} *\n </Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1.5 bg-background/50\"\n />\n </div>\n <div>\n <Label htmlFor=\"email\" className=\"text-sm font-medium\">\n {t(\"emailAddress\")} *\n </Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1.5 bg-background/50\"\n />\n </div>\n </div>\n\n <div>\n <Label htmlFor=\"message\" className=\"text-sm font-medium\">\n {t(\"message\")} *\n </Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={4}\n className=\"mt-1.5 resize-none bg-background/50\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-3 bg-green-500/10 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-3 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm\">{t(\"error\")}</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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n <>\n <Send className=\"w-4 h-4 mr-2\" />\n {t(\"submit\")}\n </>\n )}\n </Button>\n </form>\n </CardContent>\n </Card>\n\n {/* Contact Info Card - Glassmorphism */}\n <Card className=\"lg:col-span-2 backdrop-blur-xl bg-background/85 dark:bg-background/90 border-white/20 shadow-2xl\">\n <CardHeader>\n <CardTitle>{t(\"contactInfo\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Mail className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"email\")}</p>\n <a\n href={`mailto:${constants.email}`}\n className=\"font-medium hover:text-primary transition-colors\"\n >\n {constants.email}\n </a>\n </div>\n </div>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Phone className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"phone\")}</p>\n <a\n href={`tel:${constants.phone}`}\n className=\"font-medium hover:text-primary transition-colors\"\n >\n {constants.phone}\n </a>\n </div>\n </div>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <MapPin className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"address\")}</p>\n <p className=\"font-medium\">\n {constants.address.line1}\n <br />\n {constants.address.city}, {constants.address.state} {constants.address.postalCode}\n </p>\n </div>\n </div>\n\n {/* Open in Maps Link */}\n <a\n href={`https://www.google.com/maps?q=${mapLatitude},${mapLongitude}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-2 text-sm text-primary hover:underline mt-2\"\n >\n <ExternalLink className=\"w-4 h-4\" />\n {t(\"openInMaps\")}\n </a>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"pt-4 border-t\">\n <p className=\"text-sm text-muted-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-full border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
33
+ "content": "import React, { useState, useMemo, useEffect } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n Mail,\n Phone,\n MapPin,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n Send,\n ExternalLink,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { GoogleMap } from \"@/modules/google-map\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPageMapOverlay() {\n const { t } = useTranslation(\"contact-page-map-overlay\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n // Auto-reset status after 5 seconds with proper cleanup\n useEffect(() => {\n if (submitStatus === \"idle\") return;\n const timer = setTimeout(() => setSubmitStatus(\"idle\"), 5000);\n return () => clearTimeout(timer);\n }, [submitStatus]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n message: \"\",\n });\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n // Default coordinates (can be customized via constants)\n const mapLatitude = (constants as any).location?.latitude || 41.0082;\n const mapLongitude = (constants as any).location?.longitude || 28.9784;\n\n return (\n <Layout>\n <div className=\"relative min-h-[calc(100vh-4rem)]\">\n {/* Full-screen Map Background */}\n <div className=\"absolute inset-0\">\n <GoogleMap\n latitude={mapLatitude}\n longitude={mapLongitude}\n zoom={14}\n height=\"100%\"\n className=\"rounded-none border-0 h-full\"\n title={t(\"mapTitle\")}\n />\n {/* Dark overlay for better readability */}\n <div className=\"absolute inset-0 bg-black/30 pointer-events-none\" />\n </div>\n\n {/* Content Overlay */}\n <div className=\"relative z-10 min-h-[calc(100vh-4rem)] flex items-center py-12 px-4\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto\">\n <div className=\"grid lg:grid-cols-2 gap-8 items-stretch\">\n {/* Form Card - Glassmorphism */}\n <Card className=\"backdrop-blur-xl bg-background/85 dark:bg-background/90 border-white/20 shadow-2xl\">\n <CardHeader>\n <CardTitle className=\"text-2xl lg:text-3xl\">\n {t(\"title\")}\n </CardTitle>\n <p className=\"text-muted-foreground mt-2\">\n {t(\"description\")}\n </p>\n </CardHeader>\n <CardContent>\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <Label htmlFor=\"name\" className=\"text-sm font-medium\">\n {t(\"fullName\")} *\n </Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1.5 bg-background/50\"\n />\n </div>\n <div>\n <Label htmlFor=\"email\" className=\"text-sm font-medium\">\n {t(\"emailAddress\")} *\n </Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1.5 bg-background/50\"\n />\n </div>\n </div>\n\n <div>\n <Label htmlFor=\"message\" className=\"text-sm font-medium\">\n {t(\"message\")} *\n </Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={4}\n className=\"mt-1.5 resize-none bg-background/50\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-3 bg-green-500/10 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-3 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm\">{t(\"error\")}</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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n <>\n <Send className=\"w-4 h-4 mr-2\" />\n {t(\"submit\")}\n </>\n )}\n </Button>\n </form>\n </CardContent>\n </Card>\n\n {/* Contact Info Card - Glassmorphism */}\n <Card className=\"backdrop-blur-xl bg-background/85 dark:bg-background/90 border-white/20 shadow-2xl\">\n <CardHeader>\n <CardTitle>{t(\"contactInfo\")}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Mail className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"email\")}</p>\n <a\n href={`mailto:${constants.email}`}\n className=\"font-medium hover:text-primary transition-colors\"\n >\n {constants.email}\n </a>\n </div>\n </div>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Phone className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"phone\")}</p>\n <a\n href={`tel:${constants.phone}`}\n className=\"font-medium hover:text-primary transition-colors\"\n >\n {constants.phone}\n </a>\n </div>\n </div>\n\n <div className=\"flex items-start gap-4\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <MapPin className=\"w-5 h-5 text-primary\" />\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">{t(\"address\")}</p>\n <p className=\"font-medium\">\n {constants.address.line1}\n <br />\n {constants.address.city}, {constants.address.state} {constants.address.postalCode}\n </p>\n </div>\n </div>\n\n {/* Open in Maps Link */}\n <a\n href={`https://www.google.com/maps?q=${mapLatitude},${mapLongitude}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-2 text-sm text-primary hover:underline mt-2\"\n >\n <ExternalLink className=\"w-4 h-4\" />\n {t(\"openInMaps\")}\n </a>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"pt-4 border-t\">\n <p className=\"text-sm text-muted-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-full border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default ContactPageMapOverlay;\n"
34
34
  },
35
35
  {
36
36
  "path": "contact-page-map-overlay/lang/en.json",
@@ -48,7 +48,8 @@
48
48
  "exports": {
49
49
  "types": [],
50
50
  "variables": [
51
- "ContactPageMapOverlay"
51
+ "ContactPageMapOverlay",
52
+ "default"
52
53
  ]
53
54
  }
54
55
  }
@@ -24,13 +24,13 @@
24
24
  "path": "contact-page-map-split/index.ts",
25
25
  "type": "registry:index",
26
26
  "target": "$modules$/contact-page-map-split/index.ts",
27
- "content": "export * from \"./contact-page-map-split\";\n"
27
+ "content": "export * from \"./contact-page-map-split\";\nexport { default } from \"./contact-page-map-split\";\n"
28
28
  },
29
29
  {
30
30
  "path": "contact-page-map-split/contact-page-map-split.tsx",
31
31
  "type": "registry:component",
32
32
  "target": "$modules$/contact-page-map-split/contact-page-map-split.tsx",
33
- "content": "import React, { useState, useMemo, useEffect } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n Mail,\n Phone,\n MapPin,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n Send,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { GoogleMap } from \"@/modules/google-map\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPageMapSplit() {\n const { t } = useTranslation(\"contact-page-map-split\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n // Auto-reset status after 5 seconds with proper cleanup\n useEffect(() => {\n if (submitStatus === \"idle\") return;\n const timer = setTimeout(() => setSubmitStatus(\"idle\"), 5000);\n return () => clearTimeout(timer);\n }, [submitStatus]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"phone\", required: false },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n // Default coordinates (can be customized via constants)\n const mapLatitude = (constants as any).location?.latitude || 41.0082;\n const mapLongitude = (constants as any).location?.longitude || 28.9784;\n\n return (\n <Layout>\n <div className=\"min-h-screen flex flex-col lg:flex-row\">\n {/* Left Side - Form & Info */}\n <div className=\"w-full lg:w-1/2 bg-background py-12 lg:py-16 px-6 lg:px-12 flex flex-col justify-center\">\n <div className=\"max-w-lg mx-auto w-full\">\n {/* Header */}\n <div className=\"mb-10\">\n <h1 className=\"text-3xl lg:text-4xl font-bold text-foreground mb-3\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"description\")}\n </p>\n </div>\n\n {/* Contact Info Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 mb-10\">\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <Mail className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"email\")}</p>\n <p className=\"text-sm font-medium truncate\">{constants.email}</p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <Phone className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"phone\")}</p>\n <p className=\"text-sm font-medium truncate\">{constants.phone}</p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50 sm:col-span-2\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <MapPin className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"address\")}</p>\n <p className=\"text-sm font-medium\">\n {constants.address.line1}, {constants.address.city}\n </p>\n </div>\n </div>\n </div>\n\n {/* Contact Form */}\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <Label htmlFor=\"name\" className=\"text-sm font-medium\">\n {t(\"fullName\")} *\n </Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1.5\"\n />\n </div>\n <div>\n <Label htmlFor=\"email\" className=\"text-sm font-medium\">\n {t(\"emailAddress\")} *\n </Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1.5\"\n />\n </div>\n </div>\n\n <div>\n <Label htmlFor=\"phone\" className=\"text-sm font-medium\">\n {t(\"phoneNumber\")}\n </Label>\n <Input\n id=\"phone\"\n name=\"phone\"\n type=\"tel\"\n value={formData.phone}\n onChange={handleChange}\n placeholder={t(\"phonePlaceholder\")}\n className=\"mt-1.5\"\n />\n </div>\n\n <div>\n <Label htmlFor=\"message\" className=\"text-sm font-medium\">\n {t(\"message\")} *\n </Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={4}\n className=\"mt-1.5 resize-none\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-3 bg-green-500/10 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-3 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm\">{t(\"error\")}</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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n <>\n <Send className=\"w-4 h-4 mr-2\" />\n {t(\"submit\")}\n </>\n )}\n </Button>\n </form>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"mt-10 pt-6 border-t\">\n <p className=\"text-sm text-muted-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-full border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right Side - Map */}\n <div className=\"w-full lg:w-1/2 h-[400px] lg:h-auto lg:min-h-screen relative\">\n <GoogleMap\n latitude={mapLatitude}\n longitude={mapLongitude}\n zoom={14}\n height=\"100%\"\n className=\"rounded-none border-0 h-full\"\n title={t(\"mapTitle\")}\n />\n </div>\n </div>\n </Layout>\n );\n}\n"
33
+ "content": "import React, { useState, useMemo, useEffect } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n Mail,\n Phone,\n MapPin,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n Send,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { GoogleMap } from \"@/modules/google-map\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPageMapSplit() {\n const { t } = useTranslation(\"contact-page-map-split\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n // Auto-reset status after 5 seconds with proper cleanup\n useEffect(() => {\n if (submitStatus === \"idle\") return;\n const timer = setTimeout(() => setSubmitStatus(\"idle\"), 5000);\n return () => clearTimeout(timer);\n }, [submitStatus]);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"phone\", required: false },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n phone: \"\",\n message: \"\",\n });\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n // Default coordinates (can be customized via constants)\n const mapLatitude = (constants as any).location?.latitude || 41.0082;\n const mapLongitude = (constants as any).location?.longitude || 28.9784;\n\n return (\n <Layout>\n <div className=\"min-h-screen flex flex-col lg:flex-row\">\n {/* Left Side - Form & Info */}\n <div className=\"w-full lg:w-1/2 bg-background py-12 lg:py-16 px-6 lg:px-12 flex flex-col justify-center\">\n <div className=\"max-w-lg mx-auto w-full\">\n {/* Header */}\n <div className=\"mb-10\">\n <h1 className=\"text-3xl lg:text-4xl font-bold text-foreground mb-3\">\n {t(\"title\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\"description\")}\n </p>\n </div>\n\n {/* Contact Info Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 mb-10\">\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <Mail className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"email\")}</p>\n <p className=\"text-sm font-medium truncate\">{constants.email}</p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center\">\n <Phone className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"phone\")}</p>\n <p className=\"text-sm font-medium truncate\">{constants.phone}</p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-3 p-4 rounded-xl bg-muted/50 sm:col-span-2\">\n <div className=\"w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <MapPin className=\"w-5 h-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-xs text-muted-foreground\">{t(\"address\")}</p>\n <p className=\"text-sm font-medium\">\n {constants.address.line1}, {constants.address.city}\n </p>\n </div>\n </div>\n </div>\n\n {/* Contact Form */}\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <Label htmlFor=\"name\" className=\"text-sm font-medium\">\n {t(\"fullName\")} *\n </Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1.5\"\n />\n </div>\n <div>\n <Label htmlFor=\"email\" className=\"text-sm font-medium\">\n {t(\"emailAddress\")} *\n </Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1.5\"\n />\n </div>\n </div>\n\n <div>\n <Label htmlFor=\"phone\" className=\"text-sm font-medium\">\n {t(\"phoneNumber\")}\n </Label>\n <Input\n id=\"phone\"\n name=\"phone\"\n type=\"tel\"\n value={formData.phone}\n onChange={handleChange}\n placeholder={t(\"phonePlaceholder\")}\n className=\"mt-1.5\"\n />\n </div>\n\n <div>\n <Label htmlFor=\"message\" className=\"text-sm font-medium\">\n {t(\"message\")} *\n </Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={4}\n className=\"mt-1.5 resize-none\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-3 bg-green-500/10 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-3 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm\">{t(\"error\")}</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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n <>\n <Send className=\"w-4 h-4 mr-2\" />\n {t(\"submit\")}\n </>\n )}\n </Button>\n </form>\n\n {/* Social Links */}\n {socialLinks.length > 0 && (\n <div className=\"mt-10 pt-6 border-t\">\n <p className=\"text-sm text-muted-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-full border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right Side - Map */}\n <div className=\"w-full lg:w-1/2 h-[400px] lg:h-[calc(100vh-4rem)] relative\">\n <GoogleMap\n latitude={mapLatitude}\n longitude={mapLongitude}\n zoom={14}\n height=\"100%\"\n className=\"rounded-none border-0 h-full\"\n title={t(\"mapTitle\")}\n />\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default ContactPageMapSplit;\n"
34
34
  },
35
35
  {
36
36
  "path": "contact-page-map-split/lang/en.json",
@@ -48,7 +48,8 @@
48
48
  "exports": {
49
49
  "types": [],
50
50
  "variables": [
51
- "ContactPageMapSplit"
51
+ "ContactPageMapSplit",
52
+ "default"
52
53
  ]
53
54
  }
54
55
  }
@@ -24,19 +24,19 @@
24
24
  "path": "contact-page-split/contact-page-split.tsx",
25
25
  "type": "registry:component",
26
26
  "target": "$modules$/contact-page-split/contact-page-split.tsx",
27
- "content": "import React, { useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { Mail, Phone, MapPin, Clock } from \"lucide-react\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { useApiService } from \"@/lib/api\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactPageSplitProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactPageSplit({ className }: ContactPageSplitProps) {\r\n const { t } = useTranslation(\"contact-page-split\");\r\n const apiService = useApiService();\r\n\r\n const [formData, setFormData] = useState({\r\n name: \"\",\r\n email: \"\",\r\n phone: \"\",\r\n message: \"\",\r\n });\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [submitStatus, setSubmitStatus] = useState<\"idle\" | \"success\" | \"error\">(\"idle\");\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n setSubmitStatus(\"idle\");\r\n\r\n try {\r\n await apiService.submitForm(\r\n formData,\r\n {\r\n email_subject1: \"Thank you for contacting us\",\r\n email_subject2: \"New Contact Form Submission\",\r\n fields: [\r\n { name: \"name\", required: true },\r\n { name: \"email\", required: true },\r\n { name: \"phone\", required: false },\r\n { name: \"message\", required: true },\r\n ],\r\n },\r\n constants.site.defaultLanguage\r\n );\r\n\r\n setSubmitStatus(\"success\");\r\n setFormData({ name: \"\", email: \"\", phone: \"\", message: \"\" });\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } catch {\r\n setSubmitStatus(\"error\");\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\r\n setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));\r\n };\r\n\r\n return (\r\n <Layout>\r\n <div className={cn(\"min-h-screen\", className)}>\r\n <div className=\"grid lg:grid-cols-2 min-h-screen\">\r\n {/* Left Side - Info & Image */}\r\n <div className=\"relative bg-primary text-primary-foreground p-8 lg:p-12 flex flex-col justify-center\">\r\n {/* Background Pattern */}\r\n <div className=\"absolute inset-0 bg-[linear-gradient(to_right,rgba(255,255,255,0.1)_1px,transparent_1px),linear-gradient(to_bottom,rgba(255,255,255,0.1)_1px,transparent_1px)] bg-[size:4rem_4rem]\" />\r\n\r\n <div className=\"relative z-10 max-w-lg\">\r\n <h1 className=\"text-3xl lg:text-4xl font-bold mb-4\">\r\n {t(\"title\", \"Let's Start a Conversation\")}\r\n </h1>\r\n <p className=\"text-primary-foreground/80 mb-8\">\r\n {t(\"subtitle\", \"Have a project in mind? We'd love to hear about it. Get in touch and let's create something amazing together.\")}\r\n </p>\r\n\r\n <div className=\"space-y-6\">\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Mail className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"emailLabel\", \"Email\")}</p>\r\n <p className=\"text-primary-foreground/70\">{constants.email || \"hello@example.com\"}</p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Phone className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"phoneLabel\", \"Phone\")}</p>\r\n <p className=\"text-primary-foreground/70\">{constants.phone || \"+1 234 567 890\"}</p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <MapPin className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"addressLabel\", \"Address\")}</p>\r\n <p className=\"text-primary-foreground/70\">\r\n {constants.address?.line1 || \"123 Main Street\"}<br />\r\n {constants.address?.city || \"New York\"}, {constants.address?.country || \"USA\"}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Clock className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"hoursLabel\", \"Business Hours\")}</p>\r\n <p className=\"text-primary-foreground/70\">{t(\"hours\", \"Mon - Fri: 9:00 AM - 6:00 PM\")}</p>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Right Side - Form */}\r\n <div className=\"p-8 lg:p-12 flex items-center justify-center bg-background\">\r\n <div className=\"w-full max-w-md\">\r\n <h2 className=\"text-2xl font-bold mb-2\">{t(\"formTitle\", \"Send us a message\")}</h2>\r\n <p className=\"text-muted-foreground mb-8\">\r\n {t(\"formSubtitle\", \"Fill out the form below and we'll get back to you as soon as possible.\")}\r\n </p>\r\n\r\n <form onSubmit={handleSubmit} className=\"space-y-5\">\r\n <div>\r\n <Label htmlFor=\"name\">{t(\"nameLabel\", \"Full Name\")} *</Label>\r\n <Input\r\n id=\"name\"\r\n name=\"name\"\r\n value={formData.name}\r\n onChange={handleChange}\r\n placeholder={t(\"namePlaceholder\", \"John Doe\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"email\">{t(\"emailInputLabel\", \"Email\")} *</Label>\r\n <Input\r\n id=\"email\"\r\n name=\"email\"\r\n type=\"email\"\r\n value={formData.email}\r\n onChange={handleChange}\r\n placeholder={t(\"emailPlaceholder\", \"john@example.com\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"phone\">{t(\"phoneInputLabel\", \"Phone\")}</Label>\r\n <Input\r\n id=\"phone\"\r\n name=\"phone\"\r\n type=\"tel\"\r\n value={formData.phone}\r\n onChange={handleChange}\r\n placeholder={t(\"phonePlaceholder\", \"+1 234 567 890\")}\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"message\">{t(\"messageLabel\", \"Message\")} *</Label>\r\n <Textarea\r\n id=\"message\"\r\n name=\"message\"\r\n value={formData.message}\r\n onChange={handleChange}\r\n placeholder={t(\"messagePlaceholder\", \"Tell us about your project...\")}\r\n required\r\n rows={5}\r\n className=\"mt-1 resize-none\"\r\n />\r\n </div>\r\n\r\n {submitStatus === \"success\" && (\r\n <div className=\"p-4 bg-green-500/10 border border-green-500/30 rounded-lg\">\r\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\r\n {t(\"success\", \"Message sent! We'll be in touch soon.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {submitStatus === \"error\" && (\r\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\r\n <p className=\"text-destructive text-sm font-medium\">\r\n {t(\"error\", \"Something went wrong. Please try again.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button type=\"submit\" size=\"lg\" className=\"w-full\" disabled={isSubmitting}>\r\n {isSubmitting ? t(\"sending\", \"Sending...\") : t(\"submit\", \"Send Message\")}\r\n </Button>\r\n </form>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n"
27
+ "content": "import React, { useState } from \"react\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\nimport { Mail, Phone, MapPin, Clock } from \"lucide-react\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { useApiService } from \"@/lib/api\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport constants from \"@/constants/constants.json\";\r\n\r\ninterface ContactPageSplitProps {\r\n className?: string;\r\n}\r\n\r\nexport function ContactPageSplit({ className }: ContactPageSplitProps) {\r\n const { t } = useTranslation(\"contact-page-split\");\r\n usePageTitle({ title: t(\"pageTitle\", \"Contact Us\") });\r\n const apiService = useApiService();\r\n\r\n const [formData, setFormData] = useState({\r\n name: \"\",\r\n email: \"\",\r\n phone: \"\",\r\n message: \"\",\r\n });\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [submitStatus, setSubmitStatus] = useState<\"idle\" | \"success\" | \"error\">(\"idle\");\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n setSubmitStatus(\"idle\");\r\n\r\n try {\r\n await apiService.submitForm(\r\n formData,\r\n {\r\n email_subject1: \"Thank you for contacting us\",\r\n email_subject2: \"New Contact Form Submission\",\r\n fields: [\r\n { name: \"name\", required: true },\r\n { name: \"email\", required: true },\r\n { name: \"phone\", required: false },\r\n { name: \"message\", required: true },\r\n ],\r\n },\r\n constants.site.defaultLanguage\r\n );\r\n\r\n setSubmitStatus(\"success\");\r\n setFormData({ name: \"\", email: \"\", phone: \"\", message: \"\" });\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } catch {\r\n setSubmitStatus(\"error\");\r\n setTimeout(() => setSubmitStatus(\"idle\"), 5000);\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n };\r\n\r\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\r\n setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));\r\n };\r\n\r\n return (\r\n <Layout>\r\n <div className={cn(\"min-h-[calc(100vh-200px)] py-8 md:py-12\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"grid lg:grid-cols-2 h-full rounded-xl overflow-hidden shadow-lg\">\r\n {/* Left Side - Info & Image */}\r\n <div className=\"relative bg-primary text-primary-foreground p-8 lg:p-12 flex flex-col justify-center\">\r\n {/* Background Pattern */}\r\n <div className=\"absolute inset-0 bg-[linear-gradient(to_right,rgba(255,255,255,0.1)_1px,transparent_1px),linear-gradient(to_bottom,rgba(255,255,255,0.1)_1px,transparent_1px)] bg-[size:4rem_4rem]\" />\r\n\r\n <div className=\"relative z-10 max-w-lg\">\r\n <h1 className=\"text-3xl lg:text-4xl font-bold mb-4\">\r\n {t(\"title\", \"Let's Start a Conversation\")}\r\n </h1>\r\n <p className=\"text-primary-foreground/80 mb-8\">\r\n {t(\"subtitle\", \"Have a project in mind? We'd love to hear about it. Get in touch and let's create something amazing together.\")}\r\n </p>\r\n\r\n <div className=\"space-y-6\">\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Mail className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"emailLabel\", \"Email\")}</p>\r\n <p className=\"text-primary-foreground/70\">{constants.email || \"hello@example.com\"}</p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Phone className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"phoneLabel\", \"Phone\")}</p>\r\n <p className=\"text-primary-foreground/70\">{constants.phone || \"+1 234 567 890\"}</p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <MapPin className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"addressLabel\", \"Address\")}</p>\r\n <p className=\"text-primary-foreground/70\">\r\n {constants.address?.line1 || \"123 Main Street\"}<br />\r\n {constants.address?.city || \"New York\"}, {constants.address?.country || \"USA\"}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-start gap-4\">\r\n <div className=\"w-10 h-10 rounded-full bg-primary-foreground/10 flex items-center justify-center flex-shrink-0\">\r\n <Clock className=\"h-5 w-5\" />\r\n </div>\r\n <div>\r\n <p className=\"font-semibold\">{t(\"hoursLabel\", \"Business Hours\")}</p>\r\n <p className=\"text-primary-foreground/70\">{t(\"hours\", \"Mon - Fri: 9:00 AM - 6:00 PM\")}</p>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {/* Right Side - Form */}\r\n <div className=\"p-8 lg:p-12 flex items-center justify-center bg-background\">\r\n <div className=\"w-full max-w-md\">\r\n <h2 className=\"text-2xl font-bold mb-2\">{t(\"formTitle\", \"Send us a message\")}</h2>\r\n <p className=\"text-muted-foreground mb-8\">\r\n {t(\"formSubtitle\", \"Fill out the form below and we'll get back to you as soon as possible.\")}\r\n </p>\r\n\r\n <form onSubmit={handleSubmit} className=\"space-y-5\">\r\n <div>\r\n <Label htmlFor=\"name\">{t(\"nameLabel\", \"Full Name\")} *</Label>\r\n <Input\r\n id=\"name\"\r\n name=\"name\"\r\n value={formData.name}\r\n onChange={handleChange}\r\n placeholder={t(\"namePlaceholder\", \"John Doe\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"email\">{t(\"emailInputLabel\", \"Email\")} *</Label>\r\n <Input\r\n id=\"email\"\r\n name=\"email\"\r\n type=\"email\"\r\n value={formData.email}\r\n onChange={handleChange}\r\n placeholder={t(\"emailPlaceholder\", \"john@example.com\")}\r\n required\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"phone\">{t(\"phoneInputLabel\", \"Phone\")}</Label>\r\n <Input\r\n id=\"phone\"\r\n name=\"phone\"\r\n type=\"tel\"\r\n value={formData.phone}\r\n onChange={handleChange}\r\n placeholder={t(\"phonePlaceholder\", \"+1 234 567 890\")}\r\n className=\"mt-1\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <Label htmlFor=\"message\">{t(\"messageLabel\", \"Message\")} *</Label>\r\n <Textarea\r\n id=\"message\"\r\n name=\"message\"\r\n value={formData.message}\r\n onChange={handleChange}\r\n placeholder={t(\"messagePlaceholder\", \"Tell us about your project...\")}\r\n required\r\n rows={5}\r\n className=\"mt-1 resize-none\"\r\n />\r\n </div>\r\n\r\n {submitStatus === \"success\" && (\r\n <div className=\"p-4 bg-green-500/10 border border-green-500/30 rounded-lg\">\r\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\r\n {t(\"success\", \"Message sent! We'll be in touch soon.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {submitStatus === \"error\" && (\r\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\r\n <p className=\"text-destructive text-sm font-medium\">\r\n {t(\"error\", \"Something went wrong. Please try again.\")}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Button type=\"submit\" size=\"lg\" className=\"w-full\" disabled={isSubmitting}>\r\n {isSubmitting ? t(\"sending\", \"Sending...\") : t(\"submit\", \"Send Message\")}\r\n </Button>\r\n </form>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n\r\nexport default ContactPageSplit;\r\n"
28
28
  },
29
29
  {
30
30
  "path": "contact-page-split/lang/en.json",
31
31
  "type": "registry:lang",
32
32
  "target": "$modules$/contact-page-split/lang/en.json",
33
- "content": "{\r\n \"title\": \"Let's Start a Conversation\",\r\n \"subtitle\": \"AI will customize this subtitle based on your contact page purpose. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"Email\",\r\n \"phoneLabel\": \"Phone\",\r\n \"addressLabel\": \"Address\",\r\n \"hoursLabel\": \"Hours\",\r\n \"hours\": \"Mon - Fri: 9:00 AM - 6:00 PM\",\r\n \"formTitle\": \"Send us a message\",\r\n \"formSubtitle\": \"Fill out the form below and we'll get back to you as soon as possible.\",\r\n \"nameLabel\": \"Full Name\",\r\n \"namePlaceholder\": \"John Doe\",\r\n \"emailInputLabel\": \"Email\",\r\n \"emailPlaceholder\": \"john@example.com\",\r\n \"phoneInputLabel\": \"Phone\",\r\n \"phonePlaceholder\": \"+1 234 567 890\",\r\n \"messageLabel\": \"Message\",\r\n \"messagePlaceholder\": \"Tell us about your project...\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Message sent! We'll be in touch soon.\",\r\n \"error\": \"Something went wrong. Please try again.\"\r\n}\r\n"
33
+ "content": "{\r\n \"pageTitle\": \"Contact Us\",\r\n \"title\": \"Let's Start a Conversation\",\r\n \"subtitle\": \"Ask Promake to customize this subtitle based on your contact page purpose. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"Email\",\r\n \"phoneLabel\": \"Phone\",\r\n \"addressLabel\": \"Address\",\r\n \"hoursLabel\": \"Hours\",\r\n \"hours\": \"Mon - Fri: 9:00 AM - 6:00 PM\",\r\n \"formTitle\": \"Send us a message\",\r\n \"formSubtitle\": \"Fill out the form below and we'll get back to you as soon as possible.\",\r\n \"nameLabel\": \"Full Name\",\r\n \"namePlaceholder\": \"John Doe\",\r\n \"emailInputLabel\": \"Email\",\r\n \"emailPlaceholder\": \"john@example.com\",\r\n \"phoneInputLabel\": \"Phone\",\r\n \"phonePlaceholder\": \"+1 234 567 890\",\r\n \"messageLabel\": \"Message\",\r\n \"messagePlaceholder\": \"Tell us about your project...\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Message sent! We'll be in touch soon.\",\r\n \"error\": \"Something went wrong. Please try again.\"\r\n}\r\n"
34
34
  },
35
35
  {
36
36
  "path": "contact-page-split/lang/tr.json",
37
37
  "type": "registry:lang",
38
38
  "target": "$modules$/contact-page-split/lang/tr.json",
39
- "content": "{\r\n \"title\": \"Bir Sohbet Başlatalım\",\r\n \"subtitle\": \"AI bu alt başlığı iletişim sayfası amacınıza göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"E-posta\",\r\n \"phoneLabel\": \"Telefon\",\r\n \"addressLabel\": \"Adres\",\r\n \"hoursLabel\": \"Çalışma Saatleri\",\r\n \"hours\": \"Pazartesi - Cuma: 09:00 - 18:00\",\r\n \"formTitle\": \"Bize mesaj gönderin\",\r\n \"formSubtitle\": \"Aşağıdaki formu doldurun, en kısa sürede size döneceğiz.\",\r\n \"nameLabel\": \"Ad Soyad\",\r\n \"namePlaceholder\": \"Ahmet Yılmaz\",\r\n \"emailInputLabel\": \"E-posta\",\r\n \"emailPlaceholder\": \"ahmet@ornek.com\",\r\n \"phoneInputLabel\": \"Telefon\",\r\n \"phonePlaceholder\": \"+90 532 123 4567\",\r\n \"messageLabel\": \"Mesaj\",\r\n \"messagePlaceholder\": \"Projeniz hakkında bize bilgi verin...\",\r\n \"submit\": \"Mesaj Gönder\",\r\n \"sending\": \"Gönderiliyor...\",\r\n \"success\": \"Mesaj gönderildi! En kısa sürede iletişime geçeceğiz.\",\r\n \"error\": \"Bir şeyler yanlış gitti. Lütfen tekrar deneyin.\"\r\n}\r\n"
39
+ "content": "{\r\n \"pageTitle\": \"İletişim\",\r\n \"title\": \"Bir Sohbet Başlatalım\",\r\n \"subtitle\": \"AI bu alt başlığı iletişim sayfası amacınıza göre özelleştirecektir. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"emailLabel\": \"E-posta\",\r\n \"phoneLabel\": \"Telefon\",\r\n \"addressLabel\": \"Adres\",\r\n \"hoursLabel\": \"Çalışma Saatleri\",\r\n \"hours\": \"Pazartesi - Cuma: 09:00 - 18:00\",\r\n \"formTitle\": \"Bize mesaj gönderin\",\r\n \"formSubtitle\": \"Aşağıdaki formu doldurun, en kısa sürede size döneceğiz.\",\r\n \"nameLabel\": \"Ad Soyad\",\r\n \"namePlaceholder\": \"Ahmet Yılmaz\",\r\n \"emailInputLabel\": \"E-posta\",\r\n \"emailPlaceholder\": \"ahmet@ornek.com\",\r\n \"phoneInputLabel\": \"Telefon\",\r\n \"phonePlaceholder\": \"+90 532 123 4567\",\r\n \"messageLabel\": \"Mesaj\",\r\n \"messagePlaceholder\": \"Projeniz hakkında bize bilgi verin...\",\r\n \"submit\": \"Mesaj Gönder\",\r\n \"sending\": \"Gönderiliyor...\",\r\n \"success\": \"Mesaj gönderildi! En kısa sürede iletişime geçeceğiz.\",\r\n \"error\": \"Bir şeyler yanlış gitti. Lütfen tekrar deneyin.\"\r\n}\r\n"
40
40
  }
41
41
  ],
42
42
  "exports": {
@@ -3,7 +3,9 @@
3
3
  "type": "registry:page",
4
4
  "title": "Contact Page",
5
5
  "description": "Contact page with two-column layout: contact form (name, email, subject, message) on left, and contact information (address, phone, email, business hours, social links) on right. Includes embedded Google Map, form validation, and success/error toast notifications. Mobile-responsive with stacked layout.",
6
- "registryDependencies": [],
6
+ "registryDependencies": [
7
+ "animations"
8
+ ],
7
9
  "usage": "import { ContactPage } from '@/modules/contact-page';\n\n<Route path=\"/contact\" element={<ContactPage />} />\n\n• Two-column: form left, info right\n• Form: name, email, subject, message\n• Info: address, phone, email, hours, map\n• Toast notifications on submit",
8
10
  "route": {
9
11
  "path": "/contact",
@@ -20,13 +22,13 @@
20
22
  "path": "contact-page/contact-page.tsx",
21
23
  "type": "registry:page",
22
24
  "target": "$modules$/contact-page/contact-page.tsx",
23
- "content": "import React, { useState, useMemo } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n Mail,\n Phone,\n MapPin,\n MessageSquare,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { FadeIn, SlideInLeft, SlideInRight } from \"@/modules/animations\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPage() {\n const { t } = useTranslation(\"contact-page\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n subject: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"phone\", required: false },\n { name: \"subject\", required: false },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n phone: \"\",\n subject: \"\",\n message: \"\",\n });\n\n setTimeout(() => {\n setSubmitStatus(\"idle\");\n }, 5000);\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n\n setTimeout(() => {\n setSubmitStatus(\"idle\");\n }, 5000);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<\n HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement\n >\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\n }));\n };\n\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"container mx-auto px-4 max-w-6xl\">\n {/* Hero Section */}\n <FadeIn className=\"text-center mb-12\">\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\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-3xl mx-auto\">\n {t(\"description\")}\n </p>\n </FadeIn>\n\n <div className=\"grid lg:grid-cols-3 gap-8\">\n {/* Contact Information */}\n <SlideInLeft className=\"lg:col-span-1 space-y-6\">\n {/* Company Info */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-5 h-5 text-primary\" />\n {t(\"getInTouch\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-start gap-3\">\n <Mail className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"email\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.email}</p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\"emailResponse\")}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <Phone className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"phone\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.phone}</p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <MapPin className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"address\")}\n </p>\n <div className=\"text-muted-foreground\">\n <p>{constants.address.line1}</p>\n {constants.address.line2 && (\n <p>{constants.address.line2}</p>\n )}\n <p>\n {constants.address.city}, {constants.address.state}{\" \"}\n {constants.address.postalCode}\n </p>\n {constants.address.country && (\n <p>{constants.address.country}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Social Media Links */}\n {socialLinks.length > 0 && (\n <div className=\"pt-4 border-t\">\n <p className=\"font-medium text-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-md border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-5 w-5\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Support Info */}\n <Card>\n <CardHeader>\n <CardTitle>{t(\"needSupport\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-muted-foreground mb-4\">\n {t(\"supportDescription\")}\n </p>\n <div className=\"space-y-2\">\n <p className=\"font-medium text-foreground\">\n {t(\"supportEmail\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.email}</p>\n </div>\n </CardContent>\n </Card>\n </SlideInLeft>\n\n {/* Contact Form */}\n <SlideInRight className=\"lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>{t(\"sendMessage\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Full Name */}\n <div>\n <Label htmlFor=\"name\">{t(\"fullName\")} *</Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1\"\n />\n </div>\n\n {/* Email */}\n <div>\n <Label htmlFor=\"email\">{t(\"emailAddress\")} *</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1\"\n />\n </div>\n\n {/* Phone */}\n <div>\n <Label htmlFor=\"phone\">{t(\"phoneNumber\")}</Label>\n <Input\n id=\"phone\"\n name=\"phone\"\n type=\"tel\"\n value={formData.phone}\n onChange={handleChange}\n placeholder={t(\"phonePlaceholder\")}\n className=\"mt-1\"\n />\n </div>\n\n {/* Subject */}\n <div>\n <Label htmlFor=\"subject\">{t(\"subject\")}</Label>\n <Input\n id=\"subject\"\n name=\"subject\"\n type=\"text\"\n value={formData.subject}\n onChange={handleChange}\n placeholder={t(\"subjectPlaceholder\")}\n className=\"mt-1\"\n />\n </div>\n\n {/* Message */}\n <div>\n <Label htmlFor=\"message\">{t(\"message\")} *</Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={6}\n className=\"mt-1 resize-none\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-4 bg-green-500/10 dark:bg-green-500/20 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm font-medium\">\n {t(\"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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n t(\"submit\")\n )}\n </Button>\n </form>\n </CardContent>\n </Card>\n </SlideInRight>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n"
25
+ "content": "import React, { useState, useMemo } from \"react\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\nimport { useTranslation } from \"react-i18next\";\nimport { Layout } from \"@/components/Layout\";\nimport { useApiService } from \"@/lib/api\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Label } from \"@/components/ui/label\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n Mail,\n Phone,\n MapPin,\n MessageSquare,\n Facebook,\n Twitter,\n Instagram,\n Linkedin,\n} from \"lucide-react\";\nimport constants from \"@/constants/constants.json\";\nimport { FadeIn, SlideInLeft, SlideInRight } from \"@/modules/animations\";\n\nconst socialIcons: Record<string, React.ElementType> = {\n facebook: Facebook,\n twitter: Twitter,\n instagram: Instagram,\n linkedin: Linkedin,\n};\n\nexport function ContactPage() {\n const { t } = useTranslation(\"contact-page\");\n usePageTitle({ title: t(\"title\") });\n\n const apiService = useApiService();\n\n const socialLinks = useMemo(() => {\n const socialMedia = constants.socialMedia as\n | Record<string, string>\n | undefined;\n if (!socialMedia) return [];\n return Object.entries(socialMedia)\n .filter(([platform, url]) => url && socialIcons[platform])\n .map(([platform, url]) => ({\n platform,\n url,\n Icon: socialIcons[platform],\n }));\n }, []);\n\n const [formData, setFormData] = useState({\n name: \"\",\n email: \"\",\n phone: \"\",\n subject: \"\",\n message: \"\",\n });\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [submitStatus, setSubmitStatus] = useState<\n \"idle\" | \"success\" | \"error\"\n >(\"idle\");\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n setSubmitStatus(\"idle\");\n\n try {\n const currentLanguage = constants.site.defaultLanguage;\n\n await apiService.submitForm(\n formData,\n {\n email_subject1: \"Thank you for contacting us\",\n email_subject2: \"New Contact Form Submission\",\n fields: [\n { name: \"name\", required: true },\n { name: \"email\", required: true },\n { name: \"phone\", required: false },\n { name: \"subject\", required: false },\n { name: \"message\", required: true },\n ],\n },\n currentLanguage\n );\n\n setSubmitStatus(\"success\");\n setFormData({\n name: \"\",\n email: \"\",\n phone: \"\",\n subject: \"\",\n message: \"\",\n });\n\n setTimeout(() => {\n setSubmitStatus(\"idle\");\n }, 5000);\n } catch (error: unknown) {\n console.error(\"Form submission failed:\", error);\n setSubmitStatus(\"error\");\n\n setTimeout(() => {\n setSubmitStatus(\"idle\");\n }, 5000);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleChange = (\n e: React.ChangeEvent<\n HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement\n >\n ) => {\n setFormData((prev) => ({\n ...prev,\n [e.target.name]: e.target.value,\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 max-w-6xl\">\n {/* Hero Section */}\n <FadeIn className=\"text-center mb-12\">\n <h1 className=\"text-4xl font-bold text-foreground mb-4\">\n {t(\"title\")}\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-3xl mx-auto\">\n {t(\"description\")}\n </p>\n </FadeIn>\n\n <div className=\"grid lg:grid-cols-3 gap-8\">\n {/* Contact Information */}\n <SlideInLeft className=\"lg:col-span-1 space-y-6\">\n {/* Company Info */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-5 h-5 text-primary\" />\n {t(\"getInTouch\")}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-start gap-3\">\n <Mail className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"email\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.email}</p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\"emailResponse\")}\n </p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <Phone className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"phone\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.phone}</p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <MapPin className=\"w-5 h-5 text-primary mt-1\" />\n <div>\n <p className=\"font-medium text-foreground\">\n {t(\"address\")}\n </p>\n <div className=\"text-muted-foreground\">\n <p>{constants.address.line1}</p>\n {constants.address.line2 && (\n <p>{constants.address.line2}</p>\n )}\n <p>\n {constants.address.city}, {constants.address.state}{\" \"}\n {constants.address.postalCode}\n </p>\n {constants.address.country && (\n <p>{constants.address.country}</p>\n )}\n </div>\n </div>\n </div>\n\n {/* Social Media Links */}\n {socialLinks.length > 0 && (\n <div className=\"pt-4 border-t\">\n <p className=\"font-medium text-foreground mb-3\">\n {t(\"followUs\")}\n </p>\n <div className=\"flex gap-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-md border text-muted-foreground hover:text-primary hover:border-primary transition-colors\"\n >\n <Icon className=\"h-5 w-5\" />\n </a>\n ))}\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Support Info */}\n <Card>\n <CardHeader>\n <CardTitle>{t(\"needSupport\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-muted-foreground mb-4\">\n {t(\"supportDescription\")}\n </p>\n <div className=\"space-y-2\">\n <p className=\"font-medium text-foreground\">\n {t(\"supportEmail\")}\n </p>\n <p className=\"text-muted-foreground\">{constants.email}</p>\n </div>\n </CardContent>\n </Card>\n </SlideInLeft>\n\n {/* Contact Form */}\n <SlideInRight className=\"lg:col-span-2\">\n <Card>\n <CardHeader>\n <CardTitle>{t(\"sendMessage\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Full Name */}\n <div>\n <Label htmlFor=\"name\">{t(\"fullName\")} *</Label>\n <Input\n id=\"name\"\n name=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={handleChange}\n placeholder={t(\"fullNamePlaceholder\")}\n required\n className=\"mt-1\"\n />\n </div>\n\n {/* Email */}\n <div>\n <Label htmlFor=\"email\">{t(\"emailAddress\")} *</Label>\n <Input\n id=\"email\"\n name=\"email\"\n type=\"email\"\n value={formData.email}\n onChange={handleChange}\n placeholder={t(\"emailPlaceholder\")}\n required\n className=\"mt-1\"\n />\n </div>\n\n {/* Phone */}\n <div>\n <Label htmlFor=\"phone\">{t(\"phoneNumber\")}</Label>\n <Input\n id=\"phone\"\n name=\"phone\"\n type=\"tel\"\n value={formData.phone}\n onChange={handleChange}\n placeholder={t(\"phonePlaceholder\")}\n className=\"mt-1\"\n />\n </div>\n\n {/* Subject */}\n <div>\n <Label htmlFor=\"subject\">{t(\"subject\")}</Label>\n <Input\n id=\"subject\"\n name=\"subject\"\n type=\"text\"\n value={formData.subject}\n onChange={handleChange}\n placeholder={t(\"subjectPlaceholder\")}\n className=\"mt-1\"\n />\n </div>\n\n {/* Message */}\n <div>\n <Label htmlFor=\"message\">{t(\"message\")} *</Label>\n <Textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n placeholder={t(\"messagePlaceholder\")}\n required\n rows={6}\n className=\"mt-1 resize-none\"\n />\n </div>\n\n {submitStatus === \"success\" && (\n <div className=\"p-4 bg-green-500/10 dark:bg-green-500/20 border border-green-500/30 rounded-lg\">\n <p className=\"text-green-600 dark:text-green-400 text-sm font-medium\">\n {t(\"success\")}\n </p>\n </div>\n )}\n\n {submitStatus === \"error\" && (\n <div className=\"p-4 bg-destructive/10 border border-destructive/30 rounded-lg\">\n <p className=\"text-destructive text-sm font-medium\">\n {t(\"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-primary-foreground border-t-transparent rounded-full animate-spin mr-2\" />\n {t(\"sending\")}\n </>\n ) : (\n t(\"submit\")\n )}\n </Button>\n </form>\n </CardContent>\n </Card>\n </SlideInRight>\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default ContactPage;\n"
24
26
  },
25
27
  {
26
28
  "path": "contact-page/lang/en.json",
27
29
  "type": "registry:lang",
28
30
  "target": "$modules$/contact-page/lang/en.json",
29
- "content": "{\r\n \"title\": \"Contact Us\",\r\n \"getInTouch\": \"Get in Touch\",\r\n \"emailUs\": \"Email Us\",\r\n \"callUs\": \"Call Us\",\r\n \"visitUs\": \"Visit Us\",\r\n \"businessHours\": \"Hours\",\r\n \"sendMessage\": \"Send us a Message\",\r\n \"formNotAvailable\": \"Form is not available at the moment.\",\r\n \"fullName\": \"Full Name\",\r\n \"emailAddress\": \"Email Address\",\r\n \"phoneNumber\": \"Phone Number\",\r\n \"subject\": \"Subject\",\r\n \"message\": \"Message\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Thank you for your message! We will get back to you soon.\",\r\n \"error\": \"Failed to send message. Please try again later.\",\r\n \"description\": \"AI will customize this contact page description based on your site and support options. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"email\": \"Email\",\r\n \"phone\": \"Phone\",\r\n \"address\": \"Address\",\r\n \"fullNamePlaceholder\": \"Your full name\",\r\n \"emailPlaceholder\": \"your@email.com\",\r\n \"phonePlaceholder\": \"+1 (555) 123-4567\",\r\n \"subjectPlaceholder\": \"What is this regarding?\",\r\n \"messagePlaceholder\": \"Tell us how we can help you...\",\r\n \"loading\": \"Loading contact information...\",\r\n \"emailResponse\": \"We typically respond within 24 hours\",\r\n \"needSupport\": \"Need Support?\",\r\n \"supportDescription\": \"For technical support or general inquiries, contact our dedicated support team:\",\r\n \"supportEmail\": \"Support Email\",\r\n \"monday_friday\": \"Monday - Friday\",\r\n \"saturday\": \"Saturday\",\r\n \"sunday\": \"Sunday\",\r\n \"closed\": \"Closed\",\r\n \"am\": \"AM\",\r\n \"pm\": \"PM\"\r\n}\r\n"
31
+ "content": "{\r\n \"title\": \"Contact Us\",\r\n \"getInTouch\": \"Get in Touch\",\r\n \"emailUs\": \"Email Us\",\r\n \"callUs\": \"Call Us\",\r\n \"visitUs\": \"Visit Us\",\r\n \"businessHours\": \"Hours\",\r\n \"sendMessage\": \"Send us a Message\",\r\n \"formNotAvailable\": \"Form is not available at the moment.\",\r\n \"fullName\": \"Full Name\",\r\n \"emailAddress\": \"Email Address\",\r\n \"phoneNumber\": \"Phone Number\",\r\n \"subject\": \"Subject\",\r\n \"message\": \"Message\",\r\n \"submit\": \"Send Message\",\r\n \"sending\": \"Sending...\",\r\n \"success\": \"Thank you for your message! We will get back to you soon.\",\r\n \"error\": \"Failed to send message. Please try again later.\",\r\n \"description\": \"Ask Promake to customize this contact page description based on your site and support options. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\",\r\n \"email\": \"Email\",\r\n \"phone\": \"Phone\",\r\n \"address\": \"Address\",\r\n \"fullNamePlaceholder\": \"Your full name\",\r\n \"emailPlaceholder\": \"your@email.com\",\r\n \"phonePlaceholder\": \"+1 (555) 123-4567\",\r\n \"subjectPlaceholder\": \"What is this regarding?\",\r\n \"messagePlaceholder\": \"Tell us how we can help you...\",\r\n \"loading\": \"Loading contact information...\",\r\n \"emailResponse\": \"We typically respond within 24 hours\",\r\n \"needSupport\": \"Need Support?\",\r\n \"supportDescription\": \"For technical support or general inquiries, contact our dedicated support team:\",\r\n \"supportEmail\": \"Support Email\",\r\n \"monday_friday\": \"Monday - Friday\",\r\n \"saturday\": \"Saturday\",\r\n \"sunday\": \"Sunday\",\r\n \"closed\": \"Closed\",\r\n \"am\": \"AM\",\r\n \"pm\": \"PM\"\r\n}\r\n"
30
32
  },
31
33
  {
32
34
  "path": "contact-page/lang/tr.json",