@jhits/plugin-website 0.0.16 β†’ 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 +15 -4
  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 +41 -4
  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
@@ -1,270 +1,67 @@
1
1
  /**
2
2
  * Website Settings View
3
- * Main interface for managing website settings
3
+ * Refactored modular interface with sidebar navigation
4
4
  */
5
5
  'use client';
6
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
7
- import { useState, useEffect, useRef } from 'react';
8
- import { Save, RefreshCw, Globe, Globe2, Mail, Search, Settings2, Calendar, Plus, Trash2, X, Edit3 } from 'lucide-react';
9
- import { FaFacebook, FaInstagram, FaLinkedin, FaPinterest, FaTiktok } from 'react-icons/fa';
10
- import { FaXTwitter } from 'react-icons/fa6';
11
- const AVAILABLE_LOCALES = [
12
- { code: 'en', name: 'English', flag: 'πŸ‡¬πŸ‡§' },
13
- { code: 'sv', name: 'Swedish', flag: 'πŸ‡ΈπŸ‡ͺ' },
14
- { code: 'nl', name: 'Dutch', flag: 'πŸ‡³πŸ‡±' },
15
- ];
16
- const AVAILABLE_PLATFORMS = [
17
- { name: 'Instagram', icon: _jsx(FaInstagram, { size: 18 }) },
18
- { name: 'Facebook', icon: _jsx(FaFacebook, { size: 18 }) },
19
- { name: 'Twitter', icon: _jsx(FaXTwitter, { size: 18 }) },
20
- { name: 'Pinterest', icon: _jsx(FaPinterest, { size: 18 }) },
21
- { name: 'LinkedIn', icon: _jsx(FaLinkedin, { size: 18 }) },
22
- { name: 'TikTok', icon: _jsx(FaTiktok, { size: 18 }) },
23
- ];
24
- function DomainLanguagesCard({ config, onEdit }) {
25
- return (_jsxs("section", { className: "bg-dashboard-bg p-6 rounded-3xl border border-dashboard-border", children: [_jsx("div", { className: "flex items-center justify-between border-b border-dashboard-border pb-4 mb-4", children: _jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text", children: [_jsx(Globe2, { size: 18, className: "text-primary" }), "Domain Languages"] }) }), _jsx("div", { className: "space-y-2", children: config && config.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: config.map((entry, index) => (_jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 bg-dashboard-card rounded-full border border-dashboard-border text-xs", children: [_jsx("span", { className: "font-medium text-dashboard-text", children: entry.domain }), _jsx("span", { className: "text-dashboard-text-secondary", children: "\u2192" }), _jsx("span", { children: AVAILABLE_LOCALES.find(l => l.code === entry.locale)?.flag }), _jsx("span", { className: "text-dashboard-text-secondary", children: entry.allowUserOverride ? '' : 'πŸ”’' })] }, index))) })) : (_jsx("p", { className: "text-xs text-dashboard-text-secondary", children: "No domain languages configured." })) }), _jsxs("button", { onClick: onEdit, className: "mt-4 w-full flex items-center justify-center gap-2 px-4 py-2 bg-primary text-white rounded-xl text-xs font-bold uppercase tracking-wider hover:bg-primary/90 transition-colors", children: [_jsx(Edit3, { size: 14 }), config?.length ? 'Edit Domains' : 'Add Domains'] })] }));
26
- }
27
- function DomainLanguagesModal({ isOpen, config, onSave, onClose, onSaveAndClose, isSaving = false }) {
28
- const [tempConfig, setTempConfig] = useState(config);
29
- const [expandedIndex, setExpandedIndex] = useState(null);
30
- useEffect(() => {
31
- setTempConfig(config);
32
- setExpandedIndex(null);
33
- }, [config, isOpen]);
34
- if (!isOpen)
35
- return null;
36
- const handleAdd = () => {
37
- setTempConfig([...tempConfig, { domain: '', locale: 'en', allowUserOverride: true }]);
38
- setExpandedIndex(tempConfig.length);
39
- };
40
- const handleUpdate = (index, field, value) => {
41
- const updated = [...tempConfig];
42
- updated[index] = { ...updated[index], [field]: value };
43
- setTempConfig(updated);
44
- };
45
- const handleRemove = (index) => {
46
- setTempConfig(tempConfig.filter((_, i) => i !== index));
47
- if (expandedIndex === index)
48
- setExpandedIndex(null);
49
- };
50
- const toggleExpand = (index) => {
51
- setExpandedIndex(expandedIndex === index ? null : index);
52
- };
53
- return (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4", children: _jsxs("div", { className: "bg-dashboard-bg rounded-3xl border border-dashboard-border w-full max-w-lg max-h-[80vh] overflow-hidden flex flex-col", children: [_jsxs("div", { className: "flex items-center justify-between p-6 border-b border-dashboard-border", children: [_jsx("h2", { className: "text-xl font-bold text-dashboard-text", children: "Domain Languages" }), _jsx("button", { onClick: onClose, className: "p-2 text-dashboard-text-secondary hover:text-dashboard-text transition-colors", children: _jsx(X, { size: 20 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-6 space-y-4", children: [tempConfig.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-wider", children: "Current Config" }), tempConfig.map((entry, index) => (_jsxs("div", { className: "bg-dashboard-card rounded-xl border border-dashboard-border overflow-hidden", children: [_jsxs("div", { onClick: () => toggleExpand(index), className: "w-full flex items-center justify-between p-3 text-left hover:bg-dashboard-border/50 transition-colors cursor-pointer", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium text-dashboard-text", children: entry.domain || '(no domain)' }), _jsx("span", { className: "text-dashboard-text-secondary", children: "\u2192" }), _jsx("span", { children: AVAILABLE_LOCALES.find(l => l.code === entry.locale)?.flag }), entry.allowUserOverride === false && _jsx("span", { className: "text-dashboard-text-secondary", children: "\uD83D\uDD12" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: (e) => { e.stopPropagation(); handleRemove(index); }, className: "p-1 text-red-500 hover:bg-red-500/10 rounded transition-colors", title: "Remove", children: _jsx(Trash2, { size: 14 }) }), _jsx("span", { className: "text-dashboard-text-secondary text-xs", children: expandedIndex === index ? 'β–²' : 'β–Ό' })] })] }), expandedIndex === index && (_jsxs("div", { className: "p-3 pt-0 space-y-3 border-t border-dashboard-border", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-[10px] font-bold text-dashboard-text-secondary uppercase tracking-wider", children: "Domain" }), _jsx("input", { type: "text", value: entry.domain, onChange: (e) => handleUpdate(index, 'domain', e.target.value), placeholder: "example.com", className: "w-full px-3 py-2 bg-dashboard-bg border border-dashboard-border rounded-lg outline-none focus:ring-2 focus:ring-primary text-dashboard-text text-sm" })] }), _jsx("div", { className: "flex items-center gap-2", children: _jsxs("div", { className: "flex-1 space-y-1", children: [_jsx("label", { className: "text-[10px] font-bold text-dashboard-text-secondary uppercase tracking-wider", children: "Language" }), _jsx("select", { value: entry.locale, onChange: (e) => handleUpdate(index, 'locale', e.target.value), className: "w-full px-3 py-2 bg-dashboard-bg border border-dashboard-border rounded-lg outline-none focus:ring-2 focus:ring-primary text-dashboard-text text-sm", children: AVAILABLE_LOCALES.map(loc => (_jsxs("option", { value: loc.code, children: [loc.flag, " ", loc.name] }, loc.code))) })] }) }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { className: "text-[10px] text-dashboard-text-secondary", children: "Allow user to change" }), _jsx("button", { onClick: () => handleUpdate(index, 'allowUserOverride', !entry.allowUserOverride), className: `relative w-10 h-5 rounded-full transition-colors ${entry.allowUserOverride ? 'bg-primary' : 'bg-neutral-300 dark:bg-neutral-600'}`, children: _jsx("div", { className: `absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full transition-transform duration-200 ${entry.allowUserOverride ? 'translate-x-5' : 'translate-x-0'}` }) })] })] }))] }, index)))] })), _jsxs("div", { className: "border-t border-dashboard-border pt-4", children: [_jsx("p", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-wider mb-3", children: "Add New Domain" }), _jsxs("button", { onClick: handleAdd, className: "w-full flex items-center justify-center gap-2 px-4 py-3 border-2 border-dashed border-dashboard-border text-dashboard-text-secondary hover:border-primary hover:text-primary rounded-xl transition-colors", children: [_jsx(Plus, { size: 16 }), "Add Domain"] })] }), _jsx("p", { className: "text-[10px] text-dashboard-text-secondary text-center pt-2", children: "Configure which language to use per domain. E.g., botanicsandyou.nl \u2192 Dutch, botanicsandyou.se \u2192 Swedish." })] }), _jsxs("div", { className: "flex gap-3 p-6 border-t border-dashboard-border", children: [_jsx("button", { onClick: onClose, className: "flex-1 px-4 py-2 bg-dashboard-card text-dashboard-text rounded-xl text-sm font-bold hover:bg-dashboard-border transition-colors", children: "Cancel" }), _jsx("button", { onClick: () => onSaveAndClose(tempConfig), disabled: isSaving, className: "flex-1 px-4 py-2 bg-primary text-white rounded-xl text-sm font-bold hover:bg-primary/90 transition-colors disabled:opacity-50", children: isSaving ? 'Saving...' : 'Save' })] })] }) }));
54
- }
55
- // =============================================================================
56
- // Main Component
57
- // =============================================================================
58
- export function SettingsView({ siteId, locale }) {
59
- const [isLoading, setIsLoading] = useState(true);
60
- const [isSaving, setIsSaving] = useState(false);
61
- const [showSuccess, setShowSuccess] = useState(false);
62
- const [activeTab, setActiveTab] = useState('en');
63
- const [showDomainModal, setShowDomainModal] = useState(false);
64
- const [settings, setSettings] = useState({
65
- identifier: 'site_config',
66
- siteName: '',
67
- siteTagline: '',
68
- siteDescription: '',
69
- keywords: '',
70
- contactEmail: '',
71
- phoneNumber: '',
72
- physicalAddress: '',
73
- smtpUser: '',
74
- maintenanceMode: false,
75
- launch_date: '',
76
- socials: [],
77
- localizedContactInfo: {
78
- en: { physicalAddress: '', phoneNumber: '' },
79
- sv: { physicalAddress: '', phoneNumber: '' },
80
- nl: { physicalAddress: '', phoneNumber: '' },
81
- },
82
- localizedSocials: {
83
- en: [],
84
- sv: [],
85
- nl: [],
86
- },
87
- domainLocaleConfig: [],
88
- });
89
- // Fetch settings on load
90
- useEffect(() => {
91
- const fetchSettings = async () => {
92
- try {
93
- setIsLoading(true);
94
- const response = await fetch('/api/plugin-website/settings', { credentials: 'include' });
95
- if (!response.ok)
96
- throw new Error('Failed to fetch settings');
97
- const data = await response.json();
98
- if (data.siteName !== undefined) {
99
- let launchDate = '';
100
- if (data.launch_date) {
101
- const date = new Date(data.launch_date);
102
- if (!isNaN(date.getTime())) {
103
- const year = date.getFullYear();
104
- const month = String(date.getMonth() + 1).padStart(2, '0');
105
- const day = String(date.getDate()).padStart(2, '0');
106
- const hours = String(date.getHours()).padStart(2, '0');
107
- const minutes = String(date.getMinutes()).padStart(2, '0');
108
- launchDate = `${year}-${month}-${day}T${hours}:${minutes}`;
109
- }
110
- }
111
- setSettings({
112
- identifier: 'site_config',
113
- siteName: data.siteName || '',
114
- siteTagline: data.siteTagline || '',
115
- siteDescription: data.siteDescription || '',
116
- keywords: data.keywords || '',
117
- contactEmail: data.contactEmail || '',
118
- phoneNumber: data.phoneNumber || '',
119
- physicalAddress: data.physicalAddress || '',
120
- smtpUser: data.smtpUser || '',
121
- maintenanceMode: data.maintenanceMode ?? false,
122
- launch_date: launchDate,
123
- socials: data.socials || [],
124
- localizedContactInfo: data.localizedContactInfo || {
125
- en: { physicalAddress: '', phoneNumber: '' },
126
- sv: { physicalAddress: '', phoneNumber: '' },
127
- nl: { physicalAddress: '', phoneNumber: '' },
128
- },
129
- localizedSocials: data.localizedSocials || { en: [], sv: [], nl: [] },
130
- domainLocaleConfig: data.domainLocaleConfig || [],
131
- });
132
- }
133
- }
134
- catch (error) {
135
- console.error('Failed to load settings:', error);
136
- }
137
- finally {
138
- setIsLoading(false);
139
- }
140
- };
141
- fetchSettings();
142
- }, []);
143
- // Use ref to always get latest state
144
- const settingsRef = useRef(settings);
145
- useEffect(() => { settingsRef.current = settings; }, [settings]);
146
- // Save settings
147
- const handleSave = async () => {
148
- try {
149
- setIsSaving(true);
150
- const currentSettings = settingsRef.current;
151
- const settingsToSave = { ...currentSettings };
152
- if (currentSettings.launch_date && currentSettings.launch_date.trim() !== '') {
153
- settingsToSave.launch_date = currentSettings.launch_date;
154
- }
155
- else {
156
- delete settingsToSave.launch_date;
157
- }
158
- const response = await fetch('/api/plugin-website/settings', {
159
- method: 'POST',
160
- headers: { 'Content-Type': 'application/json' },
161
- credentials: 'include',
162
- body: JSON.stringify(settingsToSave),
163
- });
164
- if (!response.ok) {
165
- const error = await response.json();
166
- throw new Error(error.error || 'Failed to save settings');
167
- }
168
- setShowSuccess(true);
169
- setTimeout(() => setShowSuccess(false), 3000);
170
- }
171
- catch (error) {
172
- console.error('Failed to save settings:', error);
173
- alert(error.message || 'Failed to save settings');
174
- }
175
- finally {
176
- setIsSaving(false);
177
- }
178
- };
179
- // Update localized contact info
180
- const handleUpdateLocalizedContact = (field, value) => {
181
- setSettings({
182
- ...settings,
183
- localizedContactInfo: {
184
- ...settings.localizedContactInfo,
185
- [activeTab]: { ...settings.localizedContactInfo?.[activeTab], [field]: value },
186
- },
187
- });
188
- };
189
- // Social links handlers
190
- const handleAddLocalizedSocial = () => {
191
- const currentSocials = settings.localizedSocials?.[activeTab] || [];
192
- const newId = currentSocials.length ? Math.max(...currentSocials.map(s => s.id)) + 1 : 1;
193
- setSettings({
194
- ...settings,
195
- localizedSocials: {
196
- ...settings.localizedSocials,
197
- [activeTab]: [...currentSocials, { id: newId, platform: '', url: '' }],
198
- },
199
- });
200
- };
201
- const handleUpdateLocalizedSocial = (id, field, value) => {
202
- const currentSocials = settings.localizedSocials?.[activeTab] || [];
203
- setSettings({
204
- ...settings,
205
- localizedSocials: {
206
- ...settings.localizedSocials,
207
- [activeTab]: currentSocials.map(s => s.id === id ? { ...s, [field]: value } : s),
208
- },
209
- });
210
- };
211
- const handleRemoveLocalizedSocial = (id) => {
212
- const currentSocials = settings.localizedSocials?.[activeTab] || [];
213
- setSettings({
214
- ...settings,
215
- localizedSocials: {
216
- ...settings.localizedSocials,
217
- [activeTab]: currentSocials.filter(s => s.id !== id),
218
- },
219
- });
220
- };
221
- // Domain locale handlers
222
- const openDomainModal = () => setShowDomainModal(true);
223
- const closeDomainModal = () => setShowDomainModal(false);
224
- const saveDomainConfig = (config) => {
225
- setSettings({ ...settings, domainLocaleConfig: config });
226
- setShowDomainModal(false);
227
- };
228
- const saveDomainConfigAndClose = async (config) => {
229
- setSettings({ ...settings, domainLocaleConfig: config });
230
- setShowDomainModal(false);
231
- // Immediately save to API
232
- try {
233
- const response = await fetch('/api/plugin-website/settings', {
234
- method: 'POST',
235
- headers: { 'Content-Type': 'application/json' },
236
- credentials: 'include',
237
- body: JSON.stringify({ ...settings, domainLocaleConfig: config }),
238
- });
239
- if (response.ok) {
240
- setShowSuccess(true);
241
- setTimeout(() => setShowSuccess(false), 3000);
242
- }
243
- }
244
- catch (error) {
245
- console.error('Failed to save domain config:', error);
246
- }
247
- };
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { useState } from 'react';
8
+ import { Save, RefreshCw, Settings2, Globe, Search, Mail, Check, Lock as LockIcon } from 'lucide-react';
9
+ import { useWebsiteSettings } from './Settings/hooks/useWebsiteSettings';
10
+ import { FlagIcon } from './Settings/components/FlagIcon';
11
+ // Sub-components
12
+ import { SeoIdentitySection } from './Settings/components/SeoIdentitySection';
13
+ import { LocalizedContentSection } from './Settings/components/LocalizedContentSection';
14
+ import { LocalesManagementCard } from './Settings/components/LocalesManagementCard';
15
+ import { StatusSection } from './Settings/components/StatusSection';
16
+ import { LaunchDateSection } from './Settings/components/LaunchDateSection';
17
+ import { DomainLanguagesCard } from './Settings/components/DomainLanguagesCard';
18
+ import { DomainLanguagesModal } from './Settings/components/DomainLanguagesModal';
19
+ export function SettingsView({ siteId }) {
20
+ const [currentTab, setCurrentTab] = useState('general');
21
+ const { settings, isLoading, isSaving, isDirty, showSuccess, activeTab, setActiveTab, activeLocales, showDomainModal, setShowDomainModal, handleUpdateSettings, handleUpdateLocalizedIdentity, handleUpdateLocalizedContact, handleAddLocalizedSocial, handleUpdateLocalizedSocial, handleRemoveLocalizedSocial, handleUpdateLocales, handleSave, handleReset, saveDomainConfigAndClose } = useWebsiteSettings(siteId);
248
22
  if (isLoading) {
249
23
  return (_jsx("div", { className: "h-full w-full bg-dashboard-card text-dashboard-text flex items-center justify-center", children: _jsxs("div", { className: "text-center", children: [_jsx(RefreshCw, { className: "w-8 h-8 animate-spin text-primary mx-auto mb-4" }), _jsx("p", { className: "text-sm text-dashboard-text-secondary", children: "Loading settings..." })] }) }));
250
24
  }
251
- return (_jsxs("div", { className: "h-full w-full rounded-[2.5rem] bg-dashboard-card p-8 overflow-y-auto", children: [_jsxs("div", { className: "max-w-6xl mx-auto", children: [_jsxs("div", { className: "flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-black text-dashboard-text uppercase tracking-tighter mb-2", children: "Website Settings" }), _jsx("p", { className: "text-sm text-dashboard-text-secondary", children: "Manage your website identity, contact information, and social links" })] }), _jsx("button", { onClick: handleSave, disabled: isSaving, className: `inline-flex items-center gap-2 px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-all shadow-lg shadow-primary/20 ${isSaving ? 'bg-neutral-400 text-white cursor-not-allowed' :
252
- showSuccess ? 'bg-green-600 text-white' :
253
- 'bg-primary text-white hover:bg-primary/90'}`, children: isSaving ? _jsxs(_Fragment, { children: [_jsx(RefreshCw, { className: "w-4 h-4 animate-spin" }), " Saving..."] }) :
254
- showSuccess ? _jsxs(_Fragment, { children: [_jsx(Settings2, { className: "w-4 h-4" }), " Saved!"] }) :
255
- _jsxs(_Fragment, { children: [_jsx(Save, { className: "w-4 h-4" }), " Save Settings"] }) })] }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-8", children: [_jsxs("div", { className: "lg:col-span-2 space-y-8", children: [_jsxs("section", { className: "bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border", children: [_jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6", children: [_jsx(Search, { size: 20, className: "text-primary" }), "Website Identity & SEO"] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Website Name" }), _jsx("input", { type: "text", value: settings.siteName || '', onChange: (e) => setSettings({ ...settings, siteName: e.target.value }), placeholder: "e.g., Botanics & You", className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Website Tagline" }), _jsx("input", { type: "text", value: settings.siteTagline || '', onChange: (e) => setSettings({ ...settings, siteTagline: e.target.value }), placeholder: "e.g., Sharing my knowledge with you", className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text" })] })] }), _jsxs("div", { className: "mt-6 space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Website Description" }), _jsx("textarea", { value: settings.siteDescription || '', onChange: (e) => setSettings({ ...settings, siteDescription: e.target.value }), placeholder: "A brief description of your website", rows: 3, className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text resize-none" })] }), _jsxs("div", { className: "mt-6 space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Keywords (comma-separated)" }), _jsx("input", { type: "text", value: settings.keywords || '', onChange: (e) => setSettings({ ...settings, keywords: e.target.value }), placeholder: "keyword1, keyword2, keyword3", className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text" })] })] }), _jsxs("section", { className: "bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border", children: [_jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6", children: [_jsx(Mail, { size: 20, className: "text-primary" }), "Contact Information"] }), _jsx("div", { className: "flex gap-2 mb-6", children: AVAILABLE_LOCALES.map((loc) => (_jsxs("button", { onClick: () => setActiveTab(loc.code), className: `px-4 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all ${activeTab === loc.code ? 'bg-primary text-white' : 'bg-dashboard-card text-dashboard-text-secondary hover:bg-dashboard-border'}`, children: [_jsx("span", { className: "mr-1", children: loc.flag }), loc.code.toUpperCase()] }, loc.code))) }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Contact Email (global)" }), _jsx("input", { type: "email", value: settings.contactEmail || '', onChange: (e) => setSettings({ ...settings, contactEmail: e.target.value }), placeholder: "contact@example.com", className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: ["Phone Number (", activeTab.toUpperCase(), ")"] }), _jsx("input", { type: "tel", value: settings.localizedContactInfo?.[activeTab]?.phoneNumber || settings.phoneNumber || '', onChange: (e) => handleUpdateLocalizedContact('phoneNumber', e.target.value), placeholder: "+31 6 12345678", className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text" })] })] }), _jsxs("div", { className: "mt-6 space-y-2", children: [_jsxs("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: ["Physical Address (", activeTab.toUpperCase(), ")"] }), _jsx("textarea", { value: settings.localizedContactInfo?.[activeTab]?.physicalAddress || settings.physicalAddress || '', onChange: (e) => handleUpdateLocalizedContact('physicalAddress', e.target.value), placeholder: "Street address, City, Country", rows: 2, className: "w-full px-4 py-3 bg-dashboard-card border border-dashboard-border rounded-2xl outline-none focus:ring-2 focus:ring-primary transition-all text-dashboard-text resize-none" })] })] }), _jsxs("section", { className: "bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-dashboard-border pb-4 mb-6", children: [_jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text", children: [_jsx(Globe, { size: 20, className: "text-primary" }), "Social Links (", activeTab.toUpperCase(), ")"] }), _jsx("button", { onClick: handleAddLocalizedSocial, className: "px-4 py-2 text-xs font-bold uppercase tracking-widest bg-primary text-white rounded-full hover:bg-primary/90 transition-all", children: "Add Social" })] }), _jsx("div", { className: "flex gap-2 mb-6", children: AVAILABLE_LOCALES.map((loc) => (_jsxs("button", { onClick: () => setActiveTab(loc.code), className: `px-4 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all ${activeTab === loc.code ? 'bg-primary text-white' : 'bg-dashboard-card text-dashboard-text-secondary hover:bg-dashboard-border'}`, children: [_jsx("span", { className: "mr-1", children: loc.flag }), loc.code.toUpperCase()] }, loc.code))) }), _jsx("div", { className: "space-y-4", children: (() => {
256
- const currentSocials = settings.localizedSocials?.[activeTab] || [];
257
- return currentSocials.length > 0 ? (currentSocials.map((social) => {
258
- const platform = AVAILABLE_PLATFORMS.find(p => p.name === social.platform);
259
- return (_jsxs("div", { className: "flex items-center gap-4 p-4 bg-dashboard-card rounded-2xl border border-dashboard-border", children: [_jsx("div", { className: "flex-shrink-0 text-dashboard-text-secondary", children: platform?.icon || _jsx(Globe, { size: 18 }) }), _jsxs("select", { value: social.platform, onChange: (e) => handleUpdateLocalizedSocial(social.id, 'platform', e.target.value), className: "flex-1 px-4 py-2 bg-dashboard-bg border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text", children: [_jsx("option", { value: "", children: "Select Platform" }), AVAILABLE_PLATFORMS.map(p => (_jsx("option", { value: p.name, children: p.name }, p.name)))] }), _jsx("input", { type: "url", value: social.url, onChange: (e) => handleUpdateLocalizedSocial(social.id, 'url', e.target.value), placeholder: "https://...", className: "flex-1 px-4 py-2 bg-dashboard-bg border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text" }), _jsx("button", { onClick: () => handleRemoveLocalizedSocial(social.id), className: "px-4 py-2 text-xs font-bold text-red-500 hover:text-red-700 transition-colors", children: "Remove" })] }, social.id));
260
- })) : (_jsxs("p", { className: "text-sm text-dashboard-text-secondary text-center py-8", children: ["No social links added for ", activeTab.toUpperCase(), ". Click \"Add Social\" to get started."] }));
261
- })() })] })] }), _jsxs("div", { className: "space-y-8", children: [_jsxs("section", { className: "bg-dashboard-bg p-6 rounded-3xl border border-dashboard-border", children: [_jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6", children: [_jsx(Settings2, { size: 18, className: "text-primary" }), "Maintenance"] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text block mb-1", children: "Maintenance Mode" }), _jsx("p", { className: "text-[10px] text-dashboard-text-secondary", children: "Show maintenance page to visitors" })] }), _jsx("button", { onClick: () => setSettings({ ...settings, maintenanceMode: !settings.maintenanceMode }), className: `relative w-12 h-6 rounded-full transition-colors ${settings.maintenanceMode ? 'bg-primary' : 'bg-neutral-200 dark:bg-neutral-700'}`, children: _jsx("div", { className: `absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform duration-200 ${settings.maintenanceMode ? 'translate-x-6' : 'translate-x-0'}` }) })] })] }), _jsxs("section", { className: "bg-dashboard-bg p-6 rounded-3xl border border-dashboard-border", children: [_jsxs("div", { className: "flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6", children: [_jsx(Calendar, { size: 18, className: "text-primary" }), "Launch Date"] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text block", children: "Date" }), _jsx("input", { type: "date", value: settings.launch_date ? settings.launch_date.split('T')[0] : '', onChange: (e) => {
262
- const dateValue = e.target.value;
263
- const currentTime = settings.launch_date?.includes('T') ? settings.launch_date.split('T')[1] : '00:00';
264
- setSettings(prev => ({ ...prev, launch_date: dateValue ? `${dateValue}T${currentTime}` : '' }));
265
- }, className: "w-full px-4 py-2 bg-dashboard-card border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs font-bold text-dashboard-text block", children: "Time (24-hour format)" }), _jsx("input", { type: "time", step: "60", value: settings.launch_date?.includes('T') ? settings.launch_date.split('T')[1] : '00:00', onChange: (e) => {
266
- const timeValue = e.target.value;
267
- const currentDate = settings.launch_date?.includes('T') ? settings.launch_date.split('T')[0] : new Date().toISOString().split('T')[0];
268
- setSettings(prev => ({ ...prev, launch_date: timeValue ? `${currentDate}T${timeValue}` : '' }));
269
- }, className: "w-full px-4 py-2 bg-dashboard-card border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text" })] }), _jsx("p", { className: "text-[10px] text-dashboard-text-secondary", children: "Used for countdown timers." })] })] }), _jsx(DomainLanguagesCard, { config: settings.domainLocaleConfig || [], onEdit: openDomainModal })] })] })] }), _jsx(DomainLanguagesModal, { isOpen: showDomainModal, config: settings.domainLocaleConfig || [], onSave: saveDomainConfig, onSaveAndClose: saveDomainConfigAndClose, onClose: closeDomainModal, isSaving: isSaving })] }));
25
+ const hasLocales = settings.supportedLocales && settings.supportedLocales.length > 0;
26
+ const navigationItems = [
27
+ { id: 'general', label: 'General', icon: Settings2, description: 'Status & Launch Date', localized: false },
28
+ { id: 'localization', label: 'Localization', icon: Globe, description: 'Languages & Domains', localized: false },
29
+ { id: 'seo', label: 'Identity & SEO', icon: Search, description: 'Site Name & Meta', localized: true },
30
+ { id: 'contact', label: 'Contact & Social', icon: Mail, description: 'Address & Links', localized: true },
31
+ ];
32
+ const renderMissingLocalesState = () => (_jsx("div", { className: "h-full flex items-center justify-center p-12", children: _jsxs("div", { className: "max-w-md w-full text-center space-y-8", children: [_jsx("div", { className: "size-24 bg-primary/15 rounded-[2rem] flex items-center justify-center mx-auto shadow-2xl shadow-primary/10 border border-primary/30", children: _jsx(Globe, { size: 48, className: "text-primary animate-pulse" }) }), _jsxs("div", { className: "space-y-4", children: [_jsx("h3", { className: "text-3xl font-bold text-dashboard-text tracking-tight", children: "Configuration Required" }), _jsx("p", { className: "text-sm text-dashboard-text-secondary font-medium leading-relaxed opacity-80", children: "To manage localized content like site names and contact info, you first need to define which languages your website supports." })] }), _jsx("button", { onClick: () => setCurrentTab('localization'), className: "inline-flex items-center gap-3 px-8 py-4 bg-primary text-white rounded-2xl text-xs font-bold uppercase tracking-wider hover:bg-primary/90 transition-all shadow-lg shadow-primary/25 active:scale-95", children: "Setup Languages Now" })] }) }));
33
+ const renderContent = () => {
34
+ // If trying to access localized tab without locales, show prompt
35
+ const activeItem = navigationItems.find(i => i.id === currentTab);
36
+ if (activeItem?.localized && !hasLocales) {
37
+ return renderMissingLocalesState();
38
+ }
39
+ switch (currentTab) {
40
+ case 'general':
41
+ return (_jsxs("div", { className: "space-y-10 max-w-3xl", children: [_jsx(StatusSection, { settings: settings, onUpdate: handleUpdateSettings }), _jsx(LaunchDateSection, { settings: settings, onUpdate: handleUpdateSettings })] }));
42
+ case 'localization':
43
+ return (_jsxs("div", { className: "space-y-10 max-w-3xl", children: [_jsx(LocalesManagementCard, { supportedLocales: settings.supportedLocales, onUpdate: handleUpdateLocales }), _jsx(DomainLanguagesCard, { config: settings.domainLocaleConfig || [], onEdit: () => setShowDomainModal(true) })] }));
44
+ case 'seo':
45
+ return (_jsx("div", { className: "max-w-3xl", children: _jsx(SeoIdentitySection, { settings: settings, activeTab: activeTab, onUpdateLocalized: handleUpdateLocalizedIdentity }) }));
46
+ case 'contact':
47
+ return (_jsx("div", { className: "max-w-3xl", children: _jsx(LocalizedContentSection, { settings: settings, activeTab: activeTab, onUpdateContact: handleUpdateLocalizedContact, onAddSocial: handleAddLocalizedSocial, onUpdateSocial: handleUpdateLocalizedSocial, onRemoveSocial: handleRemoveLocalizedSocial }) }));
48
+ default:
49
+ return null;
50
+ }
51
+ };
52
+ return (_jsxs("div", { className: "absolute inset-0 w-full flex overflow-hidden bg-dashboard-bg/50", children: [_jsxs("aside", { className: "w-80 bg-dashboard-card/40 backdrop-blur-2xl border-r border-dashboard-border/60 flex flex-col p-6 flex-shrink-0 h-full", children: [_jsxs("div", { className: "mb-12 px-2 shrink-0 pt-8", children: [_jsxs("h1", { className: "text-3xl font-bold text-dashboard-text tracking-tight flex items-center gap-3", children: [_jsx("div", { className: "size-8 bg-primary rounded-lg flex items-center justify-center shadow-lg shadow-primary/20", children: _jsx(Settings2, { size: 18, className: "text-white" }) }), "Settings"] }), _jsxs("div", { className: "flex items-center gap-2 mt-3 ml-11", children: [_jsx("p", { className: "text-[10px] font-bold text-primary/80 uppercase tracking-widest", children: "Website Engine" }), isDirty && (_jsx("span", { className: "flex size-2 rounded-full bg-amber-500 animate-pulse shadow-[0_0_8px_rgba(245,158,11,0.5)]", title: "Unsaved changes" }))] })] }), _jsx("nav", { className: "space-y-2 flex-1 overflow-y-auto custom-scrollbar pr-2", children: navigationItems.map((item) => {
53
+ const Icon = item.icon;
54
+ const isActive = currentTab === item.id;
55
+ const isLocked = item.localized && !hasLocales;
56
+ return (_jsxs("button", { onClick: () => !isLocked && setCurrentTab(item.id), className: `w-full flex items-center gap-4 p-4 rounded-2xl transition-all text-left group relative ${isActive
57
+ ? 'bg-primary text-white shadow-xl shadow-primary/25 border border-primary/20'
58
+ : 'hover:bg-primary/8 text-dashboard-text-secondary hover:text-primary border border-transparent'} ${isLocked ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}`, children: [_jsx("div", { className: `flex items-center justify-center shrink-0 transition-transform duration-300 ${isActive ? 'scale-110' : 'group-hover:scale-110'}`, children: _jsx(Icon, { size: 20, className: isActive ? 'text-white' : 'text-primary/60 group-hover:text-primary' }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: `block text-sm font-semibold tracking-tight truncate ${isActive ? 'text-white' : 'text-dashboard-text'}`, children: item.label }), isLocked && _jsx(LockIcon, { size: 12, className: "text-dashboard-text-secondary opacity-50" })] }), _jsx("span", { className: `text-[10px] font-medium opacity-70 truncate block mt-0.5 ${isActive ? 'text-white/80' : 'text-dashboard-text-secondary'}`, children: item.description })] })] }, item.id));
59
+ }) }), _jsxs("div", { className: "mt-auto space-y-4 pt-8 border-t border-dashboard-border/40", children: [isDirty && (_jsx("button", { onClick: handleReset, className: "w-full py-3 rounded-xl text-xs font-bold text-dashboard-text-secondary hover:text-dashboard-text hover:bg-dashboard-card/60 transition-all active:scale-[0.98]", children: "Discard Changes" })), _jsxs("button", { onClick: handleSave, disabled: isSaving, className: `w-full flex items-center justify-center gap-3 py-4 rounded-2xl text-sm font-bold transition-all active:scale-[0.98] ${isSaving ? 'bg-neutral-200 dark:bg-neutral-800 text-neutral-400 cursor-not-allowed' :
60
+ showSuccess ? 'bg-emerald-500 text-white shadow-lg shadow-emerald-500/20' :
61
+ isDirty ? 'bg-primary text-white hover:bg-primary/90 shadow-xl shadow-primary/30' :
62
+ 'bg-neutral-100 dark:bg-neutral-800/40 text-dashboard-text-secondary opacity-40 cursor-not-allowed'}`, children: [isSaving ? _jsx(RefreshCw, { className: "w-4 h-4 animate-spin" }) :
63
+ showSuccess ? _jsx(Check, { className: "w-4 h-4" }) :
64
+ _jsx(Save, { className: "w-4 h-4" }), isSaving ? 'Saving...' : showSuccess ? 'Saved!' : 'Save Changes'] })] })] }), _jsxs("main", { className: "flex-1 flex flex-col min-w-0 bg-transparent", children: [_jsxs("div", { className: "h-24 px-12 flex items-center justify-between shrink-0 z-10 bg-dashboard-bg/20 backdrop-blur-sm border-b border-dashboard-border/30", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx("div", { className: "h-10 w-1.5 bg-primary rounded-full shadow-[0_0_10px_rgba(var(--color-primary),0.3)]" }), _jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold text-dashboard-text tracking-tight leading-none mb-1.5", children: navigationItems.find(i => i.id === currentTab)?.label }), _jsx("p", { className: "text-xs font-medium text-dashboard-text-secondary opacity-80", children: navigationItems.find(i => i.id === currentTab)?.description })] })] }), (currentTab === 'seo' || currentTab === 'contact') && (_jsx("div", { className: "flex gap-1.5 p-1.5 bg-dashboard-card/50 backdrop-blur-md rounded-2xl border border-dashboard-border/40 shadow-sm", children: activeLocales.map((loc) => (_jsxs("button", { onClick: () => setActiveTab(loc.code), className: `px-4 py-2 rounded-xl text-xs font-bold transition-all flex items-center gap-2.5 ${activeTab === loc.code
65
+ ? 'bg-primary text-white shadow-md'
66
+ : 'text-dashboard-text-secondary hover:text-dashboard-text hover:bg-dashboard-card/80'}`, children: [_jsx(FlagIcon, { code: loc.code, countryCode: loc.countryCode }), loc.code.toUpperCase()] }, loc.code))) }))] }), _jsx("div", { className: "flex-1 overflow-y-auto px-12 pb-12 pt-6 custom-scrollbar", children: _jsx("div", { className: "max-w-4xl", children: renderContent() }) })] }), _jsx(DomainLanguagesModal, { isOpen: showDomainModal, config: settings.domainLocaleConfig || [], supportedLocales: activeLocales, onClose: () => setShowDomainModal(false), onSaveAndClose: saveDomainConfigAndClose, isSaving: isSaving })] }));
270
67
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@jhits/plugin-website",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "Website management and configuration plugin for the JHITS ecosystem",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "main": "./src/index.ts",
9
- "types": "./src/index.ts",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./src/index.tsx",
13
- "import": "./src/index.tsx",
14
- "default": "./src/index.tsx"
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
15
  },
16
16
  "./src": {
17
17
  "types": "./src/index.tsx",
@@ -19,16 +19,19 @@
19
19
  "default": "./src/index.tsx"
20
20
  },
21
21
  "./server": {
22
- "types": "./src/index.server.ts",
23
- "import": "./src/index.server.ts",
24
- "default": "./src/index.server.ts"
22
+ "types": "./dist/index.server.d.ts",
23
+ "import": "./dist/index.server.js",
24
+ "default": "./dist/index.server.js"
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
+ "clsx": "^2.1.1",
29
+ "framer-motion": "^12.34.0",
28
30
  "lucide-react": "^0.577.0",
29
31
  "mongodb": "^7.1.0",
30
32
  "next-auth": "^4.24.13",
31
33
  "react-icons": "^5.6.0",
34
+ "tailwind-merge": "^3.5.0",
32
35
  "@jhits/plugin-core": "0.0.10"
33
36
  },
34
37
  "peerDependencies": {
@@ -51,6 +54,7 @@
51
54
  "package.json"
52
55
  ],
53
56
  "scripts": {
54
- "build": "tsc"
57
+ "build": "tsc",
58
+ "build:federation": "PLUGIN_NAME=pluginWebsite PLUGIN_DIR=$(pwd) npx webpack --config ../federation.webpack.config.js --mode production && PLUGIN_NAME=pluginWebsite PLUGIN_DIR=$(pwd) npx webpack --config ../federation.server.webpack.config.js --mode production"
55
59
  }
56
60
  }
@@ -55,12 +55,49 @@ export async function GET_SETTINGS(
55
55
 
56
56
  // Convert launch_date from Date to ISO string if it exists
57
57
  const responseData: any = { ...siteConfig };
58
+ let launchDateObj: Date | null = null;
59
+
58
60
  if (responseData.launch_date instanceof Date) {
59
- responseData.launch_date = responseData.launch_date.toISOString();
61
+ launchDateObj = responseData.launch_date;
62
+ responseData.launch_date = (launchDateObj as Date).toISOString();
60
63
  } else if (responseData.launch_date) {
61
- // If it's already a string, keep it as is
62
- responseData.launch_date = responseData.launch_date;
63
- } else {
64
+ const parsed = new Date(responseData.launch_date);
65
+ if (!isNaN(parsed.getTime())) {
66
+ launchDateObj = parsed;
67
+ }
68
+ }
69
+
70
+ // AUTO-RELEASE LOGIC:
71
+ // If there is a launch date and it has passed, force launched state to true
72
+ // and maintenance to false regardless of what is in the DB.
73
+ if (launchDateObj && launchDateObj.getTime() <= Date.now()) {
74
+ responseData.launched = true;
75
+ responseData.maintenanceMode = false;
76
+
77
+ // PERSIST the auto-release back to database IMMEDIATELY
78
+ const currentLaunched = siteConfig.launched === true;
79
+ const currentMaintenance = siteConfig.maintenanceMode === true;
80
+
81
+ if (!currentLaunched || currentMaintenance) {
82
+ try {
83
+ console.log(`[WebsiteAPI] PERSISTING AUTO-RELEASE: ${siteConfig.siteName || 'site_config'}`);
84
+ await settings.updateOne(
85
+ { identifier: 'site_config' },
86
+ {
87
+ $set: {
88
+ launched: true,
89
+ maintenanceMode: false,
90
+ updatedAt: new Date()
91
+ }
92
+ }
93
+ );
94
+ } catch (e) {
95
+ console.error('[WebsiteAPI] Failed to persist auto-release:', e);
96
+ }
97
+ }
98
+ }
99
+
100
+ if (!responseData.launch_date) {
64
101
  responseData.launch_date = '';
65
102
  }
66
103
 
@@ -2,11 +2,18 @@
2
2
  * Website Settings Types
3
3
  */
4
4
 
5
- export type SupportedLocale = 'en' | 'sv' | 'nl';
5
+ // Basic supported locales, but can be expanded dynamically
6
+ export type SupportedLocale = string;
7
+
8
+ export interface LocaleDefinition {
9
+ code: string;
10
+ name: string;
11
+ countryCode: string;
12
+ }
6
13
 
7
14
  export interface DomainLocaleConfig {
8
15
  domain: string;
9
- locale: SupportedLocale;
16
+ locale: string;
10
17
  allowUserOverride?: boolean;
11
18
  }
12
19
 
@@ -15,6 +22,13 @@ export interface LocalizedContactInfo {
15
22
  phoneNumber?: string;
16
23
  }
17
24
 
25
+ export interface LocalizedIdentity {
26
+ siteName?: string;
27
+ siteTagline?: string;
28
+ siteDescription?: string;
29
+ keywords?: string;
30
+ }
31
+
18
32
  export interface LocalizedSocialLink {
19
33
  id: number;
20
34
  platform: string;
@@ -23,19 +37,21 @@ export interface LocalizedSocialLink {
23
37
 
24
38
  export interface WebsiteSettings {
25
39
  identifier: string;
26
- siteName?: string;
27
- siteTagline?: string;
28
- siteDescription?: string;
29
- keywords?: string;
30
- contactEmail?: string;
31
- phoneNumber?: string;
32
- physicalAddress?: string;
40
+ // Global settings
41
+ contactEmail?: string; // Usually stays global for system notifications
33
42
  smtpUser?: string;
34
43
  maintenanceMode?: boolean;
44
+ launched?: boolean;
35
45
  launch_date?: string;
36
- socials?: SocialLink[];
37
- localizedContactInfo?: Partial<Record<SupportedLocale, LocalizedContactInfo>>;
38
- localizedSocials?: Partial<Record<SupportedLocale, LocalizedSocialLink[]>>;
46
+
47
+ // Supported languages
48
+ supportedLocales: LocaleDefinition[];
49
+
50
+ // Localized data buckets
51
+ localizedIdentity: Record<string, LocalizedIdentity>;
52
+ localizedContactInfo: Record<string, LocalizedContactInfo>;
53
+ localizedSocials: Record<string, LocalizedSocialLink[]>;
54
+
39
55
  domainLocaleConfig?: DomainLocaleConfig[];
40
56
  updatedAt?: Date;
41
57
  createdAt?: Date;