@jhits/plugin-website 0.0.4 → 0.0.6

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.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Website Settings API Handler
3
+ * Handles GET and POST requests for website settings
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ import { WebsiteApiConfig } from '../types/settings';
7
+ export type { WebsiteApiConfig } from '../types/settings';
8
+ /**
9
+ * GET /api/plugin-website/settings - Get website settings
10
+ */
11
+ export declare function GET_SETTINGS(req: NextRequest, config: WebsiteApiConfig): Promise<NextResponse>;
12
+ /**
13
+ * POST /api/plugin-website/settings - Update website settings
14
+ */
15
+ export declare function POST_SETTINGS(req: NextRequest, config: WebsiteApiConfig): Promise<NextResponse>;
16
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/api/handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;GAEG;AACH,wBAAsB,YAAY,CAC9B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC,CA6CvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAC/B,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC,CAmFvB"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Website Settings API Handler
3
+ * Handles GET and POST requests for website settings
4
+ */
5
+ 'use server';
6
+ import { NextResponse } from 'next/server';
7
+ /**
8
+ * GET /api/plugin-website/settings - Get website settings
9
+ */
10
+ export async function GET_SETTINGS(req, config) {
11
+ try {
12
+ const dbConnection = await config.getDb();
13
+ const db = dbConnection.db();
14
+ const settings = db.collection('settings');
15
+ const siteConfig = await settings.findOne({ identifier: 'site_config' });
16
+ if (!siteConfig) {
17
+ // Return default settings if none exist
18
+ return NextResponse.json({
19
+ identifier: 'site_config',
20
+ siteName: '',
21
+ siteTagline: '',
22
+ siteDescription: '',
23
+ keywords: '',
24
+ contactEmail: '',
25
+ phoneNumber: '',
26
+ physicalAddress: '',
27
+ smtpUser: '',
28
+ maintenanceMode: false,
29
+ launch_date: '',
30
+ socials: [],
31
+ });
32
+ }
33
+ // Convert launch_date from Date to ISO string if it exists
34
+ const responseData = { ...siteConfig };
35
+ if (responseData.launch_date instanceof Date) {
36
+ responseData.launch_date = responseData.launch_date.toISOString();
37
+ }
38
+ else if (responseData.launch_date) {
39
+ // If it's already a string, keep it as is
40
+ responseData.launch_date = responseData.launch_date;
41
+ }
42
+ else {
43
+ responseData.launch_date = '';
44
+ }
45
+ return NextResponse.json(responseData);
46
+ }
47
+ catch (error) {
48
+ console.error('[WebsiteAPI] GET_SETTINGS error:', error);
49
+ return NextResponse.json({ error: 'Failed to fetch settings', detail: error.message }, { status: 500 });
50
+ }
51
+ }
52
+ /**
53
+ * POST /api/plugin-website/settings - Update website settings
54
+ */
55
+ export async function POST_SETTINGS(req, config) {
56
+ try {
57
+ const userId = await config.getUserId?.(req);
58
+ if (!userId) {
59
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
60
+ }
61
+ const body = await req.json();
62
+ console.log('[WebsiteAPI] POST_SETTINGS received body:', {
63
+ launch_date: body.launch_date,
64
+ launch_date_type: typeof body.launch_date,
65
+ has_launch_date: 'launch_date' in body,
66
+ body_keys: Object.keys(body),
67
+ });
68
+ const dbConnection = await config.getDb();
69
+ const db = dbConnection.db();
70
+ const settings = db.collection('settings');
71
+ // Remove _id and launch_date from updateData (we'll handle launch_date separately)
72
+ const { _id, launch_date, ...updateData } = body;
73
+ // Prepare base update operations
74
+ const updateOps = {
75
+ $set: {
76
+ identifier: 'site_config',
77
+ ...updateData,
78
+ updatedAt: new Date(),
79
+ }
80
+ };
81
+ // Handle launch_date separately
82
+ if (launch_date !== undefined && launch_date !== null && launch_date !== '') {
83
+ // Convert launch_date from datetime-local string to Date if provided
84
+ const date = new Date(launch_date);
85
+ console.log('[WebsiteAPI] Date conversion:', {
86
+ input: launch_date,
87
+ parsed: date,
88
+ isValid: !isNaN(date.getTime()),
89
+ iso: !isNaN(date.getTime()) ? date.toISOString() : 'invalid',
90
+ });
91
+ if (!isNaN(date.getTime())) {
92
+ updateOps.$set.launch_date = date;
93
+ console.log('[WebsiteAPI] Setting launch_date to:', date.toISOString());
94
+ }
95
+ else {
96
+ console.warn('[WebsiteAPI] Invalid date, removing launch_date field');
97
+ updateOps.$unset = { launch_date: '' };
98
+ }
99
+ }
100
+ else {
101
+ // Remove launch_date field if it's empty, undefined, or null
102
+ console.log('[WebsiteAPI] launch_date is empty/undefined, removing field');
103
+ updateOps.$unset = { launch_date: '' };
104
+ }
105
+ console.log('[WebsiteAPI] Update operations:', JSON.stringify(updateOps, (key, value) => {
106
+ if (value instanceof Date) {
107
+ return value.toISOString();
108
+ }
109
+ return value;
110
+ }, 2));
111
+ // Upsert settings
112
+ const result = await settings.updateOne({ identifier: 'site_config' }, updateOps, { upsert: true });
113
+ console.log('[WebsiteAPI] Update result:', {
114
+ matchedCount: result.matchedCount,
115
+ modifiedCount: result.modifiedCount,
116
+ upsertedCount: result.upsertedCount,
117
+ });
118
+ return NextResponse.json({ success: true, message: 'Settings updated successfully' });
119
+ }
120
+ catch (error) {
121
+ console.error('[WebsiteAPI] POST_SETTINGS error:', error);
122
+ return NextResponse.json({ error: 'Failed to update settings', detail: error.message }, { status: 500 });
123
+ }
124
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Website Plugin API Router
3
+ * Routes API requests to appropriate handlers
4
+ */
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ import { WebsiteApiConfig } from '../types/settings';
7
+ /**
8
+ * Handle website API requests
9
+ */
10
+ export declare function handleWebsiteApi(req: NextRequest, path: string[], config: WebsiteApiConfig): Promise<NextResponse>;
11
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD;;GAEG;AACH,wBAAsB,gBAAgB,CAClC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC,CA2BvB"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Website Plugin API Router
3
+ * Routes API requests to appropriate handlers
4
+ */
5
+ 'use server';
6
+ import { NextResponse } from 'next/server';
7
+ import { GET_SETTINGS, POST_SETTINGS } from './handler';
8
+ /**
9
+ * Handle website API requests
10
+ */
11
+ export async function handleWebsiteApi(req, path, config) {
12
+ const method = req.method;
13
+ const route = path[0] || '';
14
+ try {
15
+ // Route: /api/plugin-website/settings
16
+ if (route === 'settings') {
17
+ if (method === 'GET') {
18
+ return await GET_SETTINGS(req, config);
19
+ }
20
+ if (method === 'POST' || method === 'PUT') {
21
+ return await POST_SETTINGS(req, config);
22
+ }
23
+ }
24
+ // Method not allowed
25
+ return NextResponse.json({ error: `Method ${method} not allowed for route: ${route || '/'}` }, { status: 405 });
26
+ }
27
+ catch (error) {
28
+ console.error('[WebsiteApiRouter] Error:', error);
29
+ return NextResponse.json({ error: error.message || 'Internal server error' }, { status: 500 });
30
+ }
31
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Plugin Website - Client Entry Point
3
+ * Main settings management interface for website configuration
4
+ */
5
+ export interface PluginProps {
6
+ subPath: string[];
7
+ siteId: string;
8
+ locale: string;
9
+ }
10
+ export default function WebsitePlugin({ subPath, siteId, locale }: PluginProps): import("react/jsx-runtime").JSX.Element;
11
+ export { WebsitePlugin as Index };
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,2CAS7E;AAGD,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Plugin Website - Client Entry Point
3
+ * Main settings management interface for website configuration
4
+ */
5
+ 'use client';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ import { SettingsView } from './views/SettingsView';
8
+ export default function WebsitePlugin({ subPath, siteId, locale }) {
9
+ const route = subPath[0] || 'settings';
10
+ switch (route) {
11
+ case 'settings':
12
+ return _jsx(SettingsView, { siteId: siteId, locale: locale });
13
+ default:
14
+ return _jsx(SettingsView, { siteId: siteId, locale: locale });
15
+ }
16
+ }
17
+ // Export for use as default
18
+ export { WebsitePlugin as Index };
@@ -0,0 +1,10 @@
1
+ import 'server-only';
2
+ /**
3
+ * Plugin Website - Server-Only Entry Point
4
+ * This file exports only server-side API handlers
5
+ * Used by the dynamic plugin router via @jhits/plugin-website/server
6
+ */
7
+ export { handleWebsiteApi as handleApi } from './api/router';
8
+ export { handleWebsiteApi } from './api/router';
9
+ export type { WebsiteApiConfig } from './types/settings';
10
+ //# sourceMappingURL=index.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.server.d.ts","sourceRoot":"","sources":["../src/index.server.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import 'server-only';
2
+ /**
3
+ * Plugin Website - Server-Only Entry Point
4
+ * This file exports only server-side API handlers
5
+ * Used by the dynamic plugin router via @jhits/plugin-website/server
6
+ */
7
+ export { handleWebsiteApi as handleApi } from './api/router';
8
+ export { handleWebsiteApi } from './api/router'; // Keep original export for backward compatibility
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Website Settings Types
3
+ */
4
+ export interface WebsiteSettings {
5
+ identifier: string;
6
+ siteName?: string;
7
+ siteTagline?: string;
8
+ siteDescription?: string;
9
+ keywords?: string;
10
+ contactEmail?: string;
11
+ phoneNumber?: string;
12
+ physicalAddress?: string;
13
+ smtpUser?: string;
14
+ maintenanceMode?: boolean;
15
+ launch_date?: string;
16
+ socials?: SocialLink[];
17
+ updatedAt?: Date;
18
+ createdAt?: Date;
19
+ }
20
+ export interface SocialLink {
21
+ id: number;
22
+ platform: string;
23
+ url: string;
24
+ }
25
+ export interface WebsiteApiConfig {
26
+ getDb: () => Promise<{
27
+ db: () => any;
28
+ }>;
29
+ getUserId?: (req: any) => Promise<string | null>;
30
+ mongoClient?: Promise<any>;
31
+ authOptions?: any;
32
+ jwtSecret?: string;
33
+ [key: string]: any;
34
+ }
35
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/types/settings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,GAAG,CAAA;KAAE,CAAC,CAAC;IACxC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Website Settings Types
3
+ */
4
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Website Settings View
3
+ * Main interface for managing website settings
4
+ */
5
+ export interface SettingsViewProps {
6
+ siteId: string;
7
+ locale: string;
8
+ }
9
+ export declare function SettingsView({ siteId, locale }: SettingsViewProps): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=SettingsView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsView.d.ts","sourceRoot":"","sources":["../../src/views/SettingsView.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAmBH,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA2ejE"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Website Settings View
3
+ * Main interface for managing website settings
4
+ */
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, Mail, Search, Settings2, Calendar } from 'lucide-react';
9
+ import { FaFacebook, FaInstagram, FaLinkedin, FaPinterest, FaTiktok } from 'react-icons/fa';
10
+ import { FaXTwitter } from 'react-icons/fa6';
11
+ const AVAILABLE_PLATFORMS = [
12
+ { name: 'Instagram', icon: _jsx(FaInstagram, { size: 18 }) },
13
+ { name: 'Facebook', icon: _jsx(FaFacebook, { size: 18 }) },
14
+ { name: 'Twitter', icon: _jsx(FaXTwitter, { size: 18 }) },
15
+ { name: 'Pinterest', icon: _jsx(FaPinterest, { size: 18 }) },
16
+ { name: 'LinkedIn', icon: _jsx(FaLinkedin, { size: 18 }) },
17
+ { name: 'TikTok', icon: _jsx(FaTiktok, { size: 18 }) },
18
+ ];
19
+ export function SettingsView({ siteId, locale }) {
20
+ const [isLoading, setIsLoading] = useState(true);
21
+ const [isSaving, setIsSaving] = useState(false);
22
+ const [showSuccess, setShowSuccess] = useState(false);
23
+ const [settings, setSettings] = useState({
24
+ identifier: 'site_config',
25
+ siteName: '',
26
+ siteTagline: '',
27
+ siteDescription: '',
28
+ keywords: '',
29
+ contactEmail: '',
30
+ phoneNumber: '',
31
+ physicalAddress: '',
32
+ smtpUser: '',
33
+ maintenanceMode: false,
34
+ launch_date: '',
35
+ socials: [],
36
+ });
37
+ // Fetch settings on load
38
+ useEffect(() => {
39
+ const fetchSettings = async () => {
40
+ try {
41
+ setIsLoading(true);
42
+ const response = await fetch('/api/plugin-website/settings', {
43
+ credentials: 'include',
44
+ });
45
+ if (!response.ok) {
46
+ throw new Error('Failed to fetch settings');
47
+ }
48
+ const data = await response.json();
49
+ if (data.siteName !== undefined) {
50
+ // Convert launch_date to datetime-local format if it exists
51
+ let launchDate = '';
52
+ if (data.launch_date) {
53
+ // If it's a Date object or ISO string, convert to datetime-local format
54
+ const date = new Date(data.launch_date);
55
+ if (!isNaN(date.getTime())) {
56
+ // Format as YYYY-MM-DDTHH:mm for datetime-local input (24-hour format)
57
+ const year = date.getFullYear();
58
+ const month = String(date.getMonth() + 1).padStart(2, '0');
59
+ const day = String(date.getDate()).padStart(2, '0');
60
+ const hours = String(date.getHours()).padStart(2, '0');
61
+ const minutes = String(date.getMinutes()).padStart(2, '0');
62
+ launchDate = `${year}-${month}-${day}T${hours}:${minutes}`;
63
+ }
64
+ else {
65
+ // If it's already a string in the correct format, use it
66
+ // Ensure it has time component, default to 00:00 if missing
67
+ if (data.launch_date.includes('T') && data.launch_date.split('T')[1]) {
68
+ launchDate = data.launch_date;
69
+ }
70
+ else if (data.launch_date.includes('T')) {
71
+ launchDate = `${data.launch_date.split('T')[0]}T00:00`;
72
+ }
73
+ else {
74
+ launchDate = `${data.launch_date}T00:00`;
75
+ }
76
+ }
77
+ }
78
+ setSettings({
79
+ identifier: 'site_config',
80
+ siteName: data.siteName || '',
81
+ siteTagline: data.siteTagline || '',
82
+ siteDescription: data.siteDescription || '',
83
+ keywords: data.keywords || '',
84
+ contactEmail: data.contactEmail || '',
85
+ phoneNumber: data.phoneNumber || '',
86
+ physicalAddress: data.physicalAddress || '',
87
+ smtpUser: data.smtpUser || '',
88
+ maintenanceMode: data.maintenanceMode ?? false,
89
+ launch_date: launchDate,
90
+ socials: data.socials || [],
91
+ });
92
+ }
93
+ }
94
+ catch (error) {
95
+ console.error('Failed to load settings:', error);
96
+ }
97
+ finally {
98
+ setIsLoading(false);
99
+ }
100
+ };
101
+ fetchSettings();
102
+ }, []);
103
+ // Use ref to always get latest state
104
+ const settingsRef = useRef(settings);
105
+ useEffect(() => {
106
+ settingsRef.current = settings;
107
+ }, [settings]);
108
+ // Save settings
109
+ const handleSave = async () => {
110
+ try {
111
+ setIsSaving(true);
112
+ // Get the latest state from ref
113
+ const currentSettings = settingsRef.current;
114
+ // Prepare settings for API - ensure launch_date is properly formatted
115
+ const settingsToSave = {
116
+ ...currentSettings,
117
+ };
118
+ // Handle launch_date - keep it if it has a value, otherwise don't include it
119
+ if (currentSettings.launch_date && currentSettings.launch_date.trim() !== '') {
120
+ settingsToSave.launch_date = currentSettings.launch_date;
121
+ }
122
+ else {
123
+ // Don't include launch_date if it's empty
124
+ delete settingsToSave.launch_date;
125
+ }
126
+ console.log('[SettingsView] Saving settings:', {
127
+ launch_date: settingsToSave.launch_date,
128
+ launch_date_type: typeof settingsToSave.launch_date,
129
+ has_launch_date: 'launch_date' in settingsToSave,
130
+ currentState_launch_date: currentSettings.launch_date,
131
+ all_keys: Object.keys(settingsToSave),
132
+ });
133
+ const response = await fetch('/api/plugin-website/settings', {
134
+ method: 'POST',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ credentials: 'include',
137
+ body: JSON.stringify(settingsToSave),
138
+ });
139
+ if (!response.ok) {
140
+ const error = await response.json();
141
+ throw new Error(error.error || 'Failed to save settings');
142
+ }
143
+ setShowSuccess(true);
144
+ setTimeout(() => setShowSuccess(false), 3000);
145
+ }
146
+ catch (error) {
147
+ console.error('Failed to save settings:', error);
148
+ alert(error.message || 'Failed to save settings');
149
+ }
150
+ finally {
151
+ setIsSaving(false);
152
+ }
153
+ };
154
+ // Add social link
155
+ const handleAddSocial = () => {
156
+ const newId = settings.socials?.length ? Math.max(...settings.socials.map(s => s.id)) + 1 : 1;
157
+ setSettings({
158
+ ...settings,
159
+ socials: [...(settings.socials || []), { id: newId, platform: '', url: '' }],
160
+ });
161
+ };
162
+ // Update social link
163
+ const handleUpdateSocial = (id, field, value) => {
164
+ setSettings({
165
+ ...settings,
166
+ socials: settings.socials?.map(social => social.id === id ? { ...social, [field]: value } : social) || [],
167
+ });
168
+ };
169
+ // Remove social link
170
+ const handleRemoveSocial = (id) => {
171
+ setSettings({
172
+ ...settings,
173
+ socials: settings.socials?.filter(social => social.id !== id) || [],
174
+ });
175
+ };
176
+ if (isLoading) {
177
+ 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..." })] }) }));
178
+ }
179
+ return (_jsx("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
180
+ ? 'bg-neutral-400 text-white cursor-not-allowed'
181
+ : showSuccess
182
+ ? 'bg-green-600 text-white'
183
+ : 'bg-primary text-white hover:bg-primary/90'}`, children: isSaving ? (_jsxs(_Fragment, { children: [_jsx(RefreshCw, { className: "w-4 h-4 animate-spin" }), "Saving..."] })) : showSuccess ? (_jsxs(_Fragment, { children: [_jsx(Settings2, { className: "w-4 h-4" }), "Saved!"] })) : (_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"] }), _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" }), _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: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Phone Number" }), _jsx("input", { type: "tel", value: settings.phoneNumber || '', onChange: (e) => setSettings({ ...settings, 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: [_jsx("label", { className: "text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest", children: "Physical Address" }), _jsx("textarea", { value: settings.physicalAddress || '', onChange: (e) => setSettings({ ...settings, 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"] }), _jsx("button", { onClick: handleAddSocial, 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: "space-y-4", children: settings.socials && settings.socials.length > 0 ? (settings.socials.map((social) => {
184
+ const platform = AVAILABLE_PLATFORMS.find(p => p.name === social.platform);
185
+ 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) => handleUpdateSocial(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) => handleUpdateSocial(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: () => handleRemoveSocial(social.id), className: "px-4 py-2 text-xs font-bold text-red-500 hover:text-red-700 dark:hover:text-red-400 transition-colors", children: "Remove" })] }, social.id));
186
+ })) : (_jsx("p", { className: "text-sm text-dashboard-text-secondary text-center py-8", children: "No social links added yet. Click \"Add Social\" to get started." })) })] })] }), _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) => {
187
+ const dateValue = e.target.value;
188
+ const currentTime = settings.launch_date?.includes('T')
189
+ ? settings.launch_date.split('T')[1]
190
+ : '00:00';
191
+ const newLaunchDate = dateValue ? `${dateValue}T${currentTime}` : '';
192
+ console.log('[SettingsView] Date changed:', {
193
+ dateValue,
194
+ currentTime,
195
+ newLaunchDate,
196
+ });
197
+ setSettings(prev => ({
198
+ ...prev,
199
+ launch_date: newLaunchDate,
200
+ }));
201
+ }, 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')
202
+ ? settings.launch_date.split('T')[1]
203
+ : '00:00', onChange: (e) => {
204
+ const timeValue = e.target.value;
205
+ const currentDate = settings.launch_date?.includes('T')
206
+ ? settings.launch_date.split('T')[0]
207
+ : new Date().toISOString().split('T')[0];
208
+ const newLaunchDate = timeValue ? `${currentDate}T${timeValue}` : '';
209
+ console.log('[SettingsView] Time changed:', {
210
+ timeValue,
211
+ currentDate,
212
+ newLaunchDate,
213
+ });
214
+ setSettings(prev => ({
215
+ ...prev,
216
+ launch_date: newLaunchDate,
217
+ }));
218
+ }, 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. Time defaults to 00:00 if not specified." })] })] })] })] })] }) }));
219
+ }
package/package.json CHANGED
@@ -1,26 +1,29 @@
1
1
  {
2
2
  "name": "@jhits/plugin-website",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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.tsx",
9
- "types": "./src/index.tsx",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./src/index.tsx",
13
- "default": "./src/index.tsx"
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
14
  },
15
15
  "./server": {
16
- "types": "./src/index.server.ts",
17
- "default": "./src/index.server.ts"
16
+ "types": "./dist/index.server.d.ts",
17
+ "default": "./dist/index.server.js"
18
18
  }
19
19
  },
20
+ "scripts": {
21
+ "build": "tsc"
22
+ },
20
23
  "dependencies": {
21
- "@jhits/plugin-core": "^0.0.1",
22
- "lucide-react": "^0.562.0",
23
- "mongodb": "^7.0.0",
24
+ "@jhits/plugin-core": "^0.0.2",
25
+ "lucide-react": "^0.564.0",
26
+ "mongodb": "^7.1.0",
24
27
  "next-auth": "^4.24.13",
25
28
  "react-icons": "^5.5.0"
26
29
  },
@@ -30,18 +33,17 @@
30
33
  "react-dom": ">=18.0.0"
31
34
  },
32
35
  "devDependencies": {
33
- "@types/node": "^20.19.27",
34
- "@types/react": "^19",
35
- "@types/react-dom": "^19",
36
- "next": "16.1.1",
37
- "react": "19.2.3",
38
- "react-dom": "19.2.3",
39
- "typescript": "^5"
36
+ "@types/node": "^25.2.3",
37
+ "@types/react": "^19.2.14",
38
+ "@types/react-dom": "^19.2.3",
39
+ "next": "16.1.6",
40
+ "react": "19.2.4",
41
+ "react-dom": "19.2.4",
42
+ "typescript": "^5.9.3"
40
43
  },
41
44
  "files": [
42
- "src/**/*.{ts,tsx,json}",
43
- "!src/**/*.md",
44
- "!src/**/README.md",
45
+ "dist",
46
+ "src",
45
47
  "package.json"
46
48
  ]
47
49
  }
@@ -196,33 +196,33 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
196
196
  <div className="h-full w-full bg-dashboard-card text-dashboard-text flex items-center justify-center">
197
197
  <div className="text-center">
198
198
  <RefreshCw className="w-8 h-8 animate-spin text-primary mx-auto mb-4" />
199
- <p className="text-sm text-neutral-500 dark:text-neutral-400">Loading settings...</p>
199
+ <p className="text-sm text-dashboard-text-secondary">Loading settings...</p>
200
200
  </div>
201
201
  </div>
202
202
  );
203
203
  }
204
204
 
205
205
  return (
206
- <div className="h-full rounded-[2.5rem] w-full bg-dashboard-card text-dashboard-text overflow-y-auto">
207
- <div className="max-w-6xl mx-auto p-8">
206
+ <div className="h-full w-full rounded-[2.5rem] bg-dashboard-card p-8 overflow-y-auto">
207
+ <div className="max-w-6xl mx-auto">
208
208
  {/* Header */}
209
- <div className="flex items-center justify-between mb-8">
209
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
210
210
  <div>
211
- <h1 className="text-3xl font-black text-neutral-950 dark:text-white uppercase tracking-tighter mb-2">
211
+ <h1 className="text-3xl font-black text-dashboard-text uppercase tracking-tighter mb-2">
212
212
  Website Settings
213
213
  </h1>
214
- <p className="text-sm text-neutral-500 dark:text-neutral-400">
214
+ <p className="text-sm text-dashboard-text-secondary">
215
215
  Manage your website identity, contact information, and social links
216
216
  </p>
217
217
  </div>
218
218
  <button
219
219
  onClick={handleSave}
220
220
  disabled={isSaving}
221
- className={`inline-flex items-center gap-2 px-6 py-3 rounded-full text-[10px] font-black uppercase tracking-widest transition-all shadow-lg ${isSaving
222
- ? 'bg-neutral-400 text-white cursor-not-allowed'
223
- : showSuccess
224
- ? 'bg-green-600 text-white'
225
- : 'bg-primary text-white hover:bg-primary/90'
221
+ 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
222
+ ? 'bg-neutral-400 text-white cursor-not-allowed'
223
+ : showSuccess
224
+ ? 'bg-green-600 text-white'
225
+ : 'bg-primary text-white hover:bg-primary/90'
226
226
  }`}
227
227
  >
228
228
  {isSaving ? (
@@ -248,14 +248,14 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
248
248
  {/* Main Content */}
249
249
  <div className="lg:col-span-2 space-y-8">
250
250
  {/* SEO & Website Identity */}
251
- <section className="bg-dashboard-sidebar p-8 rounded-3xl border border-dashboard-border">
252
- <div className="flex items-center gap-2 font-bold text-neutral-950 dark:text-white border-b border-dashboard-border pb-4 mb-6">
251
+ <section className="bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border">
252
+ <div className="flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6">
253
253
  <Search size={20} className="text-primary" />
254
254
  Website Identity & SEO
255
255
  </div>
256
256
  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
257
257
  <div className="space-y-2">
258
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
258
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
259
259
  Website Name
260
260
  </label>
261
261
  <input
@@ -267,7 +267,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
267
267
  />
268
268
  </div>
269
269
  <div className="space-y-2">
270
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
270
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
271
271
  Website Tagline
272
272
  </label>
273
273
  <input
@@ -280,7 +280,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
280
280
  </div>
281
281
  </div>
282
282
  <div className="mt-6 space-y-2">
283
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
283
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
284
284
  Website Description
285
285
  </label>
286
286
  <textarea
@@ -292,7 +292,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
292
292
  />
293
293
  </div>
294
294
  <div className="mt-6 space-y-2">
295
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
295
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
296
296
  Keywords (comma-separated)
297
297
  </label>
298
298
  <input
@@ -306,14 +306,14 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
306
306
  </section>
307
307
 
308
308
  {/* Contact Information */}
309
- <section className="bg-dashboard-sidebar p-8 rounded-3xl border border-dashboard-border">
310
- <div className="flex items-center gap-2 font-bold text-neutral-950 dark:text-white border-b border-dashboard-border pb-4 mb-6">
309
+ <section className="bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border">
310
+ <div className="flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6">
311
311
  <Mail size={20} className="text-primary" />
312
312
  Contact Information
313
313
  </div>
314
314
  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
315
315
  <div className="space-y-2">
316
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
316
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
317
317
  Contact Email
318
318
  </label>
319
319
  <input
@@ -325,7 +325,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
325
325
  />
326
326
  </div>
327
327
  <div className="space-y-2">
328
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
328
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
329
329
  Phone Number
330
330
  </label>
331
331
  <input
@@ -338,7 +338,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
338
338
  </div>
339
339
  </div>
340
340
  <div className="mt-6 space-y-2">
341
- <label className="text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-widest">
341
+ <label className="text-xs font-bold text-dashboard-text-secondary uppercase tracking-widest">
342
342
  Physical Address
343
343
  </label>
344
344
  <textarea
@@ -352,9 +352,9 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
352
352
  </section>
353
353
 
354
354
  {/* Social Links */}
355
- <section className="bg-dashboard-sidebar p-8 rounded-3xl border border-dashboard-border">
355
+ <section className="bg-dashboard-bg p-8 rounded-3xl border border-dashboard-border">
356
356
  <div className="flex items-center justify-between border-b border-dashboard-border pb-4 mb-6">
357
- <div className="flex items-center gap-2 font-bold text-neutral-950 dark:text-white">
357
+ <div className="flex items-center gap-2 font-bold text-dashboard-text">
358
358
  <Globe size={20} className="text-primary" />
359
359
  Social Links
360
360
  </div>
@@ -371,13 +371,13 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
371
371
  const platform = AVAILABLE_PLATFORMS.find(p => p.name === social.platform);
372
372
  return (
373
373
  <div key={social.id} className="flex items-center gap-4 p-4 bg-dashboard-card rounded-2xl border border-dashboard-border">
374
- <div className="flex-shrink-0 text-neutral-500 dark:text-neutral-400">
374
+ <div className="flex-shrink-0 text-dashboard-text-secondary">
375
375
  {platform?.icon || <Globe size={18} />}
376
376
  </div>
377
377
  <select
378
378
  value={social.platform}
379
379
  onChange={(e) => handleUpdateSocial(social.id, 'platform', e.target.value)}
380
- className="flex-1 px-4 py-2 bg-dashboard-sidebar border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text"
380
+ 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"
381
381
  >
382
382
  <option value="">Select Platform</option>
383
383
  {AVAILABLE_PLATFORMS.map(p => (
@@ -389,7 +389,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
389
389
  value={social.url}
390
390
  onChange={(e) => handleUpdateSocial(social.id, 'url', e.target.value)}
391
391
  placeholder="https://..."
392
- className="flex-1 px-4 py-2 bg-dashboard-sidebar border border-dashboard-border rounded-xl outline-none focus:ring-2 focus:ring-primary text-dashboard-text"
392
+ 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"
393
393
  />
394
394
  <button
395
395
  onClick={() => handleRemoveSocial(social.id)}
@@ -401,7 +401,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
401
401
  );
402
402
  })
403
403
  ) : (
404
- <p className="text-sm text-neutral-500 dark:text-neutral-400 text-center py-8">
404
+ <p className="text-sm text-dashboard-text-secondary text-center py-8">
405
405
  No social links added yet. Click "Add Social" to get started.
406
406
  </p>
407
407
  )}
@@ -412,40 +412,41 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
412
412
  {/* Sidebar */}
413
413
  <div className="space-y-8">
414
414
  {/* Maintenance Mode */}
415
- <section className="bg-dashboard-sidebar p-6 rounded-3xl border border-dashboard-border">
416
- <div className="flex items-center gap-2 font-bold text-neutral-950 dark:text-white border-b border-dashboard-border pb-4 mb-6">
415
+ <section className="bg-dashboard-bg p-6 rounded-3xl border border-dashboard-border">
416
+ <div className="flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6">
417
417
  <Settings2 size={18} className="text-primary" />
418
418
  Maintenance
419
419
  </div>
420
420
  <div className="flex items-center justify-between">
421
421
  <div>
422
- <label className="text-xs font-bold text-neutral-700 dark:text-neutral-300 block mb-1">
422
+ <label className="text-xs font-bold text-dashboard-text block mb-1">
423
423
  Maintenance Mode
424
424
  </label>
425
- <p className="text-[10px] text-neutral-500 dark:text-neutral-400">
425
+ <p className="text-[10px] text-dashboard-text-secondary">
426
426
  Show maintenance page to visitors
427
427
  </p>
428
428
  </div>
429
429
  <button
430
430
  onClick={() => setSettings({ ...settings, maintenanceMode: !settings.maintenanceMode })}
431
- className={`relative w-12 h-6 rounded-full transition-colors ${settings.maintenanceMode ? 'bg-primary' : 'bg-neutral-300 dark:bg-neutral-700'
431
+ className={`relative w-12 h-6 rounded-full transition-colors ${settings.maintenanceMode ? 'bg-primary' : 'bg-neutral-200 dark:bg-neutral-700'
432
432
  }`}
433
433
  >
434
- <div className={`absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform ${settings.maintenanceMode ? 'translate-x-6' : 'translate-x-0'
435
- }`} />
434
+ <div
435
+ 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'}`}
436
+ />
436
437
  </button>
437
438
  </div>
438
439
  </section>
439
440
 
440
441
  {/* Launch Date */}
441
- <section className="bg-dashboard-sidebar p-6 rounded-3xl border border-dashboard-border">
442
- <div className="flex items-center gap-2 font-bold text-neutral-950 dark:text-white border-b border-dashboard-border pb-4 mb-6">
442
+ <section className="bg-dashboard-bg p-6 rounded-3xl border border-dashboard-border">
443
+ <div className="flex items-center gap-2 font-bold text-dashboard-text border-b border-dashboard-border pb-4 mb-6">
443
444
  <Calendar size={18} className="text-primary" />
444
445
  Launch Date
445
446
  </div>
446
447
  <div className="space-y-4">
447
448
  <div className="space-y-2">
448
- <label className="text-xs font-bold text-neutral-700 dark:text-neutral-300 block">
449
+ <label className="text-xs font-bold text-dashboard-text block">
449
450
  Date
450
451
  </label>
451
452
  <input
@@ -474,7 +475,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
474
475
  />
475
476
  </div>
476
477
  <div className="space-y-2">
477
- <label className="text-xs font-bold text-neutral-700 dark:text-neutral-300 block">
478
+ <label className="text-xs font-bold text-dashboard-text block">
478
479
  Time (24-hour format)
479
480
  </label>
480
481
  <input
@@ -505,7 +506,7 @@ export function SettingsView({ siteId, locale }: SettingsViewProps) {
505
506
  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"
506
507
  />
507
508
  </div>
508
- <p className="text-[10px] text-neutral-500 dark:text-neutral-400">
509
+ <p className="text-[10px] text-dashboard-text-secondary">
509
510
  Used for countdown timers. Time defaults to 00:00 if not specified.
510
511
  </p>
511
512
  </div>