Initial commit
This commit is contained in:
936
app/admin/setup/page.tsx
Normal file
936
app/admin/setup/page.tsx
Normal file
@@ -0,0 +1,936 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface SocialMedia {
|
||||
url: string;
|
||||
display: boolean;
|
||||
}
|
||||
|
||||
interface Socials {
|
||||
facebook?: SocialMedia;
|
||||
instagram?: SocialMedia;
|
||||
twitter?: SocialMedia;
|
||||
linkedin?: SocialMedia;
|
||||
youtube?: SocialMedia;
|
||||
}
|
||||
|
||||
interface OAuthProvider {
|
||||
enabled: boolean;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
}
|
||||
|
||||
interface OAuthConfig {
|
||||
google?: OAuthProvider;
|
||||
github?: OAuthProvider;
|
||||
facebook?: OAuthProvider;
|
||||
discord?: OAuthProvider;
|
||||
}
|
||||
|
||||
export default function AdminSetupPage() {
|
||||
const [config, setConfig] = useState({
|
||||
googleAuth: {
|
||||
enabled: false,
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
},
|
||||
oauth: {
|
||||
google: { enabled: false, clientId: "", clientSecret: "" },
|
||||
github: { enabled: false, clientId: "", clientSecret: "" },
|
||||
facebook: { enabled: false, clientId: "", clientSecret: "" },
|
||||
discord: { enabled: false, clientId: "", clientSecret: "" },
|
||||
} as OAuthConfig,
|
||||
googleCalendar: {
|
||||
enabled: false,
|
||||
serviceAccountEmail: "",
|
||||
serviceAccountKey: "",
|
||||
calendarId: "",
|
||||
},
|
||||
socials: {} as Socials,
|
||||
email: {
|
||||
smtp: {
|
||||
enabled: false,
|
||||
host: "",
|
||||
port: 587,
|
||||
username: "",
|
||||
password: "",
|
||||
from: "",
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
itemsPerPage: 10,
|
||||
},
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetchConfig();
|
||||
}, []);
|
||||
|
||||
const fetchConfig = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/admin/setup");
|
||||
const data = await response.json();
|
||||
|
||||
if (data.ok) {
|
||||
// Ensure oauth object exists with all providers
|
||||
const fetchedConfig = data.data;
|
||||
if (!fetchedConfig.oauth) {
|
||||
fetchedConfig.oauth = {
|
||||
google: { enabled: false, clientId: "", clientSecret: "" },
|
||||
github: { enabled: false, clientId: "", clientSecret: "" },
|
||||
facebook: { enabled: false, clientId: "", clientSecret: "" },
|
||||
discord: { enabled: false, clientId: "", clientSecret: "" },
|
||||
};
|
||||
}
|
||||
setConfig(fetchedConfig);
|
||||
} else {
|
||||
setMessage(`❌ Failed to load config: ${data.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching config:", error);
|
||||
setMessage(`❌ Error loading configuration: ${error}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true);
|
||||
setMessage("");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/admin/setup", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(config),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.ok) {
|
||||
setMessage("✅ Settings saved successfully!");
|
||||
} else {
|
||||
setMessage(`❌ ${data.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage("❌ Failed to save settings");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
setTimeout(() => setMessage(""), 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const updateSocial = (platform: keyof Socials, field: "url" | "display", value: string | boolean) => {
|
||||
setConfig({
|
||||
...config,
|
||||
socials: {
|
||||
...config.socials,
|
||||
[platform]: {
|
||||
...config.socials[platform],
|
||||
[field]: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<main className="max-w-5xl mx-auto px-6 py-16">
|
||||
<div className="text-center">Loading configuration...</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="max-w-5xl mx-auto px-6 py-16">
|
||||
<div className="mb-8">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-primary/10 text-primary text-xs font-semibold mb-4">
|
||||
🛠️ Admin Settings
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-slate-900 dark:text-white">
|
||||
System Setup
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||
Configure authentication, social media, and email settings
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Google OAuth Configuration */}
|
||||
<div className="card p-6 border border-slate-200/60 dark:border-slate-700/60 shadow-lg">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="text-2xl">🔐</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Google OAuth</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">Enable Google sign-in for users</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="googleEnabled"
|
||||
checked={config.googleAuth.enabled}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleAuth: { ...config.googleAuth, enabled: e.target.checked },
|
||||
})
|
||||
}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="googleEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable Google Sign-In
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Google Client ID
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field"
|
||||
placeholder="Your Google OAuth Client ID"
|
||||
value={config.googleAuth.clientId}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleAuth: { ...config.googleAuth, clientId: e.target.value },
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Google Client Secret
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field"
|
||||
placeholder="Your Google OAuth Client Secret"
|
||||
value={config.googleAuth.clientSecret}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleAuth: { ...config.googleAuth, clientSecret: e.target.value },
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* BetterAuth OAuth Providers */}
|
||||
<div className="border-t border-slate-200 dark:border-slate-700 pt-6 mt-6">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 text-xs font-semibold mb-4">
|
||||
🔑 OAuth Providers (BetterAuth)
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-6">
|
||||
Configure social OAuth providers for user authentication. Get credentials from each provider's developer console.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Google OAuth */}
|
||||
<div className="card p-6 border border-blue-200/40 dark:border-blue-900/30 bg-blue-50/30 dark:bg-blue-950/20">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-2xl">🔍</span>
|
||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">Google</h3>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="googleOAuthEnabled"
|
||||
checked={config.oauth.google?.enabled || false}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
google: { ...(config.oauth.google || {}), enabled: e.target.checked } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="googleOAuthEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable Google OAuth
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client ID"
|
||||
value={config.oauth.google?.clientId || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
google: { ...(config.oauth.google || {}), clientId: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client Secret"
|
||||
value={config.oauth.google?.clientSecret || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
google: { ...(config.oauth.google || {}), clientSecret: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* GitHub OAuth */}
|
||||
<div className="card p-6 border border-gray-300/40 dark:border-gray-700/30 bg-gray-50/30 dark:bg-gray-950/20">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-2xl">🐙</span>
|
||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">GitHub</h3>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="githubOAuthEnabled"
|
||||
checked={config.oauth.github?.enabled || false}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
github: { ...(config.oauth.github || {}), enabled: e.target.checked } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="githubOAuthEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable GitHub OAuth
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client ID"
|
||||
value={config.oauth.github?.clientId || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
github: { ...(config.oauth.github || {}), clientId: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client Secret"
|
||||
value={config.oauth.github?.clientSecret || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
github: { ...(config.oauth.github || {}), clientSecret: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Facebook OAuth */}
|
||||
<div className="card p-6 border border-blue-600/40 dark:border-blue-900/30 bg-blue-50/30 dark:bg-blue-950/20">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-2xl">👍</span>
|
||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">Facebook</h3>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="facebookOAuthEnabled"
|
||||
checked={config.oauth.facebook?.enabled || false}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
facebook: { ...(config.oauth.facebook || {}), enabled: e.target.checked } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="facebookOAuthEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable Facebook OAuth
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field text-sm"
|
||||
placeholder="App ID"
|
||||
value={config.oauth.facebook?.clientId || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
facebook: { ...(config.oauth.facebook || {}), clientId: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field text-sm"
|
||||
placeholder="App Secret"
|
||||
value={config.oauth.facebook?.clientSecret || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
facebook: { ...(config.oauth.facebook || {}), clientSecret: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Discord OAuth */}
|
||||
<div className="card p-6 border border-indigo-500/40 dark:border-indigo-900/30 bg-indigo-50/30 dark:bg-indigo-950/20">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="text-2xl">💬</span>
|
||||
<h3 className="text-lg font-bold text-gray-900 dark:text-white">Discord</h3>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="discordOAuthEnabled"
|
||||
checked={config.oauth.discord?.enabled || false}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
discord: { ...(config.oauth.discord || {}), enabled: e.target.checked } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-4 h-4 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="discordOAuthEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable Discord OAuth
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client ID"
|
||||
value={config.oauth.discord?.clientId || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
discord: { ...(config.oauth.discord || {}), clientId: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field text-sm"
|
||||
placeholder="Client Secret"
|
||||
value={config.oauth.discord?.clientSecret || ""}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
oauth: {
|
||||
...config.oauth,
|
||||
discord: { ...(config.oauth.discord || {}), clientSecret: e.target.value } as OAuthProvider,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Google Calendar Configuration */}
|
||||
<div className="card p-6 border border-slate-200/60 dark:border-slate-700/60 shadow-lg">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="text-2xl">📅</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Google Calendar</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">Enable calendar invites for webinar registrations</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="calendarEnabled"
|
||||
checked={config.googleCalendar.enabled}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleCalendar: { ...config.googleCalendar, enabled: e.target.checked },
|
||||
})
|
||||
}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label
|
||||
htmlFor="calendarEnabled"
|
||||
className="text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Enable Calendar Invites
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Service Account Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
className="input-field"
|
||||
placeholder="service-account@project.iam.gserviceaccount.com"
|
||||
value={config.googleCalendar.serviceAccountEmail}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleCalendar: { ...config.googleCalendar, serviceAccountEmail: e.target.value },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Create a service account in Google Cloud Console
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Service Account Private Key (JSON)
|
||||
</label>
|
||||
<textarea
|
||||
className="input-field font-mono text-xs"
|
||||
rows={4}
|
||||
placeholder='{"type": "service_account", "project_id": "...", "private_key": "...", ...}'
|
||||
value={config.googleCalendar.serviceAccountKey}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleCalendar: { ...config.googleCalendar, serviceAccountKey: e.target.value },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Paste the entire JSON key file content
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Calendar ID
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field"
|
||||
placeholder="primary or your-calendar@group.calendar.google.com"
|
||||
value={config.googleCalendar.calendarId}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
googleCalendar: { ...config.googleCalendar, calendarId: e.target.value },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Use "primary" for the service account's calendar or specify a shared calendar ID
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social Media Configuration */}
|
||||
<div className="card p-6 border border-slate-200/60 dark:border-slate-700/60 shadow-lg">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="text-2xl">🌐</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Social Media</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">Configure social media links and visibility</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
{/* Facebook */}
|
||||
<div className="border-b border-slate-200 dark:border-slate-700 pb-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xl">📘</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-white">Facebook</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="url"
|
||||
className="input-field"
|
||||
placeholder="https://facebook.com/yourpage"
|
||||
value={config.socials.facebook?.url || ""}
|
||||
onChange={(e) => updateSocial("facebook", "url", e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="facebookDisplay"
|
||||
checked={config.socials.facebook?.display || false}
|
||||
onChange={(e) => updateSocial("facebook", "display", e.target.checked)}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="facebookDisplay" className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Display on landing page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Instagram */}
|
||||
<div className="border-b border-slate-200 dark:border-slate-700 pb-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xl">📷</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-white">Instagram</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="url"
|
||||
className="input-field"
|
||||
placeholder="https://instagram.com/yourprofile"
|
||||
value={config.socials.instagram?.url || ""}
|
||||
onChange={(e) => updateSocial("instagram", "url", e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="instagramDisplay"
|
||||
checked={config.socials.instagram?.display || false}
|
||||
onChange={(e) => updateSocial("instagram", "display", e.target.checked)}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="instagramDisplay" className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Display on landing page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Twitter */}
|
||||
<div className="border-b border-slate-200 dark:border-slate-700 pb-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xl">🐦</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-white">Twitter</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="url"
|
||||
className="input-field"
|
||||
placeholder="https://twitter.com/yourhandle"
|
||||
value={config.socials.twitter?.url || ""}
|
||||
onChange={(e) => updateSocial("twitter", "url", e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="twitterDisplay"
|
||||
checked={config.socials.twitter?.display || false}
|
||||
onChange={(e) => updateSocial("twitter", "display", e.target.checked)}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="twitterDisplay" className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Display on landing page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* LinkedIn */}
|
||||
<div className="border-b border-slate-200 dark:border-slate-700 pb-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xl">💼</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-white">LinkedIn</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="url"
|
||||
className="input-field"
|
||||
placeholder="https://linkedin.com/company/yourcompany"
|
||||
value={config.socials.linkedin?.url || ""}
|
||||
onChange={(e) => updateSocial("linkedin", "url", e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="linkedinDisplay"
|
||||
checked={config.socials.linkedin?.display || false}
|
||||
onChange={(e) => updateSocial("linkedin", "display", e.target.checked)}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="linkedinDisplay" className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Display on landing page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* YouTube */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xl">📺</span>
|
||||
<span className="font-semibold text-gray-900 dark:text-white">YouTube</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<input
|
||||
type="url"
|
||||
className="input-field"
|
||||
placeholder="https://youtube.com/channel/yourchannel"
|
||||
value={config.socials.youtube?.url || ""}
|
||||
onChange={(e) => updateSocial("youtube", "url", e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="youtubeDisplay"
|
||||
checked={config.socials.youtube?.display || false}
|
||||
onChange={(e) => updateSocial("youtube", "display", e.target.checked)}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="youtubeDisplay" className="text-sm text-gray-700 dark:text-gray-300">
|
||||
Display on landing page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* SMTP Email Configuration */}
|
||||
<div className="card p-6 border border-slate-200/60 dark:border-slate-700/60 shadow-lg">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="text-2xl">📧</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Email (SMTP)</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">Configure email server for notifications and activation</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="smtpEnabled"
|
||||
checked={config.email.smtp.enabled}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, enabled: e.target.checked },
|
||||
},
|
||||
})
|
||||
}
|
||||
className="w-5 h-5 text-primary border-gray-300 rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="smtpEnabled" className="text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Enable SMTP Email
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
SMTP Host
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field"
|
||||
placeholder="smtp.gmail.com"
|
||||
value={config.email.smtp.host}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, host: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Port
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
className="input-field"
|
||||
placeholder="587"
|
||||
value={config.email.smtp.port}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, port: parseInt(e.target.value) || 587 },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Username
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="input-field"
|
||||
placeholder="your-email@gmail.com"
|
||||
value={config.email.smtp.username}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, username: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="input-field"
|
||||
placeholder="Your SMTP password or app password"
|
||||
value={config.email.smtp.password}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, password: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
From Address
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
className="input-field"
|
||||
placeholder="noreply@yourdomain.com"
|
||||
value={config.email.smtp.from}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
email: {
|
||||
...config.email,
|
||||
smtp: { ...config.email.smtp, from: e.target.value },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pagination Configuration */}
|
||||
<div className="card p-6 border border-slate-200/60 dark:border-slate-700/60 shadow-lg">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="text-2xl">📄</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Pagination</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">Configure pagination settings for lists</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
|
||||
Items Per Page
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min="5"
|
||||
max="100"
|
||||
className="input-field"
|
||||
placeholder="10"
|
||||
value={config.pagination.itemsPerPage}
|
||||
onChange={(e) =>
|
||||
setConfig({
|
||||
...config,
|
||||
pagination: {
|
||||
itemsPerPage: Math.max(5, Math.min(100, parseInt(e.target.value) || 10)),
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
Used for users and webinars list (min: 5, max: 100)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button onClick={handleSave} disabled={saving} className="btn-primary">
|
||||
{saving ? "⏳ Saving..." : "💾 Save Settings"}
|
||||
</button>
|
||||
{message && (
|
||||
<span
|
||||
className={`px-4 py-2 rounded-lg text-sm font-semibold ${
|
||||
message.includes("✅")
|
||||
? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
|
||||
: "bg-red-500/10 text-red-600 dark:text-red-400"
|
||||
}`}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user