@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.
- package/dist/131._entry.js +2 -0
- package/dist/131._entry.js.LICENSE.txt +6 -0
- package/dist/303._entry.js +1 -0
- package/dist/_entry.js +2 -0
- package/dist/_entry.js.LICENSE.txt +6 -0
- package/dist/api/handler.d.ts.map +1 -1
- package/dist/api/handler.js +15 -4
- package/dist/remoteEntry.js +1 -0
- package/dist/server.js +232 -0
- package/dist/types/settings.d.ts +18 -11
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/views/Settings/components/ContactInfoSection.d.ts +12 -0
- package/dist/views/Settings/components/ContactInfoSection.d.ts.map +1 -0
- package/dist/views/Settings/components/ContactInfoSection.js +14 -0
- package/dist/views/Settings/components/DomainLanguagesCard.d.ts +8 -0
- package/dist/views/Settings/components/DomainLanguagesCard.d.ts.map +1 -0
- package/dist/views/Settings/components/DomainLanguagesCard.js +12 -0
- package/dist/views/Settings/components/DomainLanguagesModal.d.ts +12 -0
- package/dist/views/Settings/components/DomainLanguagesModal.d.ts.map +1 -0
- package/dist/views/Settings/components/DomainLanguagesModal.js +38 -0
- package/dist/views/Settings/components/FlagIcon.d.ts +6 -0
- package/dist/views/Settings/components/FlagIcon.d.ts.map +1 -0
- package/dist/views/Settings/components/FlagIcon.js +11 -0
- package/dist/views/Settings/components/LaunchDateSection.d.ts +8 -0
- package/dist/views/Settings/components/LaunchDateSection.d.ts.map +1 -0
- package/dist/views/Settings/components/LaunchDateSection.js +22 -0
- package/dist/views/Settings/components/LocalesManagementCard.d.ts +8 -0
- package/dist/views/Settings/components/LocalesManagementCard.d.ts.map +1 -0
- package/dist/views/Settings/components/LocalesManagementCard.js +66 -0
- package/dist/views/Settings/components/LocalizedContentSection.d.ts +12 -0
- package/dist/views/Settings/components/LocalizedContentSection.d.ts.map +1 -0
- package/dist/views/Settings/components/LocalizedContentSection.js +17 -0
- package/dist/views/Settings/components/SeoIdentitySection.d.ts +9 -0
- package/dist/views/Settings/components/SeoIdentitySection.d.ts.map +1 -0
- package/dist/views/Settings/components/SeoIdentitySection.js +12 -0
- package/dist/views/Settings/components/SocialLinksSection.d.ts +13 -0
- package/dist/views/Settings/components/SocialLinksSection.d.ts.map +1 -0
- package/dist/views/Settings/components/SocialLinksSection.js +13 -0
- package/dist/views/Settings/components/StatusSection.d.ts +8 -0
- package/dist/views/Settings/components/StatusSection.d.ts.map +1 -0
- package/dist/views/Settings/components/StatusSection.js +27 -0
- package/dist/views/Settings/constants.d.ts +12 -0
- package/dist/views/Settings/constants.d.ts.map +1 -0
- package/dist/views/Settings/constants.js +23 -0
- package/dist/views/Settings/hooks/useWebsiteSettings.d.ts +24 -0
- package/dist/views/Settings/hooks/useWebsiteSettings.d.ts.map +1 -0
- package/dist/views/Settings/hooks/useWebsiteSettings.js +217 -0
- package/dist/views/Settings/types.d.ts +5 -0
- package/dist/views/Settings/types.d.ts.map +1 -0
- package/dist/views/Settings/types.js +1 -0
- package/dist/views/SettingsView.d.ts +3 -6
- package/dist/views/SettingsView.d.ts.map +1 -1
- package/dist/views/SettingsView.js +59 -262
- package/package.json +14 -10
- package/src/api/handler.ts +41 -4
- package/src/types/settings.ts +28 -12
- package/src/views/Settings/components/ContactInfoSection.tsx +109 -0
- package/src/views/Settings/components/DomainLanguagesCard.tsx +91 -0
- package/src/views/Settings/components/DomainLanguagesModal.tsx +216 -0
- package/src/views/Settings/components/FlagIcon.tsx +20 -0
- package/src/views/Settings/components/LaunchDateSection.tsx +111 -0
- package/src/views/Settings/components/LocalesManagementCard.tsx +198 -0
- package/src/views/Settings/components/LocalizedContentSection.tsx +155 -0
- package/src/views/Settings/components/SeoIdentitySection.tsx +98 -0
- package/src/views/Settings/components/SocialLinksSection.tsx +98 -0
- package/src/views/Settings/components/StatusSection.tsx +181 -0
- package/src/views/Settings/constants.ts +26 -0
- package/src/views/Settings/hooks/useWebsiteSettings.ts +240 -0
- package/src/views/Settings/types.ts +4 -0
- package/src/views/SettingsView.tsx +226 -674
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
4
|
+
import { Globe, Plus, Loader2, X, Sparkles, Info } from 'lucide-react';
|
|
5
|
+
import { GLOBAL_LOCALE_DATABASE } from '../constants';
|
|
6
|
+
import { FlagIcon } from './FlagIcon';
|
|
7
|
+
import { clsx } from 'clsx';
|
|
8
|
+
import { twMerge } from 'tailwind-merge';
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
export function LocalesManagementCard({ supportedLocales, onUpdate }) {
|
|
13
|
+
const [searchCode, setSearchCode] = useState('');
|
|
14
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
15
|
+
const [searchError, setSearchError] = useState('');
|
|
16
|
+
const handleToggleLocale = (locale) => {
|
|
17
|
+
const isEnabled = supportedLocales.some(l => l.code === locale.code);
|
|
18
|
+
if (isEnabled) {
|
|
19
|
+
if (supportedLocales.length <= 1) {
|
|
20
|
+
alert('You must have at least one language enabled.');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
onUpdate(supportedLocales.filter(l => l.code !== locale.code));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
onUpdate([...supportedLocales, locale]);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const handleSearchAndAdd = async () => {
|
|
30
|
+
const code = searchCode.trim().toLowerCase();
|
|
31
|
+
if (!code)
|
|
32
|
+
return;
|
|
33
|
+
if (supportedLocales.some(l => l.code === code || l.countryCode === code)) {
|
|
34
|
+
setSearchError('Language already added');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setIsSearching(true);
|
|
38
|
+
setSearchError('');
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(`https://restcountries.com/v3.1/alpha/${code}`);
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
throw new Error('Country not found');
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
const country = data[0];
|
|
45
|
+
if (country) {
|
|
46
|
+
const newLocale = {
|
|
47
|
+
code: code,
|
|
48
|
+
name: country.name.common,
|
|
49
|
+
countryCode: country.cca2.toLowerCase()
|
|
50
|
+
};
|
|
51
|
+
onUpdate([...supportedLocales, newLocale]);
|
|
52
|
+
setSearchCode('');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
setSearchError('Could not find country code');
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setIsSearching(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return (_jsxs("section", { className: "space-y-8", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner", children: _jsx(Globe, { size: 20 }) }), _jsxs("h3", { className: "text-2xl lg:text-3xl font-bold text-dashboard-text leading-none tracking-tight", children: ["Localization ", _jsx("span", { className: "text-primary", children: "Matrix" })] })] }), _jsxs(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, className: "bg-dashboard-card/50 border border-dashboard-border/40 p-8 rounded-[2rem] space-y-8 relative overflow-hidden", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-3 ml-2", children: [_jsx(Sparkles, { size: 12, className: "text-primary animate-pulse" }), _jsx("span", { className: "text-[10px] font-bold uppercase tracking-[0.2em] text-primary/60", children: "Active Translation Nodes" })] }), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: _jsx(AnimatePresence, { mode: "popLayout", children: supportedLocales.length > 0 ? (supportedLocales.map((loc) => (_jsxs(motion.div, { layout: true, initial: { opacity: 0, scale: 0.95 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.95 }, 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", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("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", children: _jsx(FlagIcon, { countryCode: loc.countryCode }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-sm font-bold text-dashboard-text/90 group-hover:text-primary transition-colors", children: loc.name }), _jsx("span", { className: "text-[10px] text-dashboard-text-secondary/70 font-bold tracking-widest uppercase mt-0.5", children: loc.code })] })] }), _jsx("button", { onClick: () => handleToggleLocale(loc), className: "p-2 text-dashboard-text-secondary/40 hover:text-red-500 hover:bg-red-500/10 rounded-lg opacity-0 group-hover:opacity-100 transition-all active:scale-90", children: _jsx(X, { size: 16 }) })] }, loc.code)))) : (_jsx("div", { className: "col-span-2 py-12 text-center bg-dashboard-card/30 border-dashed border border-dashboard-border/40 rounded-2xl", children: _jsx("p", { className: "text-xs text-dashboard-text-secondary/60 font-medium", children: "No active localization nodes detected." }) })) }) })] }), _jsxs("div", { className: "space-y-4 pt-8 border-t border-dashboard-border/40", children: [_jsxs("div", { className: "flex items-center justify-between px-2", children: [_jsx("span", { className: "text-[10px] font-bold uppercase tracking-[0.2em] text-primary/60", children: "Initialize New Region" }), searchError && _jsx("p", { className: "text-[10px] font-bold text-red-500 animate-pulse", children: searchError })] }), _jsxs("div", { className: "flex gap-3", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx("input", { type: "text", value: searchCode, onChange: (e) => setSearchCode(e.target.value.toUpperCase()), onKeyDown: (e) => e.key === 'Enter' && handleSearchAndAdd(), placeholder: "COUNTRY CODE (e.g. BE, JP, FR)", className: "w-full pl-6 pr-12 py-3 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-xl text-xs font-bold uppercase tracking-wider outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/30 transition-all text-dashboard-text placeholder:text-dashboard-text-secondary/40", maxLength: 3 }), isSearching && (_jsx(Loader2, { className: "absolute right-4 top-1/2 -translate-y-1/2 size-4 animate-spin text-primary" }))] }), _jsx("button", { onClick: handleSearchAndAdd, disabled: isSearching || !searchCode, className: "px-6 bg-primary text-white rounded-xl hover:bg-primary/90 transition-all shadow-lg shadow-primary/10 disabled:opacity-50 active:scale-95 flex items-center justify-center shrink-0", children: _jsx(Plus, { size: 20, strokeWidth: 2.5 }) })] })] }), _jsxs("div", { className: "space-y-4 pt-8 border-t border-dashboard-border/40", children: [_jsx("span", { className: "text-[10px] font-bold uppercase tracking-[0.2em] text-primary/60 ml-2", children: "Recommended Protocols" }), _jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-3", children: GLOBAL_LOCALE_DATABASE
|
|
63
|
+
.filter(loc => !supportedLocales.some(s => s.code === loc.code))
|
|
64
|
+
.slice(0, 6)
|
|
65
|
+
.map((loc) => (_jsxs("button", { onClick: () => handleToggleLocale(loc), className: "flex flex-col items-center gap-2 p-3 bg-dashboard-bg/30 border border-dashboard-border/40 hover:border-primary/40 hover:bg-primary/5 group transition-all duration-500 rounded-xl", children: [_jsx("div", { className: "size-8 rounded-lg bg-dashboard-card/50 flex items-center justify-center border border-dashboard-border/40 group-hover:border-primary/20 transition-all shadow-inner", children: _jsx(FlagIcon, { countryCode: loc.countryCode }) }), _jsx("span", { className: "text-[10px] font-bold text-dashboard-text-secondary/60 group-hover:text-primary transition-colors truncate w-full text-center", children: loc.name })] }, loc.code))) })] }), _jsxs("div", { className: "bg-primary/5 border border-primary/10 p-6 rounded-2xl flex items-start gap-4 group", children: [_jsx("div", { className: "size-8 rounded-lg bg-primary/10 flex items-center justify-center text-primary shrink-0 border border-primary/20", children: _jsx(Info, { size: 16 }) }), _jsx("p", { className: "text-xs text-dashboard-text-secondary/70 font-medium leading-relaxed", children: "JHITS operates on a modular localization engine. Provisioning a region here will automatically synchronize translation schemas and localized data structures across the entire infrastructure." })] })] })] }));
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WebsiteSettings, LocalizedContactInfo } from '../../../types/settings';
|
|
2
|
+
interface LocalizedContentSectionProps {
|
|
3
|
+
settings: WebsiteSettings;
|
|
4
|
+
activeTab: string;
|
|
5
|
+
onUpdateContact: (field: keyof LocalizedContactInfo, value: string) => void;
|
|
6
|
+
onAddSocial: () => void;
|
|
7
|
+
onUpdateSocial: (id: number, field: string, value: string) => void;
|
|
8
|
+
onRemoveSocial: (id: number) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function LocalizedContentSection({ settings, activeTab, onUpdateContact, onAddSocial, onUpdateSocial, onRemoveSocial }: LocalizedContentSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=LocalizedContentSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalizedContentSection.d.ts","sourceRoot":"","sources":["../../../../src/views/Settings/components/LocalizedContentSection.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAoB,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAUlG,UAAU,4BAA4B;IAClC,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,oBAAoB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5E,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,uBAAuB,CAAC,EACpC,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAW,EACX,cAAc,EACd,cAAc,EACjB,EAAE,4BAA4B,2CA6H9B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import { Globe, Plus, Trash2, Smartphone, MapPin, Share2 } from 'lucide-react';
|
|
4
|
+
import { AVAILABLE_PLATFORMS } from '../constants';
|
|
5
|
+
import { clsx } from 'clsx';
|
|
6
|
+
import { twMerge } from 'tailwind-merge';
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
export function LocalizedContentSection({ settings, activeTab, onUpdateContact, onAddSocial, onUpdateSocial, onRemoveSocial }) {
|
|
11
|
+
const contact = settings.localizedContactInfo?.[activeTab] || {};
|
|
12
|
+
const socials = settings.localizedSocials?.[activeTab] || [];
|
|
13
|
+
return (_jsxs("section", { className: "space-y-8", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner", children: _jsx(Share2, { size: 20 }) }), _jsxs("h3", { className: "text-2xl lg:text-3xl font-bold text-dashboard-text leading-none tracking-tight", children: ["Localized ", _jsx("span", { className: "text-primary", children: "Content" })] })] }), _jsxs(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, className: "bg-dashboard-card/50 border border-dashboard-border/40 p-8 rounded-[2rem] space-y-8 relative overflow-hidden", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60", children: ["Communication Vector (", activeTab, ")"] }), _jsxs("div", { className: "relative group", children: [_jsx(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" }), _jsx("input", { type: "tel", value: contact.phoneNumber || '', onChange: (e) => onUpdateContact('phoneNumber', e.target.value), placeholder: "INITIALIZE_PHONE", 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" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60", children: ["Geospatial Coordinates (", activeTab, ")"] }), _jsxs("div", { className: "relative group", children: [_jsx(MapPin, { 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" }), _jsx("input", { type: "text", value: contact.physicalAddress || '', onChange: (e) => onUpdateContact('physicalAddress', e.target.value), placeholder: "STREET_ADDRESS_PROTOCOL", 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" })] })] })] }), _jsxs("div", { className: "pt-8 border-t border-dashboard-border/40", children: [_jsxs("div", { className: "flex items-center justify-between mb-4 ml-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] opacity-60", children: ["Social Ecosystem Nodes (", activeTab, ")"] }), _jsxs("button", { onClick: onAddSocial, className: "flex items-center gap-2 px-4 py-2 bg-primary text-white rounded-xl text-[10px] font-bold uppercase tracking-[0.15em] hover:bg-primary/90 transition-all shadow-lg shadow-primary/10 active:scale-95 group/btn overflow-hidden relative", children: [_jsx("div", { className: "absolute inset-0 bg-linear-to-br from-white/20 to-transparent opacity-0 group-hover/btn:opacity-100 transition-opacity" }), _jsx(Plus, { size: 12, strokeWidth: 3 }), " ", _jsx("span", { children: "Link Node" })] })] }), _jsx("div", { className: "space-y-3", children: _jsx(AnimatePresence, { mode: "popLayout", children: socials.length > 0 ? (socials.map((social, idx) => {
|
|
14
|
+
const platform = AVAILABLE_PLATFORMS.find(p => p.name === social.platform);
|
|
15
|
+
return (_jsxs(motion.div, { layout: true, initial: { opacity: 0, x: -20 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: 20 }, transition: { delay: idx * 0.05 }, className: "flex items-center gap-4 p-3 bg-dashboard-bg/30 border border-dashboard-border/40 rounded-xl group hover:border-primary/30 transition-all duration-500", children: [_jsx("div", { className: "shrink-0 size-10 rounded-lg bg-dashboard-card/50 flex items-center justify-center border border-dashboard-border/40 text-dashboard-text-secondary/40 group-hover:text-primary group-hover:bg-primary/10 group-hover:border-primary/20 transition-all duration-500 shadow-inner", children: platform?.icon || _jsx(Globe, { size: 18 }) }), _jsxs("div", { className: "flex-1 flex flex-col md:flex-row items-center gap-4", children: [_jsx("div", { className: "w-full md:w-32 relative", children: _jsxs("select", { value: social.platform, onChange: (e) => onUpdateSocial(social.id, 'platform', e.target.value), className: "w-full bg-transparent border-none outline-none text-[10px] font-bold uppercase tracking-wider text-dashboard-text/90 cursor-pointer focus:text-primary transition-colors appearance-none", children: [_jsx("option", { value: "", className: "bg-neutral-900", children: "PLATFORM" }), AVAILABLE_PLATFORMS.map(p => (_jsx("option", { value: p.name, className: "bg-neutral-900", children: p.name }, p.name)))] }) }), _jsx("div", { className: "hidden md:block h-5 w-px bg-dashboard-border/40" }), _jsx("input", { type: "url", value: social.url, onChange: (e) => onUpdateSocial(social.id, 'url', e.target.value), placeholder: "URL_ENDPOINT", className: "flex-1 bg-transparent border-none outline-none text-xs font-bold text-dashboard-text/90 placeholder:text-dashboard-text-secondary/30 w-full" })] }), _jsx("button", { onClick: () => onRemoveSocial(social.id), className: "p-2 text-dashboard-text-secondary/40 hover:text-red-500 hover:bg-red-500/10 rounded-lg opacity-0 group-hover:opacity-100 transition-all active:scale-90", children: _jsx(Trash2, { size: 16 }) })] }, social.id));
|
|
16
|
+
})) : (_jsx("div", { className: "text-center py-12 bg-dashboard-card/20 border border-dashed border-dashboard-border/40 rounded-2xl", children: _jsx("p", { className: "text-[10px] text-dashboard-text-secondary/40 font-bold uppercase tracking-[0.2em]", children: "No social platforms initialized" }) })) }) })] })] })] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WebsiteSettings, LocalizedIdentity } from '../../../types/settings';
|
|
2
|
+
interface SeoIdentitySectionProps {
|
|
3
|
+
settings: WebsiteSettings;
|
|
4
|
+
activeTab: string;
|
|
5
|
+
onUpdateLocalized: (field: keyof LocalizedIdentity, value: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function SeoIdentitySection({ settings, activeTab, onUpdateLocalized }: SeoIdentitySectionProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=SeoIdentitySection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SeoIdentitySection.d.ts","sourceRoot":"","sources":["../../../../src/views/Settings/components/SeoIdentitySection.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAS7E,UAAU,uBAAuB;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,iBAAiB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9E;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,iBAAiB,EACpB,EAAE,uBAAuB,2CA2EzB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
3
|
+
import { Search, Sparkles, Fingerprint } from 'lucide-react';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
function cn(...inputs) {
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
export function SeoIdentitySection({ settings, activeTab, onUpdateLocalized }) {
|
|
10
|
+
const identity = settings.localizedIdentity?.[activeTab] || {};
|
|
11
|
+
return (_jsxs("section", { className: "space-y-8", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner", children: _jsx(Fingerprint, { size: 20 }) }), _jsxs("h3", { className: "text-2xl lg:text-3xl font-bold text-dashboard-text leading-none tracking-tight", children: ["Identity ", _jsx("span", { className: "text-primary", children: "& SEO" })] })] }), _jsxs(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, className: "bg-dashboard-card/50 border border-dashboard-border/40 p-8 rounded-[2rem] space-y-6 relative overflow-hidden", children: [_jsx("div", { className: "absolute top-0 right-0 w-64 h-64 blur-3xl opacity-5 bg-primary pointer-events-none" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between ml-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] opacity-60", children: ["System Name (", activeTab, ")"] }), _jsx(Sparkles, { size: 10, className: "text-primary animate-pulse" })] }), _jsx("input", { type: "text", value: identity.siteName || '', onChange: (e) => onUpdateLocalized('siteName', e.target.value), placeholder: "INITIALIZE_NAME", className: "w-full px-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" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60", children: ["Strategic Tagline (", activeTab, ")"] }), _jsx("input", { type: "text", value: identity.siteTagline || '', onChange: (e) => onUpdateLocalized('siteTagline', e.target.value), placeholder: "DEFINE_TAGLINE", className: "w-full px-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" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60", children: ["Global Description Protocol (", activeTab, ")"] }), _jsx("textarea", { value: identity.siteDescription || '', onChange: (e) => onUpdateLocalized('siteDescription', e.target.value), placeholder: "A comprehensive architectural description for search engine crawlers...", rows: 3, className: "w-full px-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" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-[10px] font-bold text-dashboard-text-secondary/70 uppercase tracking-[0.15em] ml-2 opacity-60", children: ["Indexing Keywords (", activeTab, ")"] }), _jsxs("div", { className: "relative group", children: [_jsx(Search, { 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" }), _jsx("input", { type: "text", value: identity.keywords || '', onChange: (e) => onUpdateLocalized('keywords', e.target.value), placeholder: "comma, separated, identifiers", 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" })] }), _jsx("p", { className: "text-[10px] text-dashboard-text-secondary/60 ml-4 font-medium opacity-60", children: "Separated by commas. These tokens facilitate high-signal discovery during search engine scanning." })] })] })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { WebsiteSettings, LocaleDefinition } from '../../../types/settings';
|
|
2
|
+
interface SocialLinksSectionProps {
|
|
3
|
+
settings: WebsiteSettings;
|
|
4
|
+
activeTab: string;
|
|
5
|
+
onTabChange: (code: string) => void;
|
|
6
|
+
activeLocales: LocaleDefinition[];
|
|
7
|
+
onAdd: () => void;
|
|
8
|
+
onUpdate: (id: number, field: string, value: string) => void;
|
|
9
|
+
onRemove: (id: number) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function SocialLinksSection({ settings, activeTab, onTabChange, activeLocales, onAdd, onUpdate, onRemove }: SocialLinksSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=SocialLinksSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SocialLinksSection.d.ts","sourceRoot":"","sources":["../../../../src/views/Settings/components/SocialLinksSection.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAIjG,UAAU,uBAAuB;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,WAAW,EACX,aAAa,EACb,KAAK,EACL,QAAQ,EACR,QAAQ,EACX,EAAE,uBAAuB,2CAyEzB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Globe, X } from 'lucide-react';
|
|
3
|
+
import { AVAILABLE_PLATFORMS } from '../constants';
|
|
4
|
+
import { FlagIcon } from './FlagIcon';
|
|
5
|
+
export function SocialLinksSection({ settings, activeTab, onTabChange, activeLocales, onAdd, onUpdate, onRemove }) {
|
|
6
|
+
const currentSocials = settings.localizedSocials?.[activeTab] || [];
|
|
7
|
+
return (_jsxs("section", { className: "bg-dashboard-card/50 p-6 rounded-[2rem] border border-dashboard-border/40", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-dashboard-border/40 pb-4 mb-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-xl font-bold text-dashboard-text leading-none tracking-tight", children: [_jsx(Globe, { size: 18, className: "text-primary" }), "Social ", _jsx("span", { className: "text-primary", children: "Links" }), " (", activeTab.toUpperCase(), ")"] }), _jsx("button", { onClick: onAdd, className: "px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider bg-primary text-white rounded-xl hover:bg-primary/90 transition-all shadow-lg shadow-primary/10", children: "Add Social" })] }), _jsx("div", { className: "flex gap-2 mb-4 overflow-x-auto pb-2 custom-scrollbar", children: activeLocales.map((loc) => (_jsxs("button", { onClick: () => onTabChange(loc.code), className: `px-3 py-1.5 rounded-xl text-[10px] font-bold uppercase tracking-wider transition-all flex items-center gap-2 shrink-0 border ${activeTab === loc.code
|
|
8
|
+
? 'bg-primary text-white border-primary shadow-lg shadow-primary/10'
|
|
9
|
+
: 'bg-dashboard-bg/30 text-dashboard-text-secondary/50 border-dashboard-border/40 hover:border-primary/30'}`, children: [_jsx(FlagIcon, { code: loc.code, countryCode: loc.countryCode }), loc.code.toUpperCase()] }, loc.code))) }), _jsx("div", { className: "space-y-3", children: currentSocials.length > 0 ? (currentSocials.map((social) => {
|
|
10
|
+
const platform = AVAILABLE_PLATFORMS.find(p => p.name === social.platform);
|
|
11
|
+
return (_jsxs("div", { className: "flex items-center gap-3 p-3 bg-dashboard-bg/30 rounded-xl border border-dashboard-border/40", children: [_jsx("div", { className: "flex-shrink-0 text-dashboard-text-secondary/40", children: platform?.icon || _jsx(Globe, { size: 16 }) }), _jsxs("select", { value: social.platform, onChange: (e) => onUpdate(social.id, 'platform', e.target.value), className: "flex-1 px-3 py-1.5 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-lg outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 text-sm font-bold text-dashboard-text/90 cursor-pointer appearance-none", children: [_jsx("option", { value: "", className: "bg-neutral-900", children: "PLATFORM" }), AVAILABLE_PLATFORMS.map(p => (_jsx("option", { value: p.name, className: "bg-neutral-900", children: p.name }, p.name)))] }), _jsx("input", { type: "url", value: social.url, onChange: (e) => onUpdate(social.id, 'url', e.target.value), placeholder: "https://...", className: "flex-[2] px-3 py-1.5 bg-dashboard-bg/50 border border-dashboard-border/40 rounded-lg outline-none focus:ring-2 focus:ring-primary/10 focus:border-primary/30 text-sm font-bold text-dashboard-text/90 placeholder:text-dashboard-text-secondary/30" }), _jsx("button", { onClick: () => onRemove(social.id), className: "p-2 text-dashboard-text-secondary/40 hover:text-red-500 hover:bg-red-500/10 rounded-lg transition-all", children: _jsx(X, { size: 14 }) })] }, social.id));
|
|
12
|
+
})) : (_jsxs("p", { className: "text-[10px] text-dashboard-text-secondary/50 font-bold uppercase tracking-wider text-center py-8 bg-dashboard-card/20 border border-dashed border-dashboard-border/40 rounded-xl", children: ["No social links added for ", activeTab.toUpperCase()] })) })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { WebsiteSettings } from '../../../types/settings';
|
|
2
|
+
interface StatusSectionProps {
|
|
3
|
+
settings: WebsiteSettings;
|
|
4
|
+
onUpdate: (updates: Partial<WebsiteSettings>) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function StatusSection({ settings, onUpdate }: StatusSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=StatusSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusSection.d.ts","sourceRoot":"","sources":["../../../../src/views/Settings/components/StatusSection.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAQ1D,UAAU,kBAAkB;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,2CA8IvE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
3
|
+
import { Settings2, Rocket, ShieldAlert, Cpu } from 'lucide-react';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
function cn(...inputs) {
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
export function StatusSection({ settings, onUpdate }) {
|
|
10
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
11
|
+
const handleToggleLaunch = () => {
|
|
12
|
+
const newLaunched = !settings.launched;
|
|
13
|
+
onUpdate({
|
|
14
|
+
launched: newLaunched,
|
|
15
|
+
maintenanceMode: newLaunched ? false : settings.maintenanceMode
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs("section", { className: "space-y-12", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx("div", { className: "size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary border border-primary/20 shadow-inner", children: _jsx(Settings2, { size: 28 }) }), _jsxs("h3", { className: "text-4xl lg:text-5xl font-bold text-dashboard-text leading-none", children: ["Site ", _jsx("span", { className: "text-primary", children: "Status" })] })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-8", children: [_jsxs(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, className: "bg-dashboard-card/50 border border-dashboard-border/50 p-10 rounded-[3rem] group relative overflow-hidden", children: [_jsx("div", { className: "absolute top-0 right-0 w-32 h-32 blur-3xl opacity-0 group-hover:opacity-10 transition-opacity bg-primary pointer-events-none" }), _jsxs("div", { className: "flex items-center justify-between mb-10", children: [_jsx("div", { className: cn("size-16 rounded-2xl flex items-center justify-center transition-all duration-500 border relative overflow-hidden shadow-inner", settings.launched
|
|
19
|
+
? "bg-emerald-500/10 border-emerald-500/20 text-emerald-500"
|
|
20
|
+
: "bg-neutral-500/5 border-neutral-500/10 text-neutral-400"), children: _jsx(Rocket, { size: 28, className: cn("relative z-10 transition-transform duration-500 group-hover:scale-110", settings.launched && "animate-pulse") }) }), _jsx("button", { onClick: handleToggleLaunch, className: cn("relative w-16 h-8 rounded-full transition-all duration-500 p-1 shadow-inner", settings.launched ? "bg-emerald-500" : "bg-neutral-200 dark:bg-neutral-800"), children: _jsx("div", { className: cn("w-6 h-6 bg-white rounded-full transition-transform duration-500 shadow-lg", settings.launched ? "translate-x-8" : "translate-x-0") }) })] }), _jsxs("div", { children: [_jsx("span", { className: "text-[10px] font-bold text-primary uppercase tracking-[0.3em] mb-3 block opacity-60", children: "Deployment Vector" }), _jsx("h4", { className: "text-2xl font-bold text-dashboard-text mb-3", children: settings.launched ? 'System Published' : 'Private Mode' }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400 font-medium leading-relaxed", children: settings.launched
|
|
21
|
+
? 'Your website is live and visible to all users across the world.'
|
|
22
|
+
: 'The website is currently restricted to internal use and development.' })] })] }), _jsxs(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, transition: { delay: 0.1 }, className: cn("bg-dashboard-card/50 border border-dashboard-border/50 p-10 rounded-[3rem] group relative overflow-hidden transition-all duration-500", settings.maintenanceMode && "border-orange-500/30 bg-orange-500/10"), children: [_jsx("div", { className: "absolute top-0 right-0 w-32 h-32 blur-3xl opacity-0 group-hover:opacity-10 transition-opacity bg-orange-500 pointer-events-none" }), _jsxs("div", { className: "flex items-center justify-between mb-10", children: [_jsx("div", { className: cn("size-16 rounded-2xl flex items-center justify-center transition-all duration-500 border relative overflow-hidden shadow-inner", settings.maintenanceMode
|
|
23
|
+
? "bg-orange-500/10 border-orange-500/20 text-orange-500"
|
|
24
|
+
: "bg-neutral-500/5 border-neutral-500/10 text-neutral-400"), children: _jsx(ShieldAlert, { size: 28, className: "relative z-10 transition-transform duration-500 group-hover:scale-110" }) }), _jsx("button", { onClick: () => onUpdate({ maintenanceMode: !settings.maintenanceMode }), className: cn("relative w-16 h-8 rounded-full transition-all duration-500 p-1 shadow-inner", settings.maintenanceMode ? "bg-orange-500" : "bg-neutral-200 dark:bg-neutral-800"), children: _jsx("div", { className: cn("w-6 h-6 bg-white rounded-full transition-transform duration-500 shadow-lg", settings.maintenanceMode ? "translate-x-8" : "translate-x-0") }) })] }), _jsxs("div", { children: [_jsx("span", { className: "text-[10px] font-bold text-orange-500 uppercase tracking-[0.3em] mb-3 block opacity-60", children: "Operational Mode" }), _jsx("h4", { className: "text-2xl font-bold text-dashboard-text mb-3", children: "Maintenance" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400 font-medium leading-relaxed", children: settings.maintenanceMode
|
|
25
|
+
? 'Visitors see a maintenance screen. You can still access the dashboard.'
|
|
26
|
+
: 'System is running normally. All features are available to public users.' })] })] })] }), isDev && (_jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { delay: 0.2 }, className: "bg-dashboard-card/50 border border-dashboard-border/50 p-10 rounded-[3rem] relative overflow-hidden group", children: [_jsx("div", { className: "absolute top-0 right-0 p-10 opacity-[0.03] group-hover:opacity-[0.07] transition-opacity", children: _jsx(Cpu, { size: 160 }) }), _jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-8 relative z-10", children: [_jsx("div", { className: "size-14 rounded-2xl bg-primary/10 flex items-center justify-center text-primary shrink-0 border border-primary/20", children: _jsx(Cpu, { size: 24 }) }), _jsxs("div", { children: [_jsx("span", { className: "text-[10px] font-bold uppercase tracking-[0.4em] text-primary mb-2 block", children: "Developer Engine Protocol" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400 font-medium leading-relaxed max-w-3xl", children: "Authentication and status checks are bypassed during development. Changes made here will affect the production environment once deployed." })] })] })] }))] }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { LocaleDefinition } from '../../types/settings';
|
|
3
|
+
/**
|
|
4
|
+
* Popular Locale Suggestions
|
|
5
|
+
* These are used for "Quick Add" in the UI.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GLOBAL_LOCALE_DATABASE: LocaleDefinition[];
|
|
8
|
+
export declare const AVAILABLE_PLATFORMS: {
|
|
9
|
+
name: string;
|
|
10
|
+
icon: React.FunctionComponentElement<import("react-icons/lib").IconBaseProps>;
|
|
11
|
+
}[];
|
|
12
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/views/Settings/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,gBAAgB,EAOpD,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;GAO/B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FaFacebook, FaInstagram, FaLinkedin, FaPinterest, FaTiktok } from 'react-icons/fa';
|
|
3
|
+
import { FaXTwitter } from 'react-icons/fa6';
|
|
4
|
+
/**
|
|
5
|
+
* Popular Locale Suggestions
|
|
6
|
+
* These are used for "Quick Add" in the UI.
|
|
7
|
+
*/
|
|
8
|
+
export const GLOBAL_LOCALE_DATABASE = [
|
|
9
|
+
{ code: 'en', name: 'English', countryCode: 'gb' },
|
|
10
|
+
{ code: 'sv', name: 'Swedish', countryCode: 'se' },
|
|
11
|
+
{ code: 'nl', name: 'Dutch', countryCode: 'nl' },
|
|
12
|
+
{ code: 'de', name: 'German', countryCode: 'de' },
|
|
13
|
+
{ code: 'fr', name: 'French', countryCode: 'fr' },
|
|
14
|
+
{ code: 'es', name: 'Spanish', countryCode: 'es' },
|
|
15
|
+
];
|
|
16
|
+
export const AVAILABLE_PLATFORMS = [
|
|
17
|
+
{ name: 'Instagram', icon: React.createElement(FaInstagram, { size: 18 }) },
|
|
18
|
+
{ name: 'Facebook', icon: React.createElement(FaFacebook, { size: 18 }) },
|
|
19
|
+
{ name: 'Twitter', icon: React.createElement(FaXTwitter, { size: 18 }) },
|
|
20
|
+
{ name: 'Pinterest', icon: React.createElement(FaPinterest, { size: 18 }) },
|
|
21
|
+
{ name: 'LinkedIn', icon: React.createElement(FaLinkedin, { size: 18 }) },
|
|
22
|
+
{ name: 'TikTok', icon: React.createElement(FaTiktok, { size: 18 }) },
|
|
23
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { WebsiteSettings, LocalizedContactInfo, DomainLocaleConfig, LocaleDefinition, LocalizedIdentity } from '../../../types/settings';
|
|
2
|
+
export declare function useWebsiteSettings(siteId: string): {
|
|
3
|
+
settings: WebsiteSettings;
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
isSaving: boolean;
|
|
6
|
+
isDirty: boolean;
|
|
7
|
+
showSuccess: boolean;
|
|
8
|
+
activeTab: string;
|
|
9
|
+
setActiveTab: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
10
|
+
activeLocales: LocaleDefinition[];
|
|
11
|
+
showDomainModal: boolean;
|
|
12
|
+
setShowDomainModal: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
13
|
+
handleUpdateSettings: (updates: Partial<WebsiteSettings>) => void;
|
|
14
|
+
handleUpdateLocalizedIdentity: (field: keyof LocalizedIdentity, value: string) => void;
|
|
15
|
+
handleUpdateLocalizedContact: (field: keyof LocalizedContactInfo, value: string) => void;
|
|
16
|
+
handleAddLocalizedSocial: () => void;
|
|
17
|
+
handleUpdateLocalizedSocial: (id: number, field: string, value: string) => void;
|
|
18
|
+
handleRemoveLocalizedSocial: (id: number) => void;
|
|
19
|
+
handleUpdateLocales: (locales: LocaleDefinition[]) => void;
|
|
20
|
+
handleSave: () => Promise<void>;
|
|
21
|
+
handleReset: () => void;
|
|
22
|
+
saveDomainConfigAndClose: (config: DomainLocaleConfig[]) => Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=useWebsiteSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebsiteSettings.d.ts","sourceRoot":"","sources":["../../../../src/views/Settings/hooks/useWebsiteSettings.ts"],"names":[],"mappings":"AACA,OAAO,EACH,eAAe,EACf,oBAAoB,EAEpB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACpB,MAAM,yBAAyB,CAAC;AAEjC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;oCAqFN,OAAO,CAAC,eAAe,CAAC;2CAIjB,MAAM,iBAAiB,SAAS,MAAM;0CAWvC,MAAM,oBAAoB,SAAS,MAAM;;sCAwB7C,MAAM,SAAS,MAAM,SAAS,MAAM;sCAYpC,MAAM;mCAYT,gBAAgB,EAAE;;;uCAqCR,kBAAkB,EAAE;EAyCvE"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
export function useWebsiteSettings(siteId) {
|
|
3
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
4
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
5
|
+
const [showSuccess, setShowSuccess] = useState(false);
|
|
6
|
+
const [activeTab, setActiveTab] = useState('');
|
|
7
|
+
const [showDomainModal, setShowDomainModal] = useState(false);
|
|
8
|
+
// Initial data from server to compare for "dirty" state
|
|
9
|
+
const [initialSettings, setInitialSettings] = useState('');
|
|
10
|
+
const [settings, setSettings] = useState({
|
|
11
|
+
identifier: 'site_config',
|
|
12
|
+
contactEmail: '',
|
|
13
|
+
smtpUser: '',
|
|
14
|
+
maintenanceMode: false,
|
|
15
|
+
launched: false,
|
|
16
|
+
launch_date: '',
|
|
17
|
+
supportedLocales: [],
|
|
18
|
+
localizedIdentity: {},
|
|
19
|
+
localizedContactInfo: {},
|
|
20
|
+
localizedSocials: {},
|
|
21
|
+
domainLocaleConfig: [],
|
|
22
|
+
});
|
|
23
|
+
const isDirty = initialSettings !== JSON.stringify(settings);
|
|
24
|
+
const activeLocales = settings.supportedLocales;
|
|
25
|
+
// Ensure activeTab is always valid
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (activeLocales.length > 0) {
|
|
28
|
+
if (!activeTab || !activeLocales.some(l => l.code === activeTab)) {
|
|
29
|
+
setActiveTab(activeLocales[0].code);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, [activeLocales, activeTab]);
|
|
33
|
+
// Fetch settings on load
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const fetchSettings = async () => {
|
|
36
|
+
try {
|
|
37
|
+
setIsLoading(true);
|
|
38
|
+
const response = await fetch('/api/plugin-website/settings', { credentials: 'include' });
|
|
39
|
+
if (!response.ok)
|
|
40
|
+
throw new Error('Failed to fetch settings');
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
if (data && data.identifier) {
|
|
43
|
+
let launchDate = '';
|
|
44
|
+
if (data.launch_date) {
|
|
45
|
+
const date = new Date(data.launch_date);
|
|
46
|
+
if (!isNaN(date.getTime())) {
|
|
47
|
+
const year = date.getFullYear();
|
|
48
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
49
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
50
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
51
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
52
|
+
launchDate = `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const loaded = {
|
|
56
|
+
...data,
|
|
57
|
+
launch_date: launchDate,
|
|
58
|
+
supportedLocales: data.supportedLocales || [],
|
|
59
|
+
localizedIdentity: data.localizedIdentity || {},
|
|
60
|
+
localizedContactInfo: data.localizedContactInfo || {},
|
|
61
|
+
localizedSocials: data.localizedSocials || {},
|
|
62
|
+
domainLocaleConfig: data.domainLocaleConfig || [],
|
|
63
|
+
};
|
|
64
|
+
setSettings(loaded);
|
|
65
|
+
setInitialSettings(JSON.stringify(loaded));
|
|
66
|
+
if (loaded.supportedLocales.length > 0) {
|
|
67
|
+
setActiveTab(loaded.supportedLocales[0].code);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error('Failed to load settings:', error);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
fetchSettings();
|
|
79
|
+
}, [siteId]);
|
|
80
|
+
const handleUpdateSettings = (updates) => {
|
|
81
|
+
setSettings(prev => ({ ...prev, ...updates }));
|
|
82
|
+
};
|
|
83
|
+
const handleUpdateLocalizedIdentity = (field, value) => {
|
|
84
|
+
if (!activeTab)
|
|
85
|
+
return;
|
|
86
|
+
setSettings(prev => ({
|
|
87
|
+
...prev,
|
|
88
|
+
localizedIdentity: {
|
|
89
|
+
...prev.localizedIdentity,
|
|
90
|
+
[activeTab]: { ...(prev.localizedIdentity?.[activeTab] || {}), [field]: value },
|
|
91
|
+
},
|
|
92
|
+
}));
|
|
93
|
+
};
|
|
94
|
+
const handleUpdateLocalizedContact = (field, value) => {
|
|
95
|
+
if (!activeTab)
|
|
96
|
+
return;
|
|
97
|
+
setSettings(prev => ({
|
|
98
|
+
...prev,
|
|
99
|
+
localizedContactInfo: {
|
|
100
|
+
...prev.localizedContactInfo,
|
|
101
|
+
[activeTab]: { ...(prev.localizedContactInfo?.[activeTab] || {}), [field]: value },
|
|
102
|
+
},
|
|
103
|
+
}));
|
|
104
|
+
};
|
|
105
|
+
const handleAddLocalizedSocial = () => {
|
|
106
|
+
if (!activeTab)
|
|
107
|
+
return;
|
|
108
|
+
const currentSocials = settings.localizedSocials?.[activeTab] || [];
|
|
109
|
+
const newId = Date.now();
|
|
110
|
+
setSettings(prev => ({
|
|
111
|
+
...prev,
|
|
112
|
+
localizedSocials: {
|
|
113
|
+
...prev.localizedSocials,
|
|
114
|
+
[activeTab]: [...currentSocials, { id: newId, platform: '', url: '' }],
|
|
115
|
+
},
|
|
116
|
+
}));
|
|
117
|
+
};
|
|
118
|
+
const handleUpdateLocalizedSocial = (id, field, value) => {
|
|
119
|
+
if (!activeTab)
|
|
120
|
+
return;
|
|
121
|
+
const currentSocials = settings.localizedSocials?.[activeTab] || [];
|
|
122
|
+
setSettings(prev => ({
|
|
123
|
+
...prev,
|
|
124
|
+
localizedSocials: {
|
|
125
|
+
...prev.localizedSocials,
|
|
126
|
+
[activeTab]: currentSocials.map(s => s.id === id ? { ...s, [field]: value } : s),
|
|
127
|
+
},
|
|
128
|
+
}));
|
|
129
|
+
};
|
|
130
|
+
const handleRemoveLocalizedSocial = (id) => {
|
|
131
|
+
if (!activeTab)
|
|
132
|
+
return;
|
|
133
|
+
const currentSocials = settings.localizedSocials?.[activeTab] || [];
|
|
134
|
+
setSettings(prev => ({
|
|
135
|
+
...prev,
|
|
136
|
+
localizedSocials: {
|
|
137
|
+
...prev.localizedSocials,
|
|
138
|
+
[activeTab]: currentSocials.filter(s => s.id !== id),
|
|
139
|
+
},
|
|
140
|
+
}));
|
|
141
|
+
};
|
|
142
|
+
const handleUpdateLocales = (locales) => {
|
|
143
|
+
setSettings(prev => ({ ...prev, supportedLocales: locales }));
|
|
144
|
+
};
|
|
145
|
+
const handleReset = () => {
|
|
146
|
+
if (initialSettings) {
|
|
147
|
+
setSettings(JSON.parse(initialSettings));
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const handleSave = async () => {
|
|
151
|
+
if (settings.supportedLocales.length === 0) {
|
|
152
|
+
alert('At least one language must be enabled.');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
setIsSaving(true);
|
|
157
|
+
const response = await fetch('/api/plugin-website/settings', {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: { 'Content-Type': 'application/json' },
|
|
160
|
+
credentials: 'include',
|
|
161
|
+
body: JSON.stringify(settings),
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok)
|
|
164
|
+
throw new Error('Failed to save settings');
|
|
165
|
+
setInitialSettings(JSON.stringify(settings));
|
|
166
|
+
setShowSuccess(true);
|
|
167
|
+
setTimeout(() => setShowSuccess(false), 3000);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
console.error('Failed to save settings:', error);
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
setIsSaving(false);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const saveDomainConfigAndClose = async (config) => {
|
|
177
|
+
const newSettings = { ...settings, domainLocaleConfig: config };
|
|
178
|
+
setSettings(newSettings);
|
|
179
|
+
setShowDomainModal(false);
|
|
180
|
+
try {
|
|
181
|
+
await fetch('/api/plugin-website/settings', {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: { 'Content-Type': 'application/json' },
|
|
184
|
+
credentials: 'include',
|
|
185
|
+
body: JSON.stringify(newSettings),
|
|
186
|
+
});
|
|
187
|
+
setInitialSettings(JSON.stringify(newSettings));
|
|
188
|
+
setShowSuccess(true);
|
|
189
|
+
setTimeout(() => setShowSuccess(false), 3000);
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
console.error('Auto-save failed');
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
settings,
|
|
197
|
+
isLoading,
|
|
198
|
+
isSaving,
|
|
199
|
+
isDirty,
|
|
200
|
+
showSuccess,
|
|
201
|
+
activeTab,
|
|
202
|
+
setActiveTab,
|
|
203
|
+
activeLocales,
|
|
204
|
+
showDomainModal,
|
|
205
|
+
setShowDomainModal,
|
|
206
|
+
handleUpdateSettings,
|
|
207
|
+
handleUpdateLocalizedIdentity,
|
|
208
|
+
handleUpdateLocalizedContact,
|
|
209
|
+
handleAddLocalizedSocial,
|
|
210
|
+
handleUpdateLocalizedSocial,
|
|
211
|
+
handleRemoveLocalizedSocial,
|
|
212
|
+
handleUpdateLocales,
|
|
213
|
+
handleSave,
|
|
214
|
+
handleReset,
|
|
215
|
+
saveDomainConfigAndClose
|
|
216
|
+
};
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/views/Settings/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Website Settings View
|
|
3
|
-
*
|
|
3
|
+
* Refactored modular interface with sidebar navigation
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
locale: string;
|
|
8
|
-
}
|
|
9
|
-
export declare function SettingsView({ siteId, locale }: SettingsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
import { SettingsViewProps } from './Settings/types';
|
|
6
|
+
export declare function SettingsView({ siteId }: SettingsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
10
7
|
//# sourceMappingURL=SettingsView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsView.d.ts","sourceRoot":"","sources":["../../src/views/SettingsView.tsx"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"SettingsView.d.ts","sourceRoot":"","sources":["../../src/views/SettingsView.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAerD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA+PzD"}
|