2025-09-26 19:45:02 +05:30
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
|
import { AuthenticatedLayout } from '../layout/AuthenticatedLayout';
|
|
|
|
|
|
import { Button } from '../ui/button';
|
|
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';
|
|
|
|
|
|
import { Input } from '../ui/input';
|
|
|
|
|
|
import { Label } from '../ui/label';
|
|
|
|
|
|
import { Textarea } from '../ui/textarea';
|
|
|
|
|
|
import { Badge } from '../ui/badge';
|
|
|
|
|
|
import { toast } from "sonner@2.0.3";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Save,
|
|
|
|
|
|
Eye,
|
|
|
|
|
|
Send,
|
|
|
|
|
|
Check,
|
|
|
|
|
|
X,
|
|
|
|
|
|
History,
|
|
|
|
|
|
GripVertical,
|
|
|
|
|
|
Link
|
|
|
|
|
|
} from 'lucide-react';
|
|
|
|
|
|
import { InternalLinkPicker } from '../landing-pages/InternalLinkPicker';
|
|
|
|
|
|
import { MediaPicker } from '../landing-pages/MediaPicker';
|
|
|
|
|
|
import { PreviewModal } from '../landing-pages/PreviewModal';
|
|
|
|
|
|
import { VersionHistory } from '../landing-pages/VersionHistory';
|
|
|
|
|
|
import { AuditDrawer } from '../landing-pages/AuditDrawer';
|
2025-10-29 19:21:35 +05:30
|
|
|
|
import { Route } from '../../types/routes';
|
2025-09-26 19:45:02 +05:30
|
|
|
|
|
|
|
|
|
|
interface AboutUsEditorProps {
|
2025-10-29 19:21:35 +05:30
|
|
|
|
onNavigate: (route: Route) => void;
|
2025-09-26 19:45:02 +05:30
|
|
|
|
onLogout: () => void;
|
|
|
|
|
|
user: any;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface HeroData {
|
|
|
|
|
|
imageUrl?: string;
|
|
|
|
|
|
imageAlt?: string;
|
|
|
|
|
|
headline: string;
|
|
|
|
|
|
subtext: string;
|
|
|
|
|
|
cta: {
|
|
|
|
|
|
label: string;
|
|
|
|
|
|
internalHref: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface StatData {
|
|
|
|
|
|
value: number;
|
|
|
|
|
|
suffix: string;
|
|
|
|
|
|
label: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface TeamMember {
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
imageUrl?: string;
|
|
|
|
|
|
imageAlt?: string;
|
|
|
|
|
|
body: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function AboutUsEditor({ onNavigate, onLogout, user }: AboutUsEditorProps) {
|
|
|
|
|
|
const [status, setStatus] = useState<'draft' | 'in_review' | 'changes_requested' | 'approved' | 'published'>('published');
|
|
|
|
|
|
const [isLinkPickerOpen, setIsLinkPickerOpen] = useState(false);
|
|
|
|
|
|
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
|
|
|
|
|
const [isVersionHistoryOpen, setIsVersionHistoryOpen] = useState(false);
|
|
|
|
|
|
const [isAuditOpen, setIsAuditOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
// Form data
|
|
|
|
|
|
const [hero, setHero] = useState<HeroData>({
|
|
|
|
|
|
headline: '',
|
|
|
|
|
|
subtext: '',
|
|
|
|
|
|
cta: { label: '', internalHref: '' }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const [stats, setStats] = useState<StatData[]>([
|
|
|
|
|
|
{ value: 27187, suffix: '+', label: 'LEADERS DEVELOPED' },
|
|
|
|
|
|
{ value: 15510, suffix: '+', label: 'CORPORATE CLIENTS' },
|
|
|
|
|
|
{ value: 1240, suffix: '+', label: 'COUNTRIES SERVED' }
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
const [teamMembers, setTeamMembers] = useState<TeamMember[]>([
|
|
|
|
|
|
{ title: '', body: '' },
|
|
|
|
|
|
{ title: '', body: '' },
|
|
|
|
|
|
{ title: '', body: '' },
|
|
|
|
|
|
{ title: '', body: '' }
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
const breadcrumbs = [
|
|
|
|
|
|
{ label: "Admin", href: "/dashboard" },
|
|
|
|
|
|
{ label: "Landing Pages", href: "/landing-pages" },
|
|
|
|
|
|
{ label: "About Us" }
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const handleSaveDraft = () => {
|
|
|
|
|
|
toast.success("Saved as draft.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSubmitForApproval = () => {
|
|
|
|
|
|
setStatus('in_review');
|
|
|
|
|
|
toast.success("Submitted for approval.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleApprove = () => {
|
|
|
|
|
|
setStatus('approved');
|
|
|
|
|
|
toast.success("Approved.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleRequestChanges = () => {
|
|
|
|
|
|
setStatus('changes_requested');
|
|
|
|
|
|
toast.success("Changes requested.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handlePublish = () => {
|
|
|
|
|
|
setStatus('published');
|
|
|
|
|
|
toast.success("Published.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleUnpublish = () => {
|
|
|
|
|
|
setStatus('draft');
|
|
|
|
|
|
toast.success("Unpublished.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleLinkSelect = (link: { href: string; title: string }) => {
|
|
|
|
|
|
setHero(prev => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
cta: { ...prev.cta, internalHref: link.href }
|
|
|
|
|
|
}));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const validateCTA = (cta: { label: string; internalHref: string }) => {
|
|
|
|
|
|
if (cta.label && !cta.internalHref) return "CTA requires both text and destination.";
|
|
|
|
|
|
if (!cta.label && cta.internalHref) return "CTA requires both text and destination.";
|
|
|
|
|
|
return null;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getStatusBadgeVariant = () => {
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
case 'published': return 'default';
|
|
|
|
|
|
case 'approved': return 'secondary';
|
|
|
|
|
|
case 'in_review': return 'outline';
|
|
|
|
|
|
case 'changes_requested': return 'destructive';
|
|
|
|
|
|
default: return 'secondary';
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getStatusLabel = () => {
|
|
|
|
|
|
switch (status) {
|
|
|
|
|
|
case 'draft': return 'Draft';
|
|
|
|
|
|
case 'in_review': return 'In Review';
|
|
|
|
|
|
case 'changes_requested': return 'Changes Requested';
|
|
|
|
|
|
case 'approved': return 'Approved';
|
|
|
|
|
|
case 'published': return 'Published';
|
|
|
|
|
|
default: return 'Draft';
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const canEdit = status !== 'in_review';
|
|
|
|
|
|
const canApprove = user.role === 'Super Admin' && status === 'in_review';
|
|
|
|
|
|
const canPublish = user.role === 'Super Admin' && status === 'approved';
|
|
|
|
|
|
|
|
|
|
|
|
const renderHeader = () => (
|
|
|
|
|
|
<div className="sticky top-0 bg-background border-b p-6 z-10">
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
|
<h1>About Us Page</h1>
|
|
|
|
|
|
<Badge variant={getStatusBadgeVariant()}>
|
|
|
|
|
|
{getStatusLabel()}
|
|
|
|
|
|
</Badge>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={handleSaveDraft}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Save className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Save Draft
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={() => setIsPreviewOpen(true)}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Eye className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Preview
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
{status === 'draft' && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleSubmitForApproval}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
style={{ backgroundColor: "var(--color-brand-primary)" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Send className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Submit for Approval
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{canApprove && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleApprove}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
style={{ backgroundColor: "var(--color-brand-primary)" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Check className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Approve
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={handleRequestChanges}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<X className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Request Changes
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{canPublish && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handlePublish}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
style={{ backgroundColor: "var(--color-brand-primary)" }}
|
|
|
|
|
|
>
|
|
|
|
|
|
Publish
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{status === 'published' && (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={handleUnpublish}
|
|
|
|
|
|
className="min-h-[44px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
Unpublish
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="ghost"
|
|
|
|
|
|
onClick={() => setIsAuditOpen(true)}
|
|
|
|
|
|
className="min-h-[44px] w-[44px] p-0"
|
|
|
|
|
|
>
|
|
|
|
|
|
<History className="h-4 w-4" />
|
|
|
|
|
|
<span className="sr-only">Audit</span>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const renderRightRail = () => (
|
|
|
|
|
|
<div className="w-80 border-l bg-muted/25 p-6 space-y-6">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h3 className="font-medium mb-3">Page Information</h3>
|
|
|
|
|
|
<div className="space-y-2 text-sm">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span className="text-muted-foreground">URL:</span>
|
|
|
|
|
|
<span className="ml-2 font-mono">/about-us</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span className="text-muted-foreground">Last published:</span>
|
|
|
|
|
|
<span className="ml-2">2024-01-13 16:45</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span className="text-muted-foreground">Last editor:</span>
|
|
|
|
|
|
<span className="ml-2">Marketing Team</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h3 className="font-medium mb-3">Actions</h3>
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={() => setIsVersionHistoryOpen(true)}
|
|
|
|
|
|
className="w-full justify-start min-h-[44px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<History className="h-4 w-4 mr-2" />
|
|
|
|
|
|
Version History
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const renderHeroSection = () => (
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardTitle>Hero Section</CardTitle>
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
<CardContent className="space-y-6">
|
|
|
|
|
|
{/* Background Image */}
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label>Background Image</Label>
|
|
|
|
|
|
<MediaPicker
|
|
|
|
|
|
type="image"
|
|
|
|
|
|
onChange={() => {}}
|
|
|
|
|
|
recommendedSize="1440×600px"
|
|
|
|
|
|
required
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Headline */}
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor="hero-headline">
|
|
|
|
|
|
Headline <span className="text-destructive">*</span>
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="hero-headline"
|
|
|
|
|
|
value={hero.headline}
|
|
|
|
|
|
onChange={(e) => setHero(prev => ({ ...prev, headline: e.target.value }))}
|
|
|
|
|
|
placeholder="Enter hero headline"
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
required
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Subtext */}
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor="hero-subtext">Subtext</Label>
|
|
|
|
|
|
<Textarea
|
|
|
|
|
|
id="hero-subtext"
|
|
|
|
|
|
value={hero.subtext}
|
|
|
|
|
|
onChange={(e) => setHero(prev => ({ ...prev, subtext: e.target.value }))}
|
|
|
|
|
|
placeholder="Enter hero subtext"
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* CTA */}
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
<Label>Call to Action</Label>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor="hero-cta-text">CTA Text</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="hero-cta-text"
|
|
|
|
|
|
value={hero.cta.label}
|
|
|
|
|
|
onChange={(e) => setHero(prev => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
cta: { ...prev.cta, label: e.target.value }
|
|
|
|
|
|
}))}
|
|
|
|
|
|
placeholder="Enter CTA text"
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label>CTA Destination</Label>
|
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={hero.cta.internalHref}
|
|
|
|
|
|
placeholder="Select internal link"
|
|
|
|
|
|
readOnly
|
|
|
|
|
|
className="flex-1"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
onClick={() => setIsLinkPickerOpen(true)}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
className="flex-shrink-0"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Link className="h-4 w-4" />
|
|
|
|
|
|
<span className="sr-only">Select link</span>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{validateCTA(hero.cta) && (
|
|
|
|
|
|
<p className="text-sm text-destructive">{validateCTA(hero.cta)}</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const renderStatsSection = () => (
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardTitle>Stats Section</CardTitle>
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
<CardContent className="space-y-6">
|
|
|
|
|
|
{stats.map((stat, index) => (
|
|
|
|
|
|
<div key={index} className="grid grid-cols-3 gap-4">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor={`stat-${index}-value`}>Number</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id={`stat-${index}-value`}
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
value={stat.value}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
const newStats = [...stats];
|
|
|
|
|
|
newStats[index].value = parseInt(e.target.value) || 0;
|
|
|
|
|
|
setStats(newStats);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
min="0"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor={`stat-${index}-suffix`}>Suffix</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id={`stat-${index}-suffix`}
|
|
|
|
|
|
value={stat.suffix}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
const newStats = [...stats];
|
|
|
|
|
|
newStats[index].suffix = e.target.value;
|
|
|
|
|
|
setStats(newStats);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
placeholder="+"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor={`stat-${index}-label`}>Label</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id={`stat-${index}-label`}
|
|
|
|
|
|
value={stat.label}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
const newStats = [...stats];
|
|
|
|
|
|
newStats[index].label = e.target.value;
|
|
|
|
|
|
setStats(newStats);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
placeholder="Label"
|
|
|
|
|
|
required
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const renderTeamSection = () => (
|
|
|
|
|
|
<Card>
|
|
|
|
|
|
<CardHeader>
|
|
|
|
|
|
<CardTitle>Our Team</CardTitle>
|
|
|
|
|
|
</CardHeader>
|
|
|
|
|
|
<CardContent className="space-y-6">
|
|
|
|
|
|
{teamMembers.map((member, index) => (
|
|
|
|
|
|
<div key={index} className="space-y-4 p-4 border rounded-lg">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<GripVertical className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
|
<span className="font-medium">Team Member {index + 1}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor={`team-${index}-title`}>Name / Role</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id={`team-${index}-title`}
|
|
|
|
|
|
value={member.title}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
const newMembers = [...teamMembers];
|
|
|
|
|
|
newMembers[index].title = e.target.value;
|
|
|
|
|
|
setTeamMembers(newMembers);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
placeholder="Enter name and role"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label>Photo</Label>
|
|
|
|
|
|
<MediaPicker
|
|
|
|
|
|
type="image"
|
|
|
|
|
|
acceptedTypes={['.jpg', '.jpeg', '.png', '.svg']}
|
|
|
|
|
|
onChange={() => {}}
|
|
|
|
|
|
recommendedSize="400×400px"
|
|
|
|
|
|
required
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Label htmlFor={`team-${index}-body`}>Bio / Description</Label>
|
|
|
|
|
|
<Textarea
|
|
|
|
|
|
id={`team-${index}-body`}
|
|
|
|
|
|
value={member.body}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
const newMembers = [...teamMembers];
|
|
|
|
|
|
newMembers[index].body = e.target.value;
|
|
|
|
|
|
setTeamMembers(newMembers);
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!canEdit}
|
|
|
|
|
|
placeholder="Enter bio or role description"
|
|
|
|
|
|
rows={4}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</CardContent>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<AuthenticatedLayout
|
|
|
|
|
|
user={user}
|
|
|
|
|
|
onLogout={onLogout}
|
|
|
|
|
|
breadcrumbs={breadcrumbs}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="min-h-screen flex flex-col">
|
|
|
|
|
|
{renderHeader()}
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex-1 flex">
|
|
|
|
|
|
{/* Main Content */}
|
|
|
|
|
|
<div className="flex-1 p-6 space-y-6 overflow-y-auto">
|
|
|
|
|
|
{renderHeroSection()}
|
|
|
|
|
|
{renderStatsSection()}
|
|
|
|
|
|
{renderTeamSection()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Right Rail */}
|
|
|
|
|
|
{renderRightRail()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Modals */}
|
|
|
|
|
|
<InternalLinkPicker
|
|
|
|
|
|
isOpen={isLinkPickerOpen}
|
|
|
|
|
|
onClose={() => setIsLinkPickerOpen(false)}
|
|
|
|
|
|
onSelect={handleLinkSelect}
|
|
|
|
|
|
currentLink={{ href: hero.cta.internalHref, title: '' }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<PreviewModal
|
|
|
|
|
|
isOpen={isPreviewOpen}
|
|
|
|
|
|
onClose={() => setIsPreviewOpen(false)}
|
|
|
|
|
|
pageTitle="About Us"
|
|
|
|
|
|
pageData={{ hero, stats, teamMembers }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<VersionHistory
|
|
|
|
|
|
isOpen={isVersionHistoryOpen}
|
|
|
|
|
|
onClose={() => setIsVersionHistoryOpen(false)}
|
|
|
|
|
|
pageTitle="About Us"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<AuditDrawer
|
|
|
|
|
|
isOpen={isAuditOpen}
|
|
|
|
|
|
onClose={() => setIsAuditOpen(false)}
|
|
|
|
|
|
pageTitle="About Us"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</AuthenticatedLayout>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|