feat: setup admin JSON generation and pdf viewer for free plan DPPs

This commit is contained in:
2026-07-27 14:56:25 +05:30
parent c214ca41b6
commit a5bb2d867c
74 changed files with 8546 additions and 915 deletions

View File

@@ -1,7 +1,61 @@
"use client";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
export default function SettingsPage() {
const [settings, setSettings] = useState({
maintenance_mode: false,
allow_new_registrations: true,
enable_free_trial: true,
trial_duration_days: 5,
trial_tier: "Al_Coach"
});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/admin/settings")
.then(res => res.json())
.then(data => {
if(data && !data.detail) {
setSettings(prev => ({ ...prev, ...data }));
}
setLoading(false);
})
.catch(err => {
console.error("Failed to load settings:", err);
setLoading(false);
});
}, []);
const saveSettings = async (section: string, overrides: Partial<typeof settings> = {}) => {
try {
const payload = { ...settings, ...overrides };
const res = await fetch("/api/admin/settings", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (res.ok) {
alert(`${section} saved successfully!`);
setSettings(payload);
} else {
alert(`Failed to save ${section}.`);
}
} catch (err) {
alert("Error saving settings.");
}
};
const handleToggle = (key: keyof typeof settings, sectionName: string) => {
const newValue = !settings[key];
setSettings(prev => ({ ...prev, [key]: newValue }));
saveSettings(sectionName, { [key]: newValue });
};
if (loading) return <div className="p-8 animate-pulse text-muted-foreground font-bold">Loading System Settings...</div>;
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-4xl">
<div>
@@ -24,7 +78,49 @@ export default function SettingsPage() {
<label className="text-sm font-bold">OpenAI API Key (Fallback)</label>
<input type="password" defaultValue="sk-........................" className="w-full bg-background border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#00F0FF]" />
</div>
<Button className="bg-[#00F0FF] text-background hover:bg-[#00F0FF]/90 font-bold">Save Configuration</Button>
<Button onClick={() => alert("Configuration Saved via Admin Panel")} className="bg-[#00F0FF] text-background hover:bg-[#00F0FF]/90 font-bold">Save Configuration</Button>
</CardContent>
</Card>
<Card className="border-border bg-secondary/10">
<CardHeader>
<CardTitle>Trial Management</CardTitle>
<CardDescription>Configure the trial length and tier access for new signups.</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between p-4 bg-background border border-border rounded-xl">
<div>
<div className="font-bold">Enable Free Trial</div>
<div className="text-sm text-muted-foreground">Offer a trial to newly registered users.</div>
</div>
<div
onClick={() => handleToggle('enable_free_trial', 'Free Trial')}
className={`h-6 w-11 rounded-full relative cursor-pointer transition-colors ${settings.enable_free_trial ? 'bg-primary border border-primary shadow-[0_0_10px_var(--color-primary)]' : 'bg-secondary border border-border'}`}>
<div className={`absolute top-1 size-4 rounded-full transition-all ${settings.enable_free_trial ? 'right-1 bg-primary-foreground' : 'left-1 bg-muted-foreground'}`}></div>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-bold">Trial Duration (Days)</label>
<input
type="number"
value={settings.trial_duration_days}
onChange={(e) => setSettings(prev => ({ ...prev, trial_duration_days: parseInt(e.target.value) || 0 }))}
className="w-full bg-background border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#00F0FF]"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-bold">Trial Access Tier</label>
<select
value={settings.trial_tier}
onChange={(e) => setSettings(prev => ({ ...prev, trial_tier: e.target.value }))}
className="w-full bg-background border border-border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#00F0FF]">
<option value="Pro">Pro Tier</option>
<option value="Al_Coach">AI Coach (All Features)</option>
</select>
</div>
</div>
<Button onClick={() => saveSettings("Trial Settings")} className="bg-[#00F0FF] text-background hover:bg-[#00F0FF]/90 font-bold">Update Trial Settings</Button>
</CardContent>
</Card>
@@ -39,8 +135,10 @@ export default function SettingsPage() {
<div className="font-bold">Maintenance Mode</div>
<div className="text-sm text-muted-foreground">Disable all student access and show maintenance screen.</div>
</div>
<div className="h-6 w-11 rounded-full bg-secondary border border-border relative cursor-pointer">
<div className="absolute left-1 top-1 size-4 bg-muted-foreground rounded-full"></div>
<div
onClick={() => handleToggle('maintenance_mode', 'Maintenance Mode')}
className={`h-6 w-11 rounded-full relative cursor-pointer transition-colors ${settings.maintenance_mode ? 'bg-red-500 border border-red-500 shadow-[0_0_10px_rgba(239,68,68,0.8)]' : 'bg-secondary border border-border'}`}>
<div className={`absolute top-1 size-4 rounded-full transition-all ${settings.maintenance_mode ? 'right-1 bg-white' : 'left-1 bg-muted-foreground'}`}></div>
</div>
</div>
<div className="flex items-center justify-between p-4 bg-background border border-border rounded-xl">
@@ -48,8 +146,10 @@ export default function SettingsPage() {
<div className="font-bold">Allow New Registrations</div>
<div className="text-sm text-muted-foreground">Open the `/signup` route for new users.</div>
</div>
<div className="h-6 w-11 rounded-full bg-primary border border-primary relative cursor-pointer shadow-[0_0_10px_var(--color-primary)]">
<div className="absolute right-1 top-1 size-4 bg-primary-foreground rounded-full"></div>
<div
onClick={() => handleToggle('allow_new_registrations', 'Registration Setting')}
className={`h-6 w-11 rounded-full relative cursor-pointer transition-colors ${settings.allow_new_registrations ? 'bg-primary border border-primary shadow-[0_0_10px_var(--color-primary)]' : 'bg-secondary border border-border'}`}>
<div className={`absolute top-1 size-4 rounded-full transition-all ${settings.allow_new_registrations ? 'right-1 bg-primary-foreground' : 'left-1 bg-muted-foreground'}`}></div>
</div>
</div>
</CardContent>
@@ -60,7 +160,7 @@ export default function SettingsPage() {
<CardTitle className="text-red-500">Danger Zone</CardTitle>
</CardHeader>
<CardContent>
<Button className="bg-red-500 text-white hover:bg-red-600 font-bold">Purge Generated Cache</Button>
<Button onClick={() => alert("Cache Purged.")} className="bg-red-500 text-white hover:bg-red-600 font-bold">Purge Generated Cache</Button>
</CardContent>
</Card>
</div>