@jhits/plugin-website 0.0.15 → 0.0.17

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 (70) hide show
  1. package/dist/131._entry.js +2 -0
  2. package/dist/131._entry.js.LICENSE.txt +6 -0
  3. package/dist/303._entry.js +1 -0
  4. package/dist/_entry.js +2 -0
  5. package/dist/_entry.js.LICENSE.txt +6 -0
  6. package/dist/api/handler.d.ts.map +1 -1
  7. package/dist/api/handler.js +16 -31
  8. package/dist/remoteEntry.js +1 -0
  9. package/dist/server.js +232 -0
  10. package/dist/types/settings.d.ts +18 -11
  11. package/dist/types/settings.d.ts.map +1 -1
  12. package/dist/views/Settings/components/ContactInfoSection.d.ts +12 -0
  13. package/dist/views/Settings/components/ContactInfoSection.d.ts.map +1 -0
  14. package/dist/views/Settings/components/ContactInfoSection.js +14 -0
  15. package/dist/views/Settings/components/DomainLanguagesCard.d.ts +8 -0
  16. package/dist/views/Settings/components/DomainLanguagesCard.d.ts.map +1 -0
  17. package/dist/views/Settings/components/DomainLanguagesCard.js +12 -0
  18. package/dist/views/Settings/components/DomainLanguagesModal.d.ts +12 -0
  19. package/dist/views/Settings/components/DomainLanguagesModal.d.ts.map +1 -0
  20. package/dist/views/Settings/components/DomainLanguagesModal.js +38 -0
  21. package/dist/views/Settings/components/FlagIcon.d.ts +6 -0
  22. package/dist/views/Settings/components/FlagIcon.d.ts.map +1 -0
  23. package/dist/views/Settings/components/FlagIcon.js +11 -0
  24. package/dist/views/Settings/components/LaunchDateSection.d.ts +8 -0
  25. package/dist/views/Settings/components/LaunchDateSection.d.ts.map +1 -0
  26. package/dist/views/Settings/components/LaunchDateSection.js +22 -0
  27. package/dist/views/Settings/components/LocalesManagementCard.d.ts +8 -0
  28. package/dist/views/Settings/components/LocalesManagementCard.d.ts.map +1 -0
  29. package/dist/views/Settings/components/LocalesManagementCard.js +66 -0
  30. package/dist/views/Settings/components/LocalizedContentSection.d.ts +12 -0
  31. package/dist/views/Settings/components/LocalizedContentSection.d.ts.map +1 -0
  32. package/dist/views/Settings/components/LocalizedContentSection.js +17 -0
  33. package/dist/views/Settings/components/SeoIdentitySection.d.ts +9 -0
  34. package/dist/views/Settings/components/SeoIdentitySection.d.ts.map +1 -0
  35. package/dist/views/Settings/components/SeoIdentitySection.js +12 -0
  36. package/dist/views/Settings/components/SocialLinksSection.d.ts +13 -0
  37. package/dist/views/Settings/components/SocialLinksSection.d.ts.map +1 -0
  38. package/dist/views/Settings/components/SocialLinksSection.js +13 -0
  39. package/dist/views/Settings/components/StatusSection.d.ts +8 -0
  40. package/dist/views/Settings/components/StatusSection.d.ts.map +1 -0
  41. package/dist/views/Settings/components/StatusSection.js +27 -0
  42. package/dist/views/Settings/constants.d.ts +12 -0
  43. package/dist/views/Settings/constants.d.ts.map +1 -0
  44. package/dist/views/Settings/constants.js +23 -0
  45. package/dist/views/Settings/hooks/useWebsiteSettings.d.ts +24 -0
  46. package/dist/views/Settings/hooks/useWebsiteSettings.d.ts.map +1 -0
  47. package/dist/views/Settings/hooks/useWebsiteSettings.js +217 -0
  48. package/dist/views/Settings/types.d.ts +5 -0
  49. package/dist/views/Settings/types.d.ts.map +1 -0
  50. package/dist/views/Settings/types.js +1 -0
  51. package/dist/views/SettingsView.d.ts +3 -6
  52. package/dist/views/SettingsView.d.ts.map +1 -1
  53. package/dist/views/SettingsView.js +59 -262
  54. package/package.json +14 -10
  55. package/src/api/handler.ts +42 -34
  56. package/src/types/settings.ts +28 -12
  57. package/src/views/Settings/components/ContactInfoSection.tsx +109 -0
  58. package/src/views/Settings/components/DomainLanguagesCard.tsx +91 -0
  59. package/src/views/Settings/components/DomainLanguagesModal.tsx +216 -0
  60. package/src/views/Settings/components/FlagIcon.tsx +20 -0
  61. package/src/views/Settings/components/LaunchDateSection.tsx +111 -0
  62. package/src/views/Settings/components/LocalesManagementCard.tsx +198 -0
  63. package/src/views/Settings/components/LocalizedContentSection.tsx +155 -0
  64. package/src/views/Settings/components/SeoIdentitySection.tsx +98 -0
  65. package/src/views/Settings/components/SocialLinksSection.tsx +98 -0
  66. package/src/views/Settings/components/StatusSection.tsx +181 -0
  67. package/src/views/Settings/constants.ts +26 -0
  68. package/src/views/Settings/hooks/useWebsiteSettings.ts +240 -0
  69. package/src/views/Settings/types.ts +4 -0
  70. package/src/views/SettingsView.tsx +226 -674
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import { Mail, Smartphone, MapPin, Globe } from 'lucide-react';
4
+ import { WebsiteSettings, LocaleDefinition } from '../../../types/settings';
5
+ import { FlagIcon } from './FlagIcon';
6
+ import { clsx, type ClassValue } from 'clsx';
7
+ import { twMerge } from 'tailwind-merge';
8
+
9
+ function cn(...inputs: ClassValue[]) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ interface ContactInfoSectionProps {
14
+ settings: WebsiteSettings;
15
+ activeTab: string;
16
+ onTabChange: (code: string) => void;
17
+ activeLocales: LocaleDefinition[];
18
+ onUpdate: (updates: Partial<WebsiteSettings>) => void;
19
+ onUpdateLocalized: (field: string, value: string) => void;
20
+ }
21
+
22
+ export function ContactInfoSection({
23
+ settings,
24
+ activeTab,
25
+ onTabChange,
26
+ activeLocales,
27
+ onUpdate,
28
+ onUpdateLocalized
29
+ }: ContactInfoSectionProps) {
30
+ return (
31
+ <motion.section
32
+ initial={{ opacity: 0, y: 20 }}
33
+ animate={{ opacity: 1, y: 0 }}
34
+ className="bg-dashboard-card/50 border border-dashboard-border/40 p-8 rounded-[2rem] space-y-6 relative overflow-hidden"
35
+ >
36
+ <div className="absolute top-0 right-0 w-64 h-64 blur-3xl opacity-5 bg-primary pointer-events-none" />
37
+
38
+ <div className="flex items-center gap-3 relative z-10 border-b border-dashboard-border/40 pb-4">
39
+ <div className="size-8 rounded-lg bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner">
40
+ <Mail size={16} />
41
+ </div>
42
+ <h3 className="text-xl font-bold text-dashboard-text leading-none tracking-tight">
43
+ Core <span className="text-primary">Intelligence</span>
44
+ </h3>
45
+ </div>
46
+
47
+ <div className="flex gap-2 relative z-10 overflow-x-auto pb-2 custom-scrollbar">
48
+ {activeLocales.map((loc) => (
49
+ <button
50
+ key={loc.code}
51
+ onClick={() => onTabChange(loc.code)}
52
+ className={cn(
53
+ "px-4 py-2 rounded-xl text-[10px] font-bold uppercase tracking-wider transition-all flex items-center gap-2 shrink-0 border",
54
+ activeTab === loc.code
55
+ ? "bg-primary text-white border-primary shadow-lg shadow-primary/10"
56
+ : "bg-dashboard-bg/30 text-dashboard-text-secondary/50 border-dashboard-border/40 hover:border-primary/30"
57
+ )}
58
+ >
59
+ <FlagIcon code={loc.code} countryCode={loc.countryCode} />
60
+ <span>{loc.code}</span>
61
+ </button>
62
+ ))}
63
+ </div>
64
+
65
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
66
+ <div className="space-y-2">
67
+ <label className="text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60">Global Sync Email</label>
68
+ <div className="relative group">
69
+ <Globe className="absolute left-5 top-1/2 -translate-y-1/2 size-4 text-dashboard-text-secondary/40 group-focus-within:text-primary transition-colors" />
70
+ <input
71
+ type="email"
72
+ value={settings.contactEmail || ''}
73
+ onChange={(e) => onUpdate({ contactEmail: e.target.value })}
74
+ placeholder="SYNC_EMAIL_ENDPOINT"
75
+ className="w-full pl-12 pr-6 py-3 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-xl text-sm font-bold outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 transition-all text-dashboard-text placeholder:text-dashboard-text-secondary/30"
76
+ />
77
+ </div>
78
+ </div>
79
+ <div className="space-y-2">
80
+ <label className="text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60">Regional Phone ({activeTab})</label>
81
+ <div className="relative group">
82
+ <Smartphone className="absolute left-5 top-1/2 -translate-y-1/2 size-4 text-dashboard-text-secondary/40 group-focus-within:text-primary transition-colors" />
83
+ <input
84
+ type="tel"
85
+ value={settings.localizedContactInfo?.[activeTab]?.phoneNumber || ''}
86
+ onChange={(e) => onUpdateLocalized('phoneNumber', e.target.value)}
87
+ placeholder="INITIALIZE_PHONE"
88
+ className="w-full pl-12 pr-6 py-3 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-xl text-sm font-bold outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 transition-all text-dashboard-text placeholder:text-dashboard-text-secondary/30"
89
+ />
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ <div className="space-y-2 relative z-10">
95
+ <label className="text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60">Geospatial Protocol ({activeTab})</label>
96
+ <div className="relative group">
97
+ <MapPin className="absolute left-5 top-5 size-4 text-dashboard-text-secondary/40 group-focus-within:text-primary transition-colors" />
98
+ <textarea
99
+ value={settings.localizedContactInfo?.[activeTab]?.physicalAddress || ''}
100
+ onChange={(e) => onUpdateLocalized('physicalAddress', e.target.value)}
101
+ placeholder="STREET_ADDRESS_ORCHESTRATION"
102
+ rows={3}
103
+ className="w-full pl-12 pr-6 py-4 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-xl text-sm font-medium leading-relaxed outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 transition-all text-dashboard-text placeholder:text-dashboard-text-secondary/30 resize-none"
104
+ />
105
+ </div>
106
+ </div>
107
+ </motion.section>
108
+ );
109
+ }
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import { Globe2, Edit3, ArrowRight, ShieldCheck } from 'lucide-react';
4
+ import { DomainLocaleConfig } from '../../../types/settings';
5
+ import { FlagIcon } from './FlagIcon';
6
+ import { clsx, type ClassValue } from 'clsx';
7
+ import { twMerge } from 'tailwind-merge';
8
+
9
+ function cn(...inputs: ClassValue[]) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ interface DomainLanguagesCardProps {
14
+ config: DomainLocaleConfig[];
15
+ onEdit: () => void;
16
+ }
17
+
18
+ export function DomainLanguagesCard({ config, onEdit }: DomainLanguagesCardProps) {
19
+ return (
20
+ <section className="space-y-8">
21
+ <div className="flex items-center gap-4">
22
+ <div className="size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner">
23
+ <Globe2 size={20} />
24
+ </div>
25
+ <h3 className="text-2xl lg:text-3xl font-bold text-dashboard-text leading-none tracking-tight">
26
+ Domain <span className="text-primary">Strategy</span>
27
+ </h3>
28
+ </div>
29
+
30
+ <motion.div
31
+ initial={{ opacity: 0, y: 20 }}
32
+ animate={{ opacity: 1, y: 0 }}
33
+ className="bg-dashboard-card/50 border border-dashboard-border/40 p-8 rounded-[2rem] relative overflow-hidden"
34
+ >
35
+ <div className="space-y-6">
36
+ {config && config.length > 0 ? (
37
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
38
+ {config.map((entry, index) => (
39
+ <motion.div
40
+ initial={{ opacity: 0, scale: 0.95 }}
41
+ animate={{ opacity: 1, scale: 1 }}
42
+ transition={{ delay: index * 0.1 }}
43
+ key={index}
44
+ className="flex items-center justify-between p-4 bg-dashboard-bg/30 border border-dashboard-border/40 rounded-xl group hover:border-primary/30 transition-all duration-500 shadow-sm"
45
+ >
46
+ <div className="flex items-center gap-4">
47
+ <div className="size-10 rounded-lg bg-dashboard-card/50 flex items-center justify-center border border-dashboard-border/40 group-hover:bg-primary/10 group-hover:border-primary/20 transition-all duration-500 shadow-inner">
48
+ <FlagIcon code={entry.locale} />
49
+ </div>
50
+ <div className="flex flex-col">
51
+ <span className="text-sm font-bold text-dashboard-text/90 group-hover:text-primary transition-colors">{entry.domain}</span>
52
+ <div className="flex items-center gap-2 mt-0.5">
53
+ <ArrowRight size={10} className="text-primary opacity-40" />
54
+ <p className="text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-widest">
55
+ Primary Region
56
+ </p>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ {!entry.allowUserOverride && (
61
+ <div className="size-8 bg-dashboard-card/50 flex items-center justify-center text-dashboard-text-secondary/50 rounded-lg border border-dashboard-border/40" title="Locked to this language">
62
+ <ShieldCheck size={14} />
63
+ </div>
64
+ )}
65
+ </motion.div>
66
+ ))}
67
+ </div>
68
+ ) : (
69
+ <div className="py-12 text-center bg-dashboard-card/30 border-dashed border border-dashboard-border/40 rounded-2xl">
70
+ <p className="text-[10px] text-dashboard-text-secondary/40 font-bold uppercase tracking-[0.2em]">No domain-specific routing configured</p>
71
+ </div>
72
+ )}
73
+ </div>
74
+
75
+ <div className="mt-8 pt-8 border-t border-dashboard-border/40 flex flex-col items-center">
76
+ <button
77
+ onClick={onEdit}
78
+ className="w-full max-w-sm relative group/btn flex items-center justify-center gap-4 px-8 py-3 bg-primary text-white rounded-xl text-[10px] font-bold uppercase tracking-[0.2em] transition-all shadow-xl shadow-primary/10 hover:shadow-primary/20 active:scale-[0.98] overflow-hidden"
79
+ >
80
+ <div className="absolute inset-0 bg-linear-to-br from-white/20 to-transparent opacity-0 group-hover/btn:opacity-100 transition-opacity" />
81
+ <Edit3 size={16} />
82
+ <span>{config?.length ? 'Update Configurations' : 'Configure Domain Routing'}</span>
83
+ </button>
84
+ <p className="text-[10px] text-dashboard-text-secondary/60 text-center mt-6 font-medium max-w-md">
85
+ Domain-based routing allows you to orchestrate specific regional delivery protocols based on the incoming hostname.
86
+ </p>
87
+ </div>
88
+ </motion.div>
89
+ </section>
90
+ );
91
+ }
@@ -0,0 +1,216 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, Trash2, Plus, Globe2, Shield, ArrowRight, Save } from 'lucide-react';
4
+ import { DomainLocaleConfig, LocaleDefinition } from '../../../types/settings';
5
+ import { FlagIcon } from './FlagIcon';
6
+ import { clsx, type ClassValue } from 'clsx';
7
+ import { twMerge } from 'tailwind-merge';
8
+
9
+ function cn(...inputs: ClassValue[]) {
10
+ return twMerge(clsx(inputs));
11
+ }
12
+
13
+ interface DomainLanguagesModalProps {
14
+ isOpen: boolean;
15
+ config: DomainLocaleConfig[];
16
+ supportedLocales: LocaleDefinition[];
17
+ onClose: () => void;
18
+ onSaveAndClose: (config: DomainLocaleConfig[]) => void;
19
+ isSaving?: boolean;
20
+ }
21
+
22
+ export function DomainLanguagesModal({
23
+ isOpen,
24
+ config,
25
+ supportedLocales,
26
+ onClose,
27
+ onSaveAndClose,
28
+ isSaving = false
29
+ }: DomainLanguagesModalProps) {
30
+ const [tempConfig, setTempConfig] = useState<DomainLocaleConfig[]>(config);
31
+ const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
32
+
33
+ useEffect(() => {
34
+ setTempConfig(config);
35
+ setExpandedIndex(null);
36
+ }, [config, isOpen]);
37
+
38
+ if (!isOpen) return null;
39
+
40
+ const handleAdd = () => {
41
+ setTempConfig([...tempConfig, { domain: '', locale: supportedLocales[0]?.code || 'en', allowUserOverride: true }]);
42
+ setExpandedIndex(tempConfig.length);
43
+ };
44
+
45
+ const handleUpdate = (index: number, field: keyof DomainLocaleConfig, value: any) => {
46
+ const updated = [...tempConfig];
47
+ updated[index] = { ...updated[index], [field]: value };
48
+ setTempConfig(updated);
49
+ };
50
+
51
+ const handleRemove = (index: number) => {
52
+ setTempConfig(tempConfig.filter((_, i) => i !== index));
53
+ if (expandedIndex === index) setExpandedIndex(null);
54
+ };
55
+
56
+ const toggleExpand = (index: number) => {
57
+ setExpandedIndex(expandedIndex === index ? null : index);
58
+ };
59
+
60
+ return (
61
+ <div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-4">
62
+ <motion.div
63
+ initial={{ opacity: 0, scale: 0.9, y: 20 }}
64
+ animate={{ opacity: 1, scale: 1, y: 0 }}
65
+ exit={{ opacity: 0, scale: 0.9, y: 20 }}
66
+ className="bg-dashboard-card/50 w-full max-w-xl rounded-[2rem] border border-dashboard-border/40 shadow-2xl flex flex-col max-h-[90vh] overflow-hidden relative"
67
+ >
68
+ <div className="absolute top-0 right-0 w-64 h-64 blur-3xl opacity-5 bg-primary pointer-events-none" />
69
+
70
+ <div className="flex items-center justify-between p-6 border-b border-dashboard-border/40 relative z-10">
71
+ <div className="flex items-center gap-3">
72
+ <div className="size-8 rounded-lg bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner">
73
+ <Globe2 size={16} />
74
+ </div>
75
+ <h2 className="text-xl font-bold text-dashboard-text/90">
76
+ Domain <span className="text-primary">Routing</span>
77
+ </h2>
78
+ </div>
79
+ <button onClick={onClose} className="p-2 text-dashboard-text-secondary/50 hover:text-dashboard-text transition-all hover:bg-white/5 rounded-lg">
80
+ <X size={18} />
81
+ </button>
82
+ </div>
83
+
84
+ <div className="flex-1 overflow-y-auto p-6 space-y-4 relative z-10 custom-scrollbar">
85
+ <AnimatePresence mode="popLayout">
86
+ {tempConfig.length > 0 ? (
87
+ <div className="space-y-3">
88
+ <span className="text-[10px] font-bold text-primary/60 uppercase tracking-[0.2em] ml-2">Active Configurations</span>
89
+ {tempConfig.map((entry, index) => (
90
+ <motion.div
91
+ layout
92
+ initial={{ opacity: 0, x: -20 }}
93
+ animate={{ opacity: 1, x: 0 }}
94
+ exit={{ opacity: 0, x: 20 }}
95
+ key={index}
96
+ className="bg-dashboard-bg/30 rounded-xl border border-dashboard-border/40 overflow-hidden transition-all duration-300 hover:border-primary/20"
97
+ >
98
+ <div
99
+ onClick={() => toggleExpand(index)}
100
+ className="w-full flex items-center justify-between p-3.5 text-left hover:bg-primary/5 transition-all cursor-pointer"
101
+ >
102
+ <div className="flex items-center gap-3">
103
+ <span className="text-sm font-bold text-dashboard-text/90">{entry.domain || 'UNCONFIGURED_HOST'}</span>
104
+ <ArrowRight size={12} className="text-primary opacity-40" />
105
+ <div className="px-2 py-0.5 bg-dashboard-card/50 border border-dashboard-border/40 rounded-full flex items-center gap-2 bg-primary/5">
106
+ <FlagIcon code={entry.locale} />
107
+ <span className="text-[9px] font-bold uppercase tracking-wider text-primary">{entry.locale}</span>
108
+ </div>
109
+ {entry.allowUserOverride === false && <Shield size={10} className="text-dashboard-text-secondary/50" />}
110
+ </div>
111
+ <div className="flex items-center gap-2">
112
+ <button
113
+ onClick={(e) => { e.stopPropagation(); handleRemove(index); }}
114
+ className="p-1.5 text-dashboard-text-secondary/40 hover:text-red-500 hover:bg-red-500/10 rounded-lg transition-all"
115
+ >
116
+ <Trash2 size={14} />
117
+ </button>
118
+ <div className={cn("transition-transform duration-300", expandedIndex === index && "rotate-180")}>
119
+ <X size={12} className="text-dashboard-text-secondary/40 rotate-45" />
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ <AnimatePresence>
125
+ {expandedIndex === index && (
126
+ <motion.div
127
+ initial={{ height: 0, opacity: 0 }}
128
+ animate={{ height: 'auto', opacity: 1 }}
129
+ exit={{ height: 0, opacity: 0 }}
130
+ className="overflow-hidden"
131
+ >
132
+ <div className="p-4 pt-0 space-y-4 border-t border-dashboard-border/40 bg-dashboard-card/20">
133
+ <div className="space-y-1.5 mt-4">
134
+ <label className="text-[10px] font-bold text-dashboard-text-secondary/60 uppercase tracking-[0.15em] ml-1">Hostname Protocol</label>
135
+ <input
136
+ type="text"
137
+ value={entry.domain}
138
+ onChange={(e) => handleUpdate(index, 'domain', e.target.value)}
139
+ placeholder="e.g. jhits.tech"
140
+ className="w-full px-4 py-2 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-lg text-sm font-bold outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 transition-all text-dashboard-text placeholder:text-dashboard-text-secondary/30"
141
+ />
142
+ </div>
143
+
144
+ <div className="flex items-center gap-4">
145
+ <div className="flex-1 space-y-1.5">
146
+ <label className="text-[10px] font-bold text-dashboard-text-secondary/60 uppercase tracking-[0.15em] ml-1">Regional Node</label>
147
+ <select
148
+ value={entry.locale}
149
+ onChange={(e) => handleUpdate(index, 'locale', e.target.value)}
150
+ className="w-full px-4 py-2 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-lg text-sm font-bold uppercase tracking-wider outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 transition-all text-dashboard-text cursor-pointer appearance-none"
151
+ >
152
+ {supportedLocales.map(loc => (
153
+ <option key={loc.code} value={loc.code} className="bg-neutral-900">{loc.name}</option>
154
+ ))}
155
+ </select>
156
+ </div>
157
+ <div className="shrink-0 flex flex-col items-center gap-1.5 pt-4">
158
+ <label className="text-[9px] font-bold text-dashboard-text-secondary/50 uppercase tracking-widest">User Override</label>
159
+ <button
160
+ onClick={() => handleUpdate(index, 'allowUserOverride', !entry.allowUserOverride)}
161
+ className={cn(
162
+ "relative w-10 h-5 rounded-full transition-all duration-300 p-0.5",
163
+ entry.allowUserOverride ? "bg-primary" : "bg-neutral-700"
164
+ )}
165
+ >
166
+ <div className={cn(
167
+ "w-4 h-4 bg-white rounded-full transition-transform duration-300 shadow-sm",
168
+ entry.allowUserOverride ? "translate-x-5" : "translate-x-0"
169
+ )} />
170
+ </button>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </motion.div>
175
+ )}
176
+ </AnimatePresence>
177
+ </motion.div>
178
+ ))}
179
+ </div>
180
+ ) : (
181
+ <div className="py-12 text-center bg-dashboard-card/20 border-dashed border border-dashboard-border/40 rounded-2xl">
182
+ <p className="text-[10px] text-dashboard-text-secondary/40 font-bold uppercase tracking-[0.2em]">No domain-specific routing configured</p>
183
+ </div>
184
+ )}
185
+ </AnimatePresence>
186
+
187
+ <div className="pt-2">
188
+ <button
189
+ onClick={handleAdd}
190
+ className="w-full flex items-center justify-center gap-2 px-4 py-3 border border-dashed border-dashboard-border/40 text-dashboard-text-secondary/50 hover:border-primary/40 hover:text-primary rounded-xl transition-all duration-500 font-bold uppercase text-[10px] tracking-[0.2em]"
191
+ >
192
+ <Plus size={16} />
193
+ Add System Domain
194
+ </button>
195
+ </div>
196
+ </div>
197
+
198
+ <div className="flex gap-3 p-6 border-t border-dashboard-border/40 relative z-10">
199
+ <button onClick={onClose} className="flex-1 px-4 py-2.5 bg-dashboard-card/30 hover:bg-white/5 text-dashboard-text/80 rounded-xl text-[10px] font-bold uppercase tracking-wider transition-all border border-dashboard-border/40">
200
+ Cancel
201
+ </button>
202
+ <button
203
+ onClick={() => onSaveAndClose(tempConfig)}
204
+ disabled={isSaving}
205
+ className="flex-1 px-4 py-2.5 bg-primary text-white rounded-xl text-[10px] font-bold uppercase tracking-[0.15em] transition-all shadow-xl shadow-primary/10 hover:shadow-primary/20 disabled:opacity-50 flex items-center justify-center gap-2"
206
+ >
207
+ {isSaving ? (
208
+ <div className="size-3.5 border-2 border-white/20 border-t-white rounded-full animate-spin" />
209
+ ) : <Save size={14} />}
210
+ <span>{isSaving ? 'Syncing...' : 'Deploy Config'}</span>
211
+ </button>
212
+ </div>
213
+ </motion.div>
214
+ </div>
215
+ );
216
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { GLOBAL_LOCALE_DATABASE } from '../constants';
3
+
4
+ export function FlagIcon({ code, countryCode, className = "w-4 h-3" }: { code?: string; countryCode?: string; className?: string }) {
5
+ let finalCountryCode = countryCode;
6
+
7
+ if (!finalCountryCode && code) {
8
+ finalCountryCode = GLOBAL_LOCALE_DATABASE.find(l => l.code === code)?.countryCode || code;
9
+ }
10
+
11
+ if (!finalCountryCode) return null;
12
+
13
+ return (
14
+ <img
15
+ src={`https://flagcdn.com/w20/${finalCountryCode.toLowerCase()}.png`}
16
+ alt={code || finalCountryCode}
17
+ className={`${className} object-contain rounded-xs shadow-xs`}
18
+ />
19
+ );
20
+ }
@@ -0,0 +1,111 @@
1
+ import React from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import { Calendar, Timer, Info } from 'lucide-react';
4
+ import { WebsiteSettings } from '../../../types/settings';
5
+ import { clsx, type ClassValue } from 'clsx';
6
+ import { twMerge } from 'tailwind-merge';
7
+
8
+ function cn(...inputs: ClassValue[]) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ interface LaunchDateSectionProps {
13
+ settings: WebsiteSettings;
14
+ onUpdate: (updates: Partial<WebsiteSettings>) => void;
15
+ }
16
+
17
+ export function LaunchDateSection({ settings, onUpdate }: LaunchDateSectionProps) {
18
+ if (settings.launched) {
19
+ return (
20
+ <section className="space-y-12">
21
+ <div className="flex items-center gap-6">
22
+ <div className="size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner">
23
+ <Calendar size={28} />
24
+ </div>
25
+ <h3 className="text-4xl lg:text-5xl font-bold text-dashboard-text leading-none">
26
+ Launch <span className="text-primary">History</span>
27
+ </h3>
28
+ </div>
29
+
30
+ <motion.div
31
+ initial={{ opacity: 0, scale: 0.98 }}
32
+ animate={{ opacity: 1, scale: 1 }}
33
+ className="bg-dashboard-card/50 border border-dashboard-border/50 p-10 rounded-[3rem] flex items-center gap-8 relative overflow-hidden group"
34
+ >
35
+ <div className="absolute top-0 right-0 w-48 h-48 blur-3xl opacity-5 bg-emerald-500 pointer-events-none" />
36
+
37
+ <div className="size-20 rounded-2xl bg-emerald-500/10 flex items-center justify-center border border-emerald-500/20 shadow-inner shrink-0">
38
+ <div className="size-5 rounded-full bg-emerald-500 animate-pulse shadow-[0_0_20px_rgba(16,185,129,0.4)]" />
39
+ </div>
40
+ <div>
41
+ <span className="text-[10px] font-bold text-emerald-500 uppercase tracking-[0.3em] mb-2 block opacity-60">System Temporal Status</span>
42
+ <h4 className="text-2xl font-bold text-dashboard-text">System Fully Operational</h4>
43
+ {settings.launch_date && (
44
+ <p className="text-sm text-neutral-500 dark:text-neutral-400 font-medium mt-2">
45
+ Initial Deployment Sequence: {new Date(settings.launch_date).toLocaleDateString(undefined, { dateStyle: 'long' })}
46
+ </p>
47
+ )}
48
+ </div>
49
+ </motion.div>
50
+ </section>
51
+ );
52
+ }
53
+
54
+ return (
55
+ <section className="space-y-12">
56
+ <div className="flex items-center gap-6">
57
+ <div className="size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner">
58
+ <Timer size={28} />
59
+ </div>
60
+ <h3 className="text-4xl lg:text-5xl font-bold text-dashboard-text leading-none">
61
+ Launch <span className="text-primary">Sequence</span>
62
+ </h3>
63
+ </div>
64
+
65
+ <motion.div
66
+ initial={{ opacity: 0, y: 20 }}
67
+ animate={{ opacity: 1, y: 0 }}
68
+ className="bg-dashboard-card/50 border border-dashboard-border/50 p-10 rounded-[3rem] space-y-10 relative overflow-hidden"
69
+ >
70
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-10">
71
+ <div className="space-y-4">
72
+ <label className="text-[10px] font-bold text-primary uppercase tracking-[0.3em] ml-2 opacity-60">Deployment Date</label>
73
+ <input
74
+ type="date"
75
+ value={settings.launch_date ? settings.launch_date.split('T')[0] : ''}
76
+ onChange={(e) => {
77
+ const dateValue = e.target.value;
78
+ const currentTime = settings.launch_date?.includes('T') ? settings.launch_date.split('T')[1] : '00:00';
79
+ onUpdate({ launch_date: dateValue ? `${dateValue}T${currentTime}` : '' });
80
+ }}
81
+ className="w-full px-8 py-5 bg-dashboard-card/50 border border-dashboard-border/50 rounded-[2rem] text-sm font-bold outline-none focus:ring-4 focus:ring-primary/10 transition-all text-dashboard-text placeholder:text-neutral-500"
82
+ />
83
+ </div>
84
+ <div className="space-y-4">
85
+ <label className="text-[10px] font-bold text-primary uppercase tracking-[0.3em] ml-2 opacity-60">Synchronization Time (24h)</label>
86
+ <input
87
+ type="time"
88
+ step="60"
89
+ value={settings.launch_date?.includes('T') ? settings.launch_date.split('T')[1] : '00:00'}
90
+ onChange={(e) => {
91
+ const timeValue = e.target.value;
92
+ const currentDate = settings.launch_date?.includes('T') ? settings.launch_date.split('T')[0] : new Date().toISOString().split('T')[0];
93
+ onUpdate({ launch_date: timeValue ? `${currentDate}T${timeValue}` : '' });
94
+ }}
95
+ className="w-full px-8 py-5 bg-dashboard-card/50 border border-dashboard-border/50 rounded-[2rem] text-sm font-bold outline-none focus:ring-4 focus:ring-primary/10 transition-all text-dashboard-text placeholder:text-neutral-500"
96
+ />
97
+ </div>
98
+ </div>
99
+
100
+ <div className="bg-dashboard-card/50 border border-dashboard-border/50 p-8 rounded-[2rem] bg-primary/5 flex items-start gap-5 group">
101
+ <div className="size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary shrink-0 border border-primary/20">
102
+ <Info size={18} />
103
+ </div>
104
+ <p className="text-xs text-neutral-500 dark:text-neutral-400 font-medium leading-relaxed">
105
+ The temporal countdown will be active on the system frontend. Upon reaching the zero-point, the architecture will automatically transition to an active production state.
106
+ </p>
107
+ </div>
108
+ </motion.div>
109
+ </section>
110
+ );
111
+ }