Files
igny8/frontend/src/pages/Sites/Dashboard.tsx
IGNY8 VPS (Salman) b05421325c Refactor Site Management Components and Update URL Parameters
- Changed `siteId` to `id` in `useParams` across multiple site-related components for consistency.
- Removed "Sites" from the sidebar settings menu.
- Updated navigation links in `SiteDashboard` to reflect new paths for integrations and deployments.
- Enhanced `SiteList` component with improved site management features, including modals for site creation and sector configuration.
- Added functionality to handle industry and sector selection for sites, with validation for maximum selections.
- Improved UI elements and alerts for better user experience in site management.
2025-11-18 06:02:13 +00:00

305 lines
8.6 KiB
TypeScript

/**
* Site Dashboard (Advanced)
* Phase 7: UI Components & Prompt Management
* Site overview with statistics and analytics
*/
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import {
EyeIcon,
FileTextIcon,
PlugIcon,
TrendingUpIcon,
CalendarIcon,
GlobeIcon
} from 'lucide-react';
import PageMeta from '../../components/common/PageMeta';
import { Card } from '../../components/ui/card';
import Button from '../../components/ui/button/Button';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { fetchAPI } from '../../services/api';
interface Site {
id: number;
name: string;
slug: string;
site_type: string;
hosting_type: string;
status: string;
is_active: boolean;
created_at: string;
updated_at: string;
domain?: string;
}
interface SiteStats {
total_pages: number;
published_pages: number;
draft_pages: number;
total_content: number;
published_content: number;
integrations_count: number;
deployments_count: number;
last_deployment?: string;
views_count?: number;
visitors_count?: number;
}
export default function SiteDashboard() {
const { id: siteId } = useParams<{ id: string }>();
const navigate = useNavigate();
const toast = useToast();
const [site, setSite] = useState<Site | null>(null);
const [stats, setStats] = useState<SiteStats | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (siteId) {
loadSiteData();
}
}, [siteId]);
const loadSiteData = async () => {
try {
setLoading(true);
const [siteData, statsData] = await Promise.all([
fetchAPI(`/v1/auth/sites/${siteId}/`),
fetchSiteStats(),
]);
if (siteData) {
setSite(siteData);
}
if (statsData) {
setStats(statsData);
}
} catch (error: any) {
toast.error(`Failed to load site data: ${error.message}`);
} finally {
setLoading(false);
}
};
const fetchSiteStats = async (): Promise<SiteStats | null> => {
try {
// TODO: Create backend endpoint for site stats
// For now, return mock data structure
return {
total_pages: 0,
published_pages: 0,
draft_pages: 0,
total_content: 0,
published_content: 0,
integrations_count: 0,
deployments_count: 0,
};
} catch (error) {
console.error('Error fetching site stats:', error);
return null;
}
};
if (loading) {
return (
<div className="p-6">
<PageMeta title="Site Dashboard" />
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">Loading site dashboard...</div>
</div>
</div>
);
}
if (!site) {
return (
<div className="p-6">
<PageMeta title="Site Not Found" />
<Card className="p-12 text-center">
<p className="text-gray-600 dark:text-gray-400 mb-4">Site not found</p>
<Button onClick={() => navigate('/sites')} variant="outline">
Back to Sites
</Button>
</Card>
</div>
);
}
const statCards = [
{
label: 'Total Pages',
value: stats?.total_pages || 0,
icon: <FileTextIcon className="w-5 h-5" />,
color: 'blue',
link: `/sites/${siteId}/pages`,
},
{
label: 'Published Pages',
value: stats?.published_pages || 0,
icon: <GlobeIcon className="w-5 h-5" />,
color: 'green',
link: `/sites/${siteId}/pages?status=published`,
},
{
label: 'Draft Pages',
value: stats?.draft_pages || 0,
icon: <FileTextIcon className="w-5 h-5" />,
color: 'amber',
link: `/sites/${siteId}/pages?status=draft`,
},
{
label: 'Integrations',
value: stats?.integrations_count || 0,
icon: <PlugIcon className="w-5 h-5" />,
color: 'purple',
link: `/sites/${siteId}/settings?tab=integrations`,
},
{
label: 'Deployments',
value: stats?.deployments_count || 0,
icon: <TrendingUpIcon className="w-5 h-5" />,
color: 'teal',
link: `/sites/${siteId}/preview`,
},
{
label: 'Total Content',
value: stats?.total_content || 0,
icon: <FileTextIcon className="w-5 h-5" />,
color: 'indigo',
link: `/sites/${siteId}/content`,
},
];
return (
<div className="p-6">
<PageMeta title={`${site.name} - Dashboard`} />
{/* Header */}
<div className="mb-6 flex justify-between items-start">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
{site.name}
</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
{site.slug} {site.site_type} {site.hosting_type}
</p>
{site.domain && (
<p className="text-sm text-gray-500 dark:text-gray-500 mt-1">
<GlobeIcon className="w-4 h-4 inline mr-1" />
{site.domain}
</p>
)}
</div>
<div className="flex gap-2">
<Button
variant="outline"
onClick={() => navigate(`/sites/${siteId}/preview`)}
>
<EyeIcon className="w-4 h-4 mr-2" />
Preview
</Button>
<Button
variant="primary"
onClick={() => navigate(`/sites/${siteId}/settings`)}
>
Settings
</Button>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
{statCards.map((stat, index) => (
<Card
key={index}
className="p-4 hover:shadow-lg transition-shadow cursor-pointer"
onClick={() => stat.link && navigate(stat.link)}
>
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
{stat.label}
</p>
<p className="text-2xl font-bold text-gray-900 dark:text-white">
{stat.value}
</p>
</div>
<div className={`text-${stat.color}-500`}>
{stat.icon}
</div>
</div>
</Card>
))}
</div>
{/* Quick Actions */}
<Card className="p-6 mb-6">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Quick Actions
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
<Button
variant="outline"
onClick={() => navigate(`/sites/${siteId}/pages`)}
className="justify-start"
>
<FileTextIcon className="w-4 h-4 mr-2" />
Manage Pages
</Button>
<Button
variant="outline"
onClick={() => navigate(`/sites/${siteId}/content`)}
className="justify-start"
>
<FileTextIcon className="w-4 h-4 mr-2" />
Manage Content
</Button>
<Button
variant="outline"
onClick={() => navigate(`/sites/${siteId}/settings?tab=integrations`)}
className="justify-start"
>
<PlugIcon className="w-4 h-4 mr-2" />
Manage Integrations
</Button>
<Button
variant="outline"
onClick={() => navigate(`/sites/${siteId}/editor`)}
className="justify-start"
>
<FileTextIcon className="w-4 h-4 mr-2" />
Edit Site
</Button>
</div>
</Card>
{/* Recent Activity */}
<Card className="p-6">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
Recent Activity
</h2>
<div className="space-y-3">
{stats?.last_deployment ? (
<div className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<CalendarIcon className="w-5 h-5 text-gray-400" />
<div>
<p className="text-sm font-medium text-gray-900 dark:text-white">
Last Deployment
</p>
<p className="text-xs text-gray-600 dark:text-gray-400">
{new Date(stats.last_deployment).toLocaleString()}
</p>
</div>
</div>
) : (
<p className="text-sm text-gray-600 dark:text-gray-400">
No recent activity
</p>
)}
</div>
</Card>
</div>
);
}