Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
/**
* DashboardTemplate - Master template for module dashboard pages
*
* Usage:
* <DashboardTemplate
* title="Planner Dashboard"
* subtitle="Manage your content planning workflow"
* kpiCards={kpiConfig}
* workflowSteps={workflowConfig}
* charts={chartsConfig}
* recentActivity={activityConfig}
* />
*/
import React, { ReactNode } from 'react';
interface KPICard {
title: string;
value: string | number;
description?: string;
icon?: ReactNode;
trend?: {
value: number;
isPositive: boolean;
};
}
interface WorkflowStep {
step: number;
title: string;
status: 'completed' | 'pending' | 'in_progress';
description?: string;
count?: number;
actionLabel?: string;
onAction?: () => void;
}
interface ChartConfig {
title: string;
type: 'bar' | 'line' | 'pie' | 'progress';
data: any;
}
interface DashboardTemplateProps {
title: string;
subtitle?: string;
kpiCards?: KPICard[];
workflowSteps?: WorkflowStep[];
charts?: ChartConfig[];
recentActivity?: ReactNode;
quickActions?: ReactNode;
className?: string;
}
export default function DashboardTemplate({
title,
subtitle,
kpiCards = [],
workflowSteps = [],
charts = [],
recentActivity,
quickActions,
className = '',
}: DashboardTemplateProps) {
return (
<div className={className}>
{/* Page Header */}
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white/90 mb-1">{title}</h2>
{subtitle && (
<p className="text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
)}
</div>
{/* KPI Cards Row */}
{kpiCards.length > 0 && (
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-6">
{kpiCards.map((card, index) => (
<div
key={index}
className="rounded-xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] sm:p-6"
>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">
{card.title}
</p>
<p className="text-2xl font-semibold text-gray-800 dark:text-white/90">
{card.value}
</p>
{card.trend && (
<div className="flex items-center mt-1">
<span
className={`text-xs font-medium ${
card.trend.isPositive ? 'text-success-500' : 'text-error-500'
}`}
>
{card.trend.isPositive ? '↑' : '↓'} {Math.abs(card.trend.value)}%
</span>
</div>
)}
</div>
{card.icon && (
<div className="flex h-14 w-14 items-center justify-center rounded-[10.5px] bg-brand-50 text-brand-500 dark:bg-brand-500/10">
{card.icon}
</div>
)}
</div>
{card.description && (
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
{card.description}
</p>
)}
</div>
))}
</div>
)}
{/* Quick Actions */}
{quickActions && (
<div className="mb-6">
{quickActions}
</div>
)}
{/* Workflow Steps */}
{workflowSteps.length > 0 && (
<div className="mb-6">
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] p-6">
<h3 className="text-base font-medium text-gray-800 dark:text-white/90 mb-4">
Workflow Steps - Track your planning progress
</h3>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
{workflowSteps.map((step) => (
<div
key={step.step}
className="rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-800 dark:bg-white/[0.03]"
>
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-gray-500 dark:text-gray-400">
{step.step}. {step.title}
</span>
<span
className={`text-xs font-medium px-2 py-1 rounded ${
step.status === 'completed'
? 'bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500'
: step.status === 'in_progress'
? 'bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-warning-500'
: 'bg-gray-100 text-gray-600 dark:bg-white/5 dark:text-gray-400'
}`}
>
{step.status === 'completed' ? 'Completed' : step.status === 'in_progress' ? 'In Progress' : 'Pending'}
</span>
</div>
{step.description && (
<p className="text-xs text-gray-500 dark:text-gray-400 mb-2">
{step.description}
</p>
)}
{step.count !== undefined && (
<p className="text-sm font-medium text-gray-800 dark:text-white/90 mb-2">
{step.count} items
</p>
)}
{step.actionLabel && step.onAction && step.status !== 'completed' && (
<button
onClick={step.onAction}
className="text-sm text-brand-500 hover:text-brand-600 font-medium"
>
{step.actionLabel}
</button>
)}
</div>
))}
</div>
</div>
</div>
)}
{/* Charts Row */}
{charts.length > 0 && (
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2 mb-6">
{charts.map((chart, index) => (
<div
key={index}
className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] p-6"
>
<h3 className="text-base font-medium text-gray-800 dark:text-white/90 mb-4">
{chart.title}
</h3>
{/* Chart component would be rendered here */}
<div className="h-64 flex items-center justify-center text-gray-400">
Chart: {chart.type}
</div>
</div>
))}
</div>
)}
{/* Recent Activity */}
{recentActivity && (
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] p-6">
<h3 className="text-base font-medium text-gray-800 dark:text-white/90 mb-4">
Recent Activity
</h3>
{recentActivity}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,113 @@
/**
* FormPageTemplate - Master template for settings/form pages
*
* Usage:
* <FormPageTemplate
* title="General Settings"
* subtitle="Configure your application settings"
* sections={formSections}
* onSave={handleSave}
* onCancel={handleCancel}
* />
*/
import React, { ReactNode, useState } from 'react';
interface FormSection {
title: string;
description?: string;
fields: ReactNode;
}
interface FormPageTemplateProps {
title: string;
subtitle?: string;
sections: FormSection[];
onSave?: (data: any) => void | Promise<void>;
onCancel?: () => void;
saveLabel?: string;
cancelLabel?: string;
loading?: boolean;
className?: string;
}
export default function FormPageTemplate({
title,
subtitle,
sections,
onSave,
onCancel,
saveLabel = 'Save Changes',
cancelLabel = 'Cancel',
loading = false,
className = '',
}: FormPageTemplateProps) {
const [isDirty, setIsDirty] = useState(false);
const handleSave = async () => {
if (onSave) {
await onSave({});
setIsDirty(false);
}
};
return (
<div className={className}>
{/* Page Header */}
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white/90 mb-1">{title}</h2>
{subtitle && (
<p className="text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
)}
</div>
{/* Form Sections */}
<div className="space-y-6">
{sections.map((section, index) => (
<div
key={index}
className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]"
>
<div className="border-b border-gray-100 px-6 py-5 dark:border-gray-800">
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
{section.title}
</h3>
{section.description && (
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
{section.description}
</p>
)}
</div>
<div className="p-6">
{section.fields}
</div>
</div>
))}
</div>
{/* Save Bar */}
{(onSave || onCancel) && (
<div className="mt-6 flex items-center justify-end gap-3 rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-800 dark:bg-white/[0.03]">
{onCancel && (
<button
onClick={onCancel}
className="rounded-lg border border-gray-200 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-white/[0.03]"
>
{cancelLabel}
</button>
)}
{onSave && (
<button
onClick={handleSave}
disabled={loading || !isDirty}
className="inline-flex items-center justify-center gap-2 rounded-lg bg-brand-500 px-4 py-2.5 text-sm font-medium text-white shadow-theme-xs hover:bg-brand-600 disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? 'Saving...' : saveLabel}
</button>
)}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,192 @@
/**
* SystemPageTemplate - Master template for system/admin pages (logs, status, monitoring)
*
* Usage:
* <SystemPageTemplate
* title="System Status"
* statusCards={statusConfig}
* logs={logsData}
* charts={monitoringCharts}
* />
*/
import React, { ReactNode } from 'react';
interface StatusCard {
title: string;
value: string | number;
status: 'success' | 'warning' | 'error' | 'info';
description?: string;
icon?: ReactNode;
}
interface LogEntry {
timestamp: string;
level: 'info' | 'warning' | 'error' | 'success';
message: string;
module?: string;
}
interface SystemPageTemplateProps {
title: string;
subtitle?: string;
statusCards?: StatusCard[];
logs?: LogEntry[];
charts?: ReactNode[];
className?: string;
}
export default function SystemPageTemplate({
title,
subtitle,
statusCards = [],
logs = [],
charts = [],
className = '',
}: SystemPageTemplateProps) {
const getStatusColor = (status: string) => {
switch (status) {
case 'success':
return 'bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500';
case 'warning':
return 'bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-warning-500';
case 'error':
return 'bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500';
default:
return 'bg-blue-light-50 text-blue-light-500 dark:bg-blue-light-500/15 dark:text-blue-light-500';
}
};
const getLogLevelColor = (level: string) => {
switch (level) {
case 'error':
return 'text-error-600 dark:text-error-500';
case 'warning':
return 'text-warning-600 dark:text-warning-500';
case 'success':
return 'text-success-600 dark:text-success-500';
default:
return 'text-blue-light-600 dark:text-blue-light-500';
}
};
return (
<div className={className}>
{/* Page Header */}
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white/90 mb-1">{title}</h2>
{subtitle && (
<p className="text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
)}
</div>
{/* Status Cards */}
{statusCards.length > 0 && (
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4 mb-6">
{statusCards.map((card, index) => (
<div
key={index}
className="rounded-xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] sm:p-6"
>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">
{card.title}
</p>
<p className="text-2xl font-semibold text-gray-800 dark:text-white/90">
{card.value}
</p>
</div>
{card.icon && (
<div className={`flex h-14 w-14 items-center justify-center rounded-[10.5px] ${getStatusColor(card.status)}`}>
{card.icon}
</div>
)}
</div>
{card.description && (
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">
{card.description}
</p>
)}
<div className="mt-3">
<span
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(card.status)}`}
>
{card.status}
</span>
</div>
</div>
))}
</div>
)}
{/* Charts */}
{charts.length > 0 && (
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2 mb-6">
{charts.map((chart, index) => (
<div
key={index}
className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] p-6"
>
{chart}
</div>
))}
</div>
)}
{/* Logs Table */}
{logs.length > 0 && (
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] overflow-hidden">
<div className="border-b border-gray-100 px-6 py-5 dark:border-gray-800">
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
System Logs
</h3>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-gray-100 bg-gray-50 dark:border-gray-800 dark:bg-white/[0.03]">
<tr>
<th className="px-6 py-4 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">
Timestamp
</th>
<th className="px-6 py-4 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">
Level
</th>
<th className="px-6 py-4 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">
Module
</th>
<th className="px-6 py-4 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-400">
Message
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 dark:divide-gray-800">
{logs.map((log, index) => (
<tr key={index} className="hover:bg-gray-50 dark:hover:bg-white/[0.03]">
<td className="px-6 py-4 text-sm text-gray-800 dark:text-white/90">
{log.timestamp}
</td>
<td className="px-6 py-4">
<span
className={`text-xs font-medium ${getLogLevelColor(log.level)}`}
>
{log.level.toUpperCase()}
</span>
</td>
<td className="px-6 py-4 text-sm text-gray-800 dark:text-white/90">
{log.module || '-'}
</td>
<td className="px-6 py-4 text-sm text-gray-800 dark:text-white/90">
{log.message}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
);
}

File diff suppressed because it is too large Load Diff