stlyes fixes
This commit is contained in:
@@ -93,7 +93,7 @@ export default function SignUp() {
|
||||
/>
|
||||
|
||||
{/* Right Side - Pricing Plans */}
|
||||
<div className="hidden lg:flex lg:w-1/2 bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800 p-8 xl:p-12 items-start justify-center relative">
|
||||
<div className="hidden lg:flex lg:w-1/2 bg-gradient-to-br from-brand-50 to-purple-50 dark:from-gray-900 dark:to-gray-800 p-8 xl:p-12 items-start justify-center relative">
|
||||
{/* Logo - Top Right */}
|
||||
<Link to="/" className="absolute top-6 right-6">
|
||||
<img
|
||||
|
||||
@@ -39,13 +39,13 @@ import {
|
||||
} from '../../icons';
|
||||
|
||||
const STAGE_CONFIG = [
|
||||
{ icon: ListIcon, color: 'from-blue-500 to-blue-600', textColor: 'text-blue-600', hoverColor: 'hover:border-blue-500', name: 'Keywords → Clusters' },
|
||||
{ icon: ListIcon, color: 'from-brand-500 to-brand-600', textColor: 'text-brand-600', hoverColor: 'hover:border-brand-500', name: 'Keywords → Clusters' },
|
||||
{ icon: GroupIcon, color: 'from-purple-500 to-purple-600', textColor: 'text-purple-600', hoverColor: 'hover:border-purple-500', name: 'Clusters → Ideas' },
|
||||
{ icon: CheckCircleIcon, color: 'from-indigo-500 to-indigo-600', textColor: 'text-indigo-600', hoverColor: 'hover:border-indigo-500', name: 'Ideas → Tasks' },
|
||||
{ icon: PencilIcon, color: 'from-green-500 to-green-600', textColor: 'text-green-600', hoverColor: 'hover:border-green-500', name: 'Tasks → Content' },
|
||||
{ icon: FileIcon, color: 'from-amber-500 to-amber-600', textColor: 'text-amber-600', hoverColor: 'hover:border-amber-500', name: 'Content → Image Prompts' },
|
||||
{ icon: FileTextIcon, color: 'from-pink-500 to-pink-600', textColor: 'text-pink-600', hoverColor: 'hover:border-pink-500', name: 'Image Prompts → Images' },
|
||||
{ icon: PaperPlaneIcon, color: 'from-teal-500 to-teal-600', textColor: 'text-teal-600', hoverColor: 'hover:border-teal-500', name: 'Review Gate' },
|
||||
{ icon: CheckCircleIcon, color: 'from-purple-500 to-purple-600', textColor: 'text-purple-600', hoverColor: 'hover:border-purple-500', name: 'Ideas → Tasks' },
|
||||
{ icon: PencilIcon, color: 'from-success-500 to-success-600', textColor: 'text-success-600', hoverColor: 'hover:border-success-500', name: 'Tasks → Content' },
|
||||
{ icon: FileIcon, color: 'from-warning-500 to-warning-600', textColor: 'text-warning-600', hoverColor: 'hover:border-warning-500', name: 'Content → Image Prompts' },
|
||||
{ icon: FileTextIcon, color: 'from-purple-500 to-purple-600', textColor: 'text-purple-600', hoverColor: 'hover:border-purple-500', name: 'Image Prompts → Images' },
|
||||
{ icon: PaperPlaneIcon, color: 'from-success-500 to-success-600', textColor: 'text-purple-600', hoverColor: 'hover:border-purple-500', name: 'Review Gate' },
|
||||
];
|
||||
|
||||
const AutomationPage: React.FC = () => {
|
||||
@@ -386,7 +386,7 @@ const AutomationPage: React.FC = () => {
|
||||
{visible.map((it, idx) => (
|
||||
<div key={idx} className="w-1/3 text-center">
|
||||
<span className={`${it.colorCls ?? ''} block`}>{it.label}</span>
|
||||
<div className="font-bold text-slate-900 dark:text-white">{Number(it.value) || 0}</div>
|
||||
<div className="font-bold text-gray-900 dark:text-white">{Number(it.value) || 0}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -399,7 +399,7 @@ const AutomationPage: React.FC = () => {
|
||||
{items.concat(Array(Math.max(0, 3 - items.length)).fill({ label: '', value: '' })).slice(0,3).map((it, idx) => (
|
||||
<div key={idx} className="flex-1 text-center">
|
||||
<span className={`${it.colorCls ?? ''} block`}>{it.label}</span>
|
||||
<div className="font-bold text-slate-900 dark:text-white">{it.value !== undefined && it.value !== null ? Number(it.value) : ''}</div>
|
||||
<div className="font-bold text-gray-900 dark:text-white">{it.value !== undefined && it.value !== null ? Number(it.value) : ''}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -419,19 +419,19 @@ const AutomationPage: React.FC = () => {
|
||||
{/* Compact Ready-to-Run card (header) - absolutely centered in header */}
|
||||
<div className="flex justify-center">
|
||||
<div className={`w-full max-w-sm rounded-lg border-2 p-2 transition-all flex items-center gap-3 shadow-sm
|
||||
${currentRun?.status === 'running' ? 'border-blue-500 bg-blue-50' : currentRun?.status === 'paused' ? 'border-amber-500 bg-amber-50' : totalPending > 0 ? 'border-success-500 bg-success-50' : 'border-slate-300 bg-slate-50'}`}>
|
||||
${currentRun?.status === 'running' ? 'border-brand-500 bg-brand-50' : currentRun?.status === 'paused' ? 'border-warning-500 bg-warning-50' : totalPending > 0 ? 'border-success-500 bg-success-50' : 'border-gray-300 bg-gray-50'}`}>
|
||||
<div className={`size-9 rounded-lg flex items-center justify-center flex-shrink-0
|
||||
${currentRun?.status === 'running' ? 'bg-gradient-to-br from-blue-500 to-blue-600' : currentRun?.status === 'paused' ? 'bg-gradient-to-br from-amber-500 to-amber-600' : totalPending > 0 ? 'bg-gradient-to-br from-success-500 to-success-600' : 'bg-gradient-to-br from-slate-400 to-slate-500'}`}>
|
||||
${currentRun?.status === 'running' ? 'bg-gradient-to-br from-brand-500 to-brand-600' : currentRun?.status === 'paused' ? 'bg-gradient-to-br from-warning-500 to-warning-600' : totalPending > 0 ? 'bg-gradient-to-br from-success-500 to-success-600' : 'bg-gradient-to-br from-gray-400 to-gray-500'}`}>
|
||||
{!currentRun && totalPending > 0 ? <CheckCircleIcon className="size-4 text-white" /> : currentRun?.status === 'running' ? <BoltIcon className="size-4 text-white" /> : currentRun?.status === 'paused' ? <ClockIcon className="size-4 text-white" /> : <BoltIcon className="size-4 text-white" />}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-semibold text-slate-900 dark:text-white truncate">
|
||||
<div className="text-sm font-semibold text-gray-900 dark:text-white truncate">
|
||||
{currentRun?.status === 'running' && `Running - Stage ${currentRun.current_stage}/7`}
|
||||
{currentRun?.status === 'paused' && 'Paused'}
|
||||
{!currentRun && totalPending > 0 && 'Ready to Run'}
|
||||
{!currentRun && totalPending === 0 && 'No Items Pending'}
|
||||
</div>
|
||||
<div className="text-xs text-slate-600 dark:text-gray-400 truncate">
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400 truncate">
|
||||
{currentRun ? `Started: ${new Date(currentRun.started_at).toLocaleTimeString()}` : (totalPending > 0 ? `${totalPending} items in pipeline` : 'All stages clear')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -525,20 +525,20 @@ const AutomationPage: React.FC = () => {
|
||||
{/* Metrics Summary Cards */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
{/* Keywords */}
|
||||
<div className="bg-gradient-to-br from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/20 rounded-xl p-4 border-2 border-blue-200 dark:border-blue-800">
|
||||
<div className="bg-gradient-to-br from-brand-50 to-brand-100 dark:from-brand-900/20 dark:to-brand-800/20 rounded-xl p-4 border-2 border-brand-200 dark:border-brand-800">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-brand-500 to-brand-600 flex items-center justify-center">
|
||||
<ListIcon className="size-5 text-white" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-blue-900 dark:text-blue-100">Keywords</div>
|
||||
<div className="text-sm font-bold text-brand-900 dark:text-brand-100">Keywords</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const res = getStageResult(1);
|
||||
const total = res?.total ?? pipelineOverview[0]?.counts?.total ?? metrics?.keywords?.total ?? pipelineOverview[0]?.pending ?? 0;
|
||||
return (
|
||||
<div className="text-right">
|
||||
<div className="text-3xl font-bold text-blue-900">{total}</div>
|
||||
<div className="text-3xl font-bold text-brand-900">{total}</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
@@ -549,8 +549,8 @@ const AutomationPage: React.FC = () => {
|
||||
const mapped = res?.mapped ?? pipelineOverview[0]?.counts?.mapped ?? metrics?.keywords?.mapped ?? 0;
|
||||
return (
|
||||
renderMetricRow([
|
||||
{ label: 'New:', value: newCount, colorCls: 'text-blue-700' },
|
||||
{ label: 'Mapped:', value: mapped, colorCls: 'text-blue-700' },
|
||||
{ label: 'New:', value: newCount, colorCls: 'text-brand-700' },
|
||||
{ label: 'Mapped:', value: mapped, colorCls: 'text-brand-700' },
|
||||
])
|
||||
);
|
||||
})()}
|
||||
@@ -589,20 +589,20 @@ const AutomationPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Ideas */}
|
||||
<div className="bg-gradient-to-br from-indigo-50 to-indigo-100 dark:from-indigo-900/20 dark:to-indigo-800/20 rounded-xl p-4 border-2 border-indigo-200 dark:border-indigo-800">
|
||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/20 rounded-xl p-4 border-2 border-purple-200 dark:border-purple-800">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-indigo-500 to-indigo-600 flex items-center justify-center">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-purple-500 to-purple-600 flex items-center justify-center">
|
||||
<CheckCircleIcon className="size-5 text-white" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-indigo-900 dark:text-indigo-100">Ideas</div>
|
||||
<div className="text-sm font-bold text-purple-900 dark:text-purple-100">Ideas</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const res = getStageResult(3);
|
||||
const total = res?.total ?? pipelineOverview[2]?.counts?.total ?? metrics?.ideas?.total ?? pipelineOverview[2]?.pending ?? 0;
|
||||
return (
|
||||
<div className="text-right">
|
||||
<div className="text-3xl font-bold text-indigo-900">{total}</div>
|
||||
<div className="text-3xl font-bold text-purple-900">{total}</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
@@ -614,29 +614,29 @@ const AutomationPage: React.FC = () => {
|
||||
const completed = res?.completed ?? pipelineOverview[2]?.counts?.completed ?? metrics?.ideas?.completed ?? 0;
|
||||
return (
|
||||
renderMetricRow([
|
||||
{ label: 'New:', value: newCount, colorCls: 'text-indigo-700' },
|
||||
{ label: 'Queued:', value: queued, colorCls: 'text-indigo-700' },
|
||||
{ label: 'Completed:', value: completed, colorCls: 'text-indigo-700' },
|
||||
{ label: 'New:', value: newCount, colorCls: 'text-purple-700' },
|
||||
{ label: 'Queued:', value: queued, colorCls: 'text-purple-700' },
|
||||
{ label: 'Completed:', value: completed, colorCls: 'text-purple-700' },
|
||||
])
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/20 rounded-xl p-4 border-2 border-green-200 dark:border-green-800">
|
||||
<div className="bg-gradient-to-br from-success-50 to-success-100 dark:from-success-900/20 dark:to-success-800/20 rounded-xl p-4 border-2 border-success-200 dark:border-success-800">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-success-500 to-success-600 flex items-center justify-center">
|
||||
<FileTextIcon className="size-5 text-white" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-green-900 dark:text-green-100">Content</div>
|
||||
<div className="text-sm font-bold text-success-900 dark:text-success-100">Content</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const res = getStageResult(4);
|
||||
const total = res?.total ?? pipelineOverview[3]?.counts?.total ?? metrics?.content?.total ?? pipelineOverview[3]?.pending ?? 0;
|
||||
return (
|
||||
<div className="text-right">
|
||||
<div className="text-3xl font-bold text-green-900">{total}</div>
|
||||
<div className="text-3xl font-bold text-success-900">{total}</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
@@ -648,29 +648,29 @@ const AutomationPage: React.FC = () => {
|
||||
const publish = res?.published ?? res?.publish ?? pipelineOverview[3]?.counts?.published ?? metrics?.content?.published ?? 0;
|
||||
return (
|
||||
renderMetricRow([
|
||||
{ label: 'Draft:', value: draft, colorCls: 'text-green-700' },
|
||||
{ label: 'Review:', value: review, colorCls: 'text-green-700' },
|
||||
{ label: 'Publish:', value: publish, colorCls: 'text-green-700' },
|
||||
{ label: 'Draft:', value: draft, colorCls: 'text-success-700' },
|
||||
{ label: 'Review:', value: review, colorCls: 'text-success-700' },
|
||||
{ label: 'Publish:', value: publish, colorCls: 'text-success-700' },
|
||||
])
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Images */}
|
||||
<div className="bg-gradient-to-br from-pink-50 to-pink-100 dark:from-pink-900/20 dark:to-pink-800/20 rounded-xl p-4 border-2 border-pink-200 dark:border-pink-800">
|
||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/20 rounded-xl p-4 border-2 border-purple-200 dark:border-purple-800">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-pink-500 to-pink-600 flex items-center justify-center">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-purple-500 to-purple-600 flex items-center justify-center">
|
||||
<FileIcon className="size-5 text-white" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-pink-900 dark:text-pink-100">Images</div>
|
||||
<div className="text-sm font-bold text-purple-900 dark:text-purple-100">Images</div>
|
||||
</div>
|
||||
{(() => {
|
||||
const res = getStageResult(6);
|
||||
const total = res?.total ?? pipelineOverview[5]?.counts?.total ?? metrics?.images?.total ?? pipelineOverview[5]?.pending ?? 0;
|
||||
return (
|
||||
<div className="text-right">
|
||||
<div className="text-3xl font-bold text-pink-900">{total}</div>
|
||||
<div className="text-3xl font-bold text-purple-900">{total}</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
@@ -679,17 +679,17 @@ const AutomationPage: React.FC = () => {
|
||||
const res = getStageResult(6); // stage 6 is Image Prompts -> Images
|
||||
if (res && typeof res === 'object') {
|
||||
const entries = Object.entries(res);
|
||||
const items = entries.slice(0,3).map(([k, v]) => ({ label: `${k.replace(/_/g, ' ')}:`, value: Number(v) || 0, colorCls: 'text-pink-700' }));
|
||||
const items = entries.slice(0,3).map(([k, v]) => ({ label: `${k.replace(/_/g, ' ')}:`, value: Number(v) || 0, colorCls: 'text-purple-700' }));
|
||||
return renderMetricRow(items);
|
||||
}
|
||||
const counts = pipelineOverview[5]?.counts ?? metrics?.images ?? null;
|
||||
if (counts && typeof counts === 'object') {
|
||||
const entries = Object.entries(counts);
|
||||
const items = entries.slice(0,3).map(([k, v]) => ({ label: `${k.replace(/_/g, ' ')}:`, value: Number(v) || 0, colorCls: 'text-pink-700' }));
|
||||
const items = entries.slice(0,3).map(([k, v]) => ({ label: `${k.replace(/_/g, ' ')}:`, value: Number(v) || 0, colorCls: 'text-purple-700' }));
|
||||
return renderMetricRow(items);
|
||||
}
|
||||
return renderMetricRow([
|
||||
{ label: 'Pending:', value: pipelineOverview[5]?.pending ?? metrics?.images?.pending ?? 0, colorCls: 'text-pink-700' },
|
||||
{ label: 'Pending:', value: pipelineOverview[5]?.pending ?? metrics?.images?.pending ?? 0, colorCls: 'text-purple-700' },
|
||||
]);
|
||||
})()}
|
||||
</div>
|
||||
@@ -772,12 +772,12 @@ const AutomationPage: React.FC = () => {
|
||||
className={`
|
||||
relative rounded-xl border-2 p-5 transition-all
|
||||
${isActive
|
||||
? 'border-blue-500 bg-blue-50 dark:bg-blue-500/10 shadow-lg'
|
||||
? 'border-brand-500 bg-brand-50 dark:bg-brand-500/10 shadow-lg'
|
||||
: isComplete
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-500/10'
|
||||
: stage.pending > 0
|
||||
? `border-slate-200 bg-white dark:bg-white/[0.03] dark:border-gray-800 ${stageConfig.hoverColor} hover:shadow-lg`
|
||||
: 'border-slate-200 bg-slate-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
? `border-gray-200 bg-white dark:bg-white/[0.03] dark:border-gray-800 ${stageConfig.hoverColor} hover:shadow-lg`
|
||||
: 'border-gray-200 bg-gray-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
}
|
||||
`}
|
||||
>
|
||||
@@ -786,7 +786,7 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<div className="text-sm font-bold text-gray-900 dark:text-white">Stage {stage.number}</div>
|
||||
{isActive && <span className="text-xs px-2 py-0.5 bg-blue-500 text-white rounded-full">● Active</span>}
|
||||
{isActive && <span className="text-xs px-2 py-0.5 bg-brand-500 text-white rounded-full">● Active</span>}
|
||||
{isComplete && <span className="text-xs px-2 py-0.5 bg-success-500 text-white rounded-full">✓</span>}
|
||||
{!isActive && !isComplete && stage.pending > 0 && <span className="text-xs px-2 py-0.5 bg-gray-400 text-white rounded-full">Ready</span>}
|
||||
</div>
|
||||
@@ -804,21 +804,21 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="space-y-1.5 text-xs mb-3">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Pending:</span>
|
||||
<span className={`font-bold ${pending > 0 ? stageConfig.textColor : 'text-slate-400 dark:text-gray-500'}`}>
|
||||
<span className={`font-bold ${pending > 0 ? stageConfig.textColor : 'text-gray-400 dark:text-gray-500'}`}>
|
||||
{pending}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Processed:</span>
|
||||
<span className={`font-bold ${processed > 0 ? 'text-success-600 dark:text-success-400' : 'text-slate-400 dark:text-gray-500'}`}>
|
||||
<span className={`font-bold ${processed > 0 ? 'text-success-600 dark:text-success-400' : 'text-gray-400 dark:text-gray-500'}`}>
|
||||
{processed}
|
||||
</span>
|
||||
</div>
|
||||
{/* Credits and Duration - only show during/after run */}
|
||||
{result && (result.credits_used > 0 || result.time_elapsed) && (
|
||||
<div className="flex justify-between pt-1.5 border-t border-slate-200 dark:border-gray-700 text-[10px]">
|
||||
<div className="flex justify-between pt-1.5 border-t border-gray-200 dark:border-gray-700 text-[10px]">
|
||||
{result.credits_used > 0 && (
|
||||
<span className="text-amber-600 dark:text-amber-400">{result.credits_used} credits</span>
|
||||
<span className="text-warning-600 dark:text-warning-400">{result.credits_used} credits</span>
|
||||
)}
|
||||
{result.time_elapsed && (
|
||||
<span className="text-gray-500 dark:text-gray-400">{result.time_elapsed}</span>
|
||||
@@ -829,7 +829,7 @@ const AutomationPage: React.FC = () => {
|
||||
|
||||
{/* Progress Bar */}
|
||||
{(isActive || isComplete || processed > 0) && (
|
||||
<div className="mt-3 pt-3 border-t border-slate-200 dark:border-gray-700">
|
||||
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex justify-between text-xs text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<span>Progress</span>
|
||||
<span>{isComplete ? '100' : progressPercent}%</span>
|
||||
@@ -885,12 +885,12 @@ const AutomationPage: React.FC = () => {
|
||||
className={`
|
||||
relative rounded-xl border-2 p-5 transition-all
|
||||
${isActive
|
||||
? 'border-blue-500 bg-blue-50 dark:bg-blue-500/10 shadow-lg'
|
||||
? 'border-brand-500 bg-brand-50 dark:bg-brand-500/10 shadow-lg'
|
||||
: isComplete
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-500/10'
|
||||
: stage.pending > 0
|
||||
? `border-slate-200 bg-white dark:bg-white/[0.03] dark:border-gray-800 ${stageConfig.hoverColor} hover:shadow-lg`
|
||||
: 'border-slate-200 bg-slate-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
? `border-gray-200 bg-white dark:bg-white/[0.03] dark:border-gray-800 ${stageConfig.hoverColor} hover:shadow-lg`
|
||||
: 'border-gray-200 bg-gray-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
}
|
||||
`}
|
||||
>
|
||||
@@ -898,7 +898,7 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<div className="text-sm font-bold text-gray-900 dark:text-white">Stage {stage.number}</div>
|
||||
{isActive && <span className="text-xs px-2 py-0.5 bg-blue-500 text-white rounded-full">● Active</span>}
|
||||
{isActive && <span className="text-xs px-2 py-0.5 bg-brand-500 text-white rounded-full">● Active</span>}
|
||||
{isComplete && <span className="text-xs px-2 py-0.5 bg-success-500 text-white rounded-full">✓</span>}
|
||||
{!isActive && !isComplete && stage.pending > 0 && <span className="text-xs px-2 py-0.5 bg-gray-400 text-white rounded-full">Ready</span>}
|
||||
</div>
|
||||
@@ -916,21 +916,21 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="space-y-1.5 text-xs mb-3">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Pending:</span>
|
||||
<span className={`font-bold ${pending > 0 ? stageConfig.textColor : 'text-slate-400 dark:text-gray-500'}`}>
|
||||
<span className={`font-bold ${pending > 0 ? stageConfig.textColor : 'text-gray-400 dark:text-gray-500'}`}>
|
||||
{pending}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Processed:</span>
|
||||
<span className={`font-bold ${processed > 0 ? 'text-success-600 dark:text-success-400' : 'text-slate-400 dark:text-gray-500'}`}>
|
||||
<span className={`font-bold ${processed > 0 ? 'text-success-600 dark:text-success-400' : 'text-gray-400 dark:text-gray-500'}`}>
|
||||
{processed}
|
||||
</span>
|
||||
</div>
|
||||
{/* Credits and Duration - only show during/after run */}
|
||||
{result && (result.credits_used > 0 || result.time_elapsed) && (
|
||||
<div className="flex justify-between pt-1.5 border-t border-slate-200 dark:border-gray-700 text-[10px]">
|
||||
<div className="flex justify-between pt-1.5 border-t border-gray-200 dark:border-gray-700 text-[10px]">
|
||||
{result.credits_used > 0 && (
|
||||
<span className="text-amber-600 dark:text-amber-400">{result.credits_used} credits</span>
|
||||
<span className="text-warning-600 dark:text-warning-400">{result.credits_used} credits</span>
|
||||
)}
|
||||
{result.time_elapsed && (
|
||||
<span className="text-gray-500 dark:text-gray-400">{result.time_elapsed}</span>
|
||||
@@ -940,7 +940,7 @@ const AutomationPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{(isActive || isComplete || processed > 0) && (
|
||||
<div className="mt-3 pt-3 border-t border-slate-200 dark:border-gray-700">
|
||||
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex justify-between text-xs text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<span>Progress</span>
|
||||
<span>{isComplete ? '100' : progressPercent}%</span>
|
||||
@@ -968,12 +968,12 @@ const AutomationPage: React.FC = () => {
|
||||
className={`
|
||||
relative rounded-xl border-3 p-5 transition-all
|
||||
${isActive
|
||||
? 'border-amber-500 bg-amber-50 dark:bg-amber-500/10 shadow-lg'
|
||||
? 'border-warning-500 bg-warning-50 dark:bg-warning-500/10 shadow-lg'
|
||||
: isComplete
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-500/10'
|
||||
: stage7.pending > 0
|
||||
? 'border-amber-300 bg-amber-50 dark:bg-amber-900/20 dark:border-amber-700'
|
||||
: 'border-slate-200 bg-slate-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
? 'border-warning-300 bg-warning-50 dark:bg-warning-900/20 dark:border-warning-700'
|
||||
: 'border-gray-200 bg-gray-50 dark:bg-white/[0.02] dark:border-gray-800'
|
||||
}
|
||||
`}
|
||||
>
|
||||
@@ -981,37 +981,37 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<div className="text-sm font-bold text-gray-900 dark:text-white">Stage 7</div>
|
||||
<span className="text-xs px-2 py-0.5 bg-amber-500 text-white rounded-full">🚫 Stop</span>
|
||||
<span className="text-xs px-2 py-0.5 bg-warning-500 text-white rounded-full">🚫 Stop</span>
|
||||
</div>
|
||||
<div className="text-xs font-medium text-amber-700 dark:text-amber-300">Manual Review Gate</div>
|
||||
<div className="text-xs font-medium text-warning-700 dark:text-warning-300">Manual Review Gate</div>
|
||||
</div>
|
||||
<div className="size-8 rounded-lg bg-gradient-to-br from-amber-500 to-amber-600 flex items-center justify-center shadow-md">
|
||||
<div className="size-8 rounded-lg bg-gradient-to-br from-warning-500 to-warning-600 flex items-center justify-center shadow-md">
|
||||
<PaperPlaneIcon className="size-4 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Simplified: Just show the count, no buttons */}
|
||||
<div className="text-center py-4">
|
||||
<div className="text-3xl font-bold text-amber-600 dark:text-amber-400">{stage7.pending}</div>
|
||||
<div className="text-xs text-amber-700 dark:text-amber-300 mt-1">ready for review</div>
|
||||
<div className="text-3xl font-bold text-warning-600 dark:text-warning-400">{stage7.pending}</div>
|
||||
<div className="text-xs text-warning-700 dark:text-warning-300 mt-1">ready for review</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Approved summary card (placed after Stage 7 in the same row) */}
|
||||
<div className="rounded-xl p-5 border-2 border-green-200 bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/10 dark:to-green-800/10 flex flex-col h-full">
|
||||
<div className="rounded-xl p-5 border-2 border-success-200 bg-gradient-to-br from-success-50 to-success-100 dark:from-success-900/10 dark:to-success-800/10 flex flex-col h-full">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center">
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-success-500 to-success-600 flex items-center justify-center">
|
||||
<FileTextIcon className="size-5 text-white" />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-green-900 dark:text-green-100">Approved</div>
|
||||
<div className="text-sm font-bold text-success-900 dark:text-success-100">Approved</div>
|
||||
</div>
|
||||
<div className="text-right"> </div>
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-4xl md:text-5xl font-extrabold text-green-800 dark:text-green-300">{metrics?.content?.published ?? pipelineOverview[3]?.counts?.published ?? getStageResult(4)?.published ?? 0}</div>
|
||||
<div className="text-4xl md:text-5xl font-extrabold text-success-800 dark:text-success-300">{metrics?.content?.published ?? pipelineOverview[3]?.counts?.published ?? getStageResult(4)?.published ?? 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">Status</div>
|
||||
<div className="font-semibold mt-1">
|
||||
{config.is_enabled ? (
|
||||
<span className="text-green-600 dark:text-green-400">● Enabled</span>
|
||||
<span className="text-success-600 dark:text-success-400">● Enabled</span>
|
||||
) : (
|
||||
<span className="text-gray-600 dark:text-gray-400">○ Disabled</span>
|
||||
)}
|
||||
@@ -246,7 +246,7 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="font-semibold mt-1">
|
||||
{estimate?.estimated_credits || 0} credits
|
||||
{estimate && !estimate.sufficient && (
|
||||
<span className="text-red-600 dark:text-red-400 ml-2">(Insufficient)</span>
|
||||
<span className="text-error-600 dark:text-error-400 ml-2">(Insufficient)</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -301,7 +301,7 @@ const AutomationPage: React.FC = () => {
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{currentRun ? (
|
||||
<>
|
||||
<span className="font-semibold text-blue-600 dark:text-blue-400">● Live Run Active</span> - Stage {currentRun.current_stage} of 7
|
||||
<span className="font-semibold text-brand-600 dark:text-brand-400">● Live Run Active</span> - Stage {currentRun.current_stage} of 7
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -339,10 +339,10 @@ const AutomationPage: React.FC = () => {
|
||||
<div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">Status</div>
|
||||
<div className="font-semibold mt-1 capitalize">
|
||||
{currentRun.status === 'running' && <span className="text-blue-600 dark:text-blue-400">● {currentRun.status}</span>}
|
||||
{currentRun.status === 'paused' && <span className="text-yellow-600 dark:text-yellow-400">⏸ {currentRun.status}</span>}
|
||||
{currentRun.status === 'completed' && <span className="text-green-600 dark:text-green-400">✓ {currentRun.status}</span>}
|
||||
{currentRun.status === 'failed' && <span className="text-red-600 dark:text-red-400">✗ {currentRun.status}</span>}
|
||||
{currentRun.status === 'running' && <span className="text-brand-600 dark:text-brand-400">● {currentRun.status}</span>}
|
||||
{currentRun.status === 'paused' && <span className="text-warning-600 dark:text-warning-400">⏸ {currentRun.status}</span>}
|
||||
{currentRun.status === 'completed' && <span className="text-success-600 dark:text-success-400">✓ {currentRun.status}</span>}
|
||||
{currentRun.status === 'failed' && <span className="text-error-600 dark:text-error-400">✗ {currentRun.status}</span>}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -112,8 +112,8 @@ export default function Credits() {
|
||||
{ name: 'Multiple Formats', desc: 'Pages, articles, product content' },
|
||||
].map((feature) => (
|
||||
<div key={feature.name} className="flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="w-5 h-5 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<svg className="w-3 h-3 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div className="w-5 h-5 rounded-full bg-success-100 dark:bg-success-900 flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<svg className="w-3 h-3 text-success-600 dark:text-success-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -84,8 +84,8 @@ export default function Transactions() {
|
||||
</td>
|
||||
<td className={`py-3 px-4 text-sm font-medium ${
|
||||
transaction.amount >= 0
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
? 'text-success-600 dark:text-success-400'
|
||||
: 'text-error-600 dark:text-error-400'
|
||||
}`}>
|
||||
{transaction.amount >= 0 ? '+' : ''}{transaction.amount.toLocaleString()}
|
||||
</td>
|
||||
|
||||
@@ -150,8 +150,8 @@ export default function Usage() {
|
||||
</td>
|
||||
<td className={`py-3 px-4 text-sm font-medium ${
|
||||
txn.amount >= 0
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
? 'text-success-600 dark:text-success-400'
|
||||
: 'text-error-600 dark:text-error-400'
|
||||
}`}>
|
||||
{txn.amount >= 0 ? '+' : ''}{txn.amount}
|
||||
</td>
|
||||
|
||||
@@ -424,7 +424,7 @@ function DropdownsShowcase() {
|
||||
View
|
||||
</DropdownItem>
|
||||
<div className="my-2 border-t border-gray-200 dark:border-gray-800"></div>
|
||||
<DropdownItem onItemClick={() => setDropdown3(false)} className="flex items-center gap-3 px-3 py-2 font-medium text-red-600 rounded-lg text-theme-sm hover:bg-red-50 hover:text-red-700 dark:text-red-400 dark:hover:bg-red-900/20 dark:hover:text-red-300">
|
||||
<DropdownItem onItemClick={() => setDropdown3(false)} className="flex items-center gap-3 px-3 py-2 font-medium text-error-600 rounded-lg text-theme-sm hover:bg-error-50 hover:text-error-700 dark:text-error-400 dark:hover:bg-error-900/20 dark:hover:text-error-300">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
|
||||
@@ -311,7 +311,7 @@ export default function Help() {
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-green-500 text-white flex items-center justify-center font-bold">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-success-500 text-white flex items-center justify-center font-bold">
|
||||
7
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -332,9 +332,9 @@ export default function Help() {
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<Card className="p-4 border-l-4 border-blue-500">
|
||||
<Card className="p-4 border-l-4 border-brand-500">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2 flex items-center gap-2">
|
||||
<ListIcon className="size-5 text-blue-500" />
|
||||
<ListIcon className="size-5 text-brand-500" />
|
||||
Planner Module
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -342,9 +342,9 @@ export default function Help() {
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 border-l-4 border-green-500">
|
||||
<Card className="p-4 border-l-4 border-success-500">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2 flex items-center gap-2">
|
||||
<FileIcon className="size-5 text-green-500" />
|
||||
<FileIcon className="size-5 text-success-500" />
|
||||
Writer Module
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -353,8 +353,8 @@ export default function Help() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 dark:bg-blue-900/10 p-4 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-300">
|
||||
<div className="bg-brand-50 dark:bg-brand-900/10 p-4 rounded-lg border border-brand-200 dark:border-brand-800">
|
||||
<p className="text-sm text-brand-800 dark:text-brand-300">
|
||||
<strong>Tip:</strong> You can automate most of this workflow! Go to Dashboard > Automation Setup to configure automatic processing.
|
||||
</p>
|
||||
</div>
|
||||
@@ -393,7 +393,7 @@ export default function Help() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Setup Checklist</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Track your setup progress. Complete all items to start creating content efficiently.
|
||||
@@ -406,7 +406,7 @@ export default function Help() {
|
||||
{/* Setup Module Section */}
|
||||
<div ref={(el) => (sectionRefs.current["setup"] = el)} className="mb-12 scroll-mt-24">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<CheckCircleIcon className="size-8 text-green-600 dark:text-green-400" />
|
||||
<CheckCircleIcon className="size-8 text-success-600 dark:text-success-400" />
|
||||
Setup Module
|
||||
</h2>
|
||||
|
||||
@@ -430,8 +430,8 @@ export default function Help() {
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 dark:bg-blue-900/10 p-4 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-300">
|
||||
<div className="bg-brand-50 dark:bg-brand-900/10 p-4 rounded-lg border border-brand-200 dark:border-brand-800">
|
||||
<p className="text-sm text-brand-800 dark:text-brand-300">
|
||||
<strong>Tip:</strong> Start with 10-20 keywords to test your workflow. Mix high and low difficulty keywords for variety.
|
||||
</p>
|
||||
</div>
|
||||
@@ -445,7 +445,7 @@ export default function Help() {
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="border-l-4 border-blue-500 pl-4">
|
||||
<div className="border-l-4 border-brand-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Content Generation Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li><strong>Default Tone:</strong> Professional, Casual, Friendly, etc.</li>
|
||||
@@ -454,7 +454,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Publishing Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li><strong>Auto-Publish:</strong> Automatically publish approved content</li>
|
||||
@@ -493,7 +493,7 @@ export default function Help() {
|
||||
|
||||
<div className="space-y-3 mt-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircleIcon className="size-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<CheckCircleIcon className="size-5 text-success-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Site Dashboard</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -503,7 +503,7 @@ export default function Help() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircleIcon className="size-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<CheckCircleIcon className="size-5 text-success-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">WordPress Integration</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -520,7 +520,7 @@ export default function Help() {
|
||||
{/* Planner Module Section */}
|
||||
<div ref={(el) => (sectionRefs.current["planner-module"] = el)} className="mb-12 scroll-mt-24">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<ListIcon className="size-8 text-blue-600 dark:text-blue-400" />
|
||||
<ListIcon className="size-8 text-brand-600 dark:text-brand-400" />
|
||||
Planner Module
|
||||
</h2>
|
||||
|
||||
@@ -533,7 +533,7 @@ export default function Help() {
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircleIcon className="size-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<CheckCircleIcon className="size-5 text-success-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Adding Keywords</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -543,7 +543,7 @@ export default function Help() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircleIcon className="size-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<CheckCircleIcon className="size-5 text-success-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Organizing Keywords</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -553,7 +553,7 @@ export default function Help() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircleIcon className="size-5 text-green-500 mt-0.5 flex-shrink-0" />
|
||||
<CheckCircleIcon className="size-5 text-success-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Keyword Data</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
@@ -600,7 +600,7 @@ export default function Help() {
|
||||
{/* Writer Module Section */}
|
||||
<div ref={(el) => (sectionRefs.current["writer-module"] = el)} className="mb-12 scroll-mt-24">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<FileIcon className="size-8 text-green-600 dark:text-green-400" />
|
||||
<FileIcon className="size-8 text-success-600 dark:text-success-400" />
|
||||
Writer Module
|
||||
</h2>
|
||||
|
||||
@@ -684,7 +684,7 @@ export default function Help() {
|
||||
{/* Automation Section */}
|
||||
<div ref={(el) => (sectionRefs.current["automation"] = el)} className="mb-12 scroll-mt-24">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<BoltIcon className="size-8 text-orange-600 dark:text-orange-400" />
|
||||
<BoltIcon className="size-8 text-warning-600 dark:text-warning-400" />
|
||||
Automation Setup
|
||||
</h2>
|
||||
|
||||
@@ -694,7 +694,7 @@ export default function Help() {
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="border-l-4 border-blue-500 pl-4">
|
||||
<div className="border-l-4 border-brand-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Keywords Automation</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
Automatically add keywords from opportunities and cluster them:
|
||||
@@ -706,7 +706,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-orange-500 pl-4">
|
||||
<div className="border-l-4 border-warning-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Ideas Automation</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
Automatically generate content ideas from clusters:
|
||||
@@ -717,7 +717,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Content Automation</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
Automatically create tasks and generate content:
|
||||
@@ -751,7 +751,7 @@ export default function Help() {
|
||||
{/* Account & Billing Section */}
|
||||
<div ref={(el) => (sectionRefs.current["account"] = el)} className="mb-12 scroll-mt-24">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
|
||||
<GroupIcon className="size-8 text-indigo-600 dark:text-indigo-400" />
|
||||
<GroupIcon className="size-8 text-purple-600 dark:text-purple-400" />
|
||||
Account & Billing
|
||||
</h2>
|
||||
|
||||
@@ -763,7 +763,7 @@ export default function Help() {
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="border-l-4 border-blue-500 pl-4">
|
||||
<div className="border-l-4 border-brand-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Account Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>Organization name and billing email</li>
|
||||
@@ -772,7 +772,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Profile Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>Personal information (name, email, phone)</li>
|
||||
@@ -822,7 +822,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Upgrade Plan Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>Compare available plans</li>
|
||||
@@ -831,7 +831,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-orange-500 pl-4">
|
||||
<div className="border-l-4 border-warning-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">History Tab</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>Invoice history with PDF downloads</li>
|
||||
@@ -850,7 +850,7 @@ export default function Help() {
|
||||
</p>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="border-l-4 border-blue-500 pl-4">
|
||||
<div className="border-l-4 border-brand-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Your Limits & Usage</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li><strong>Hard Limits:</strong> Maximum allowed (sites, users, keywords)</li>
|
||||
@@ -868,7 +868,7 @@ export default function Help() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="border-l-4 border-green-500 pl-4">
|
||||
<div className="border-l-4 border-success-500 pl-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white mb-2">Activity</h4>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<li>Track operations by type</li>
|
||||
@@ -878,8 +878,8 @@ export default function Help() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-amber-50 dark:bg-amber-900/10 p-4 rounded-lg border border-amber-200 dark:border-amber-800 mt-4">
|
||||
<p className="text-sm text-amber-800 dark:text-amber-300">
|
||||
<div className="bg-warning-50 dark:bg-warning-900/10 p-4 rounded-lg border border-warning-200 dark:border-warning-800 mt-4">
|
||||
<p className="text-sm text-warning-800 dark:text-warning-300">
|
||||
<strong>Usage Alerts:</strong> You'll receive automatic alerts at 80%, 90%, and 100% of your limits.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function LinkerContentList() {
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500"></div>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading content...</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -157,7 +157,7 @@ export default function LinkerContentList() {
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
{item.cluster_name ? (
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-brand-100 text-brand-800 dark:bg-brand-900 dark:text-brand-200">
|
||||
{item.cluster_name}
|
||||
</span>
|
||||
) : (
|
||||
@@ -174,7 +174,7 @@ export default function LinkerContentList() {
|
||||
<button
|
||||
onClick={() => handleLink(item.id)}
|
||||
disabled={isProcessing || processing === -1}
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand-500 text-white rounded hover:bg-brand-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function LinkerDashboard() {
|
||||
/>
|
||||
<Link
|
||||
to="/linker/content"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-brand-500 text-white rounded-lg hover:bg-brand-600 transition-colors"
|
||||
>
|
||||
<PlugInIcon />
|
||||
View Content
|
||||
@@ -90,7 +90,7 @@ export default function LinkerDashboard() {
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500"></div>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading stats...</p>
|
||||
</div>
|
||||
) : stats ? (
|
||||
@@ -133,7 +133,7 @@ export default function LinkerDashboard() {
|
||||
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<PlugInIcon className="w-5 h-5 text-blue-500" />
|
||||
<PlugInIcon className="w-5 h-5 text-brand-500" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">Link Content</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Process content for internal linking</p>
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function AnalysisPreview() {
|
||||
|
||||
{loading || analyzing ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mb-3"></div>
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500 mb-3"></div>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{loading ? 'Loading content...' : 'Analyzing content...'}
|
||||
</p>
|
||||
@@ -126,15 +126,15 @@ export default function AnalysisPreview() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className={`p-4 rounded-lg border-2 ${
|
||||
scores.has_cluster_mapping
|
||||
? 'border-green-500 bg-green-50 dark:bg-green-900/20'
|
||||
: 'border-red-500 bg-red-50 dark:bg-red-900/20'
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-900/20'
|
||||
: 'border-error-500 bg-error-50 dark:bg-error-900/20'
|
||||
}`}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Cluster Mapping</span>
|
||||
{scores.has_cluster_mapping ? (
|
||||
<span className="text-green-600 dark:text-green-400">✓</span>
|
||||
<span className="text-success-600 dark:text-success-400">✓</span>
|
||||
) : (
|
||||
<span className="text-red-600 dark:text-red-400">✗</span>
|
||||
<span className="text-error-600 dark:text-error-400">✗</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -146,15 +146,15 @@ export default function AnalysisPreview() {
|
||||
|
||||
<div className={`p-4 rounded-lg border-2 ${
|
||||
scores.has_taxonomy_mapping
|
||||
? 'border-green-500 bg-green-50 dark:bg-green-900/20'
|
||||
: 'border-yellow-500 bg-yellow-50 dark:bg-yellow-900/20'
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-900/20'
|
||||
: 'border-warning-500 bg-warning-50 dark:bg-warning-900/20'
|
||||
}`}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Taxonomy Mapping</span>
|
||||
{scores.has_taxonomy_mapping ? (
|
||||
<span className="text-green-600 dark:text-green-400">✓</span>
|
||||
<span className="text-success-600 dark:text-success-400">✓</span>
|
||||
) : (
|
||||
<span className="text-yellow-600 dark:text-yellow-400">⚠</span>
|
||||
<span className="text-warning-600 dark:text-warning-400">⚠</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -166,15 +166,15 @@ export default function AnalysisPreview() {
|
||||
|
||||
<div className={`p-4 rounded-lg border-2 ${
|
||||
scores.has_attributes
|
||||
? 'border-green-500 bg-green-50 dark:bg-green-900/20'
|
||||
: 'border-yellow-500 bg-yellow-50 dark:bg-yellow-900/20'
|
||||
? 'border-success-500 bg-success-50 dark:bg-success-900/20'
|
||||
: 'border-warning-500 bg-warning-50 dark:bg-warning-900/20'
|
||||
}`}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">Attributes</span>
|
||||
{scores.has_attributes ? (
|
||||
<span className="text-green-600 dark:text-green-400">✓</span>
|
||||
<span className="text-success-600 dark:text-success-400">✓</span>
|
||||
) : (
|
||||
<span className="text-yellow-600 dark:text-yellow-400">⚠</span>
|
||||
<span className="text-warning-600 dark:text-warning-400">⚠</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -237,12 +237,12 @@ export default function AnalysisPreview() {
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Recommended Actions</h3>
|
||||
<div className="space-y-3">
|
||||
{!scores.has_cluster_mapping && (
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
|
||||
<div className="p-3 bg-brand-50 dark:bg-brand-900/20 border border-brand-200 dark:border-brand-800 rounded-lg">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-blue-600 dark:text-blue-400 font-bold">1.</span>
|
||||
<span className="text-brand-600 dark:text-brand-400 font-bold">1.</span>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-blue-900 dark:text-blue-300">Map Content to Cluster</div>
|
||||
<div className="text-sm text-blue-700 dark:text-blue-400 mt-1">
|
||||
<div className="font-medium text-brand-900 dark:text-brand-300">Map Content to Cluster</div>
|
||||
<div className="text-sm text-brand-700 dark:text-brand-400 mt-1">
|
||||
Assign this content to a keyword cluster to improve internal linking and SEO structure.
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,12 +263,12 @@ export default function AnalysisPreview() {
|
||||
</div>
|
||||
)}
|
||||
{!scores.has_attributes && (content?.content_type === 'product' || content?.content_type === 'service') && (
|
||||
<div className="p-3 bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-800 rounded-lg">
|
||||
<div className="p-3 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-800 rounded-lg">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-orange-600 dark:text-orange-400 font-bold">3.</span>
|
||||
<span className="text-warning-600 dark:text-warning-400 font-bold">3.</span>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-orange-900 dark:text-orange-300">Add Product/Service Attributes</div>
|
||||
<div className="text-sm text-orange-700 dark:text-orange-400 mt-1">
|
||||
<div className="font-medium text-warning-900 dark:text-warning-300">Add Product/Service Attributes</div>
|
||||
<div className="text-sm text-warning-700 dark:text-warning-400 mt-1">
|
||||
Add attributes like specifications, features, or modifiers to enhance content completeness.
|
||||
</div>
|
||||
</div>
|
||||
@@ -276,12 +276,12 @@ export default function AnalysisPreview() {
|
||||
</div>
|
||||
)}
|
||||
{scores.metadata_completeness_score !== undefined && scores.metadata_completeness_score < 80 && (
|
||||
<div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<div className="p-3 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-800 rounded-lg">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-amber-600 dark:text-amber-400 font-bold">4.</span>
|
||||
<span className="text-warning-600 dark:text-warning-400 font-bold">4.</span>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-amber-900 dark:text-amber-300">Improve Metadata Completeness</div>
|
||||
<div className="text-sm text-amber-700 dark:text-amber-400 mt-1">
|
||||
<div className="font-medium text-warning-900 dark:text-warning-300">Improve Metadata Completeness</div>
|
||||
<div className="text-sm text-warning-700 dark:text-warning-400 mt-1">
|
||||
Current score: {scores.metadata_completeness_score.toFixed(1)}%. Complete missing metadata fields to reach 80%+.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -168,7 +168,7 @@ export default function OptimizerContentSelector() {
|
||||
<button
|
||||
onClick={handleBatchOptimize}
|
||||
disabled={selectedIds.length === 0 || processing.length > 0}
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-brand-500 text-white rounded-lg hover:bg-brand-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{processing.length > 0 ? (
|
||||
<>
|
||||
@@ -193,7 +193,7 @@ export default function OptimizerContentSelector() {
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500"></div>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading content...</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -207,7 +207,7 @@ export default function OptimizerContentSelector() {
|
||||
type="checkbox"
|
||||
checked={selectedIds.length === filteredContent.length && filteredContent.length > 0}
|
||||
onChange={toggleSelectAll}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
className="rounded border-gray-300 text-brand-600 focus:ring-brand-500"
|
||||
/>
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
@@ -236,14 +236,14 @@ export default function OptimizerContentSelector() {
|
||||
return (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`hover:bg-gray-50 dark:hover:bg-gray-700 ${isSelected ? 'bg-blue-50 dark:bg-blue-900/20' : ''}`}
|
||||
className={`hover:bg-gray-50 dark:hover:bg-gray-700 ${isSelected ? 'bg-brand-50 dark:bg-brand-900/20' : ''}`}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={() => toggleSelection(item.id)}
|
||||
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
className="rounded border-gray-300 text-brand-600 focus:ring-brand-500"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
@@ -270,7 +270,7 @@ export default function OptimizerContentSelector() {
|
||||
<button
|
||||
onClick={() => handleOptimize(item.id)}
|
||||
disabled={isProcessing || processing.length > 0}
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 bg-brand-500 text-white rounded hover:bg-brand-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
|
||||
@@ -80,7 +80,7 @@ export default function OptimizerDashboard() {
|
||||
/>
|
||||
<Link
|
||||
to="/optimizer/content"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
|
||||
className="inline-flex items-center gap-2 px-4 py-2 bg-brand-500 text-white rounded-lg hover:bg-brand-600 transition-colors"
|
||||
>
|
||||
<BoltIcon />
|
||||
Optimize Content
|
||||
@@ -92,7 +92,7 @@ export default function OptimizerDashboard() {
|
||||
|
||||
{loading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500"></div>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading stats...</p>
|
||||
</div>
|
||||
) : stats ? (
|
||||
@@ -135,7 +135,7 @@ export default function OptimizerDashboard() {
|
||||
className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<BoltIcon className="w-5 h-5 text-yellow-500" />
|
||||
<BoltIcon className="w-5 h-5 text-warning-500" />
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900 dark:text-white">Optimize Content</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">Select and optimize content items</p>
|
||||
|
||||
@@ -52,68 +52,68 @@ export default function Payment() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-slate-50 px-4 py-16">
|
||||
<div className="w-full max-w-3xl bg-white rounded-2xl shadow-lg p-8 space-y-6 border border-slate-100">
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4 py-16">
|
||||
<div className="w-full max-w-3xl bg-white rounded-2xl shadow-lg p-8 space-y-6 border border-gray-100">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-slate-500">Confirm your plan</p>
|
||||
<h1 className="text-2xl font-semibold text-slate-900">Complete your subscription</h1>
|
||||
<p className="text-sm text-gray-500">Confirm your plan</p>
|
||||
<h1 className="text-2xl font-semibold text-gray-900">Complete your subscription</h1>
|
||||
</div>
|
||||
<Link to="/pricing" className="text-sm text-blue-600 hover:text-blue-700">
|
||||
<Link to="/pricing" className="text-sm text-brand-600 hover:text-brand-700">
|
||||
Change plan
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{plan && (
|
||||
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4">
|
||||
<h2 className="text-lg font-semibold text-slate-900">{plan.name}</h2>
|
||||
<p className="text-slate-700">{plan.price}</p>
|
||||
<p className="text-sm text-slate-600">{plan.content}</p>
|
||||
<p className="text-xs text-amber-700 mt-2">
|
||||
<div className="rounded-xl border border-gray-200 bg-gray-50 p-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">{plan.name}</h2>
|
||||
<p className="text-gray-700">{plan.price}</p>
|
||||
<p className="text-sm text-gray-600">{plan.content}</p>
|
||||
<p className="text-xs text-warning-700 mt-2">
|
||||
Payment is completed offline (bank transfer). Submit your email and reference below; we will verify and activate your account.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-medium text-slate-800">
|
||||
<label className="block text-sm font-medium text-gray-800">
|
||||
Contact email
|
||||
<input
|
||||
type="email"
|
||||
value={contactEmail}
|
||||
onChange={(e) => setContactEmail(e.target.value)}
|
||||
placeholder="you@example.com"
|
||||
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none"
|
||||
className="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-900 focus:border-brand-500 focus:outline-none"
|
||||
/>
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-slate-800">
|
||||
<label className="block text-sm font-medium text-gray-800">
|
||||
Notes (optional)
|
||||
<textarea
|
||||
value={note}
|
||||
onChange={(e) => setNote(e.target.value)}
|
||||
placeholder="Company name, billing contact, or questions"
|
||||
className="mt-1 w-full rounded-lg border border-slate-300 px-3 py-2 text-slate-900 focus:border-blue-500 focus:outline-none"
|
||||
className="mt-1 w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-900 focus:border-brand-500 focus:outline-none"
|
||||
rows={3}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Link to="/signup" className="text-sm text-slate-600 hover:text-slate-800">
|
||||
<Link to="/signup" className="text-sm text-gray-600 hover:text-gray-800">
|
||||
Prefer the free plan? Start your trial
|
||||
</Link>
|
||||
<a
|
||||
href={mailtoHref || "#"}
|
||||
onClick={handleRequest}
|
||||
className={`inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-semibold text-white ${
|
||||
contactEmail.trim() ? "bg-blue-600 hover:bg-blue-700" : "bg-blue-400 cursor-not-allowed"
|
||||
contactEmail.trim() ? "bg-brand-600 hover:bg-brand-700" : "bg-brand-400 cursor-not-allowed"
|
||||
}`}
|
||||
aria-disabled={!contactEmail.trim()}
|
||||
>
|
||||
Request payment instructions
|
||||
</a>
|
||||
</div>
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
{error && <p className="text-sm text-error-600">{error}</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -235,7 +235,7 @@ export default function PlannerDashboard() {
|
||||
description: `${stats?.clusters.total || 0} new clusters created`,
|
||||
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
||||
icon: GroupIcon,
|
||||
color: "text-green-600",
|
||||
color: "text-success-600",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -243,7 +243,7 @@ export default function PlannerDashboard() {
|
||||
description: `${stats?.ideas.total || 0} content ideas created`,
|
||||
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000),
|
||||
icon: BoltIcon,
|
||||
color: "text-orange-600",
|
||||
color: "text-warning-600",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -251,7 +251,7 @@ export default function PlannerDashboard() {
|
||||
description: `${stats?.keywords.total || 0} keywords in database`,
|
||||
timestamp: new Date(Date.now() - 6 * 60 * 60 * 1000),
|
||||
icon: ListIcon,
|
||||
color: "text-blue-600",
|
||||
color: "text-brand-600",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -268,18 +268,18 @@ export default function PlannerDashboard() {
|
||||
},
|
||||
xaxis: {
|
||||
categories: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
labels: { style: { colors: "#6b7280" } },
|
||||
labels: { style: { colors: "var(--color-gray-500)" } },
|
||||
},
|
||||
yaxis: {
|
||||
labels: { style: { colors: "#6b7280" } },
|
||||
labels: { style: { colors: "var(--color-gray-500)" } },
|
||||
},
|
||||
legend: {
|
||||
position: "top",
|
||||
labels: { colors: "#6b7280" },
|
||||
labels: { colors: "var(--color-gray-500)" },
|
||||
},
|
||||
colors: ["var(--color-primary)", "var(--color-success)", "var(--color-warning)"],
|
||||
grid: {
|
||||
borderColor: "#e5e7eb",
|
||||
borderColor: "var(--color-gray-200)",
|
||||
},
|
||||
fill: {
|
||||
type: "gradient",
|
||||
@@ -315,7 +315,7 @@ export default function PlannerDashboard() {
|
||||
toolbar: { show: false }
|
||||
},
|
||||
labels: Object.keys(stats.keywords.byStatus).filter(key => stats.keywords.byStatus[key] > 0),
|
||||
colors: ['#465FFF', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6'],
|
||||
colors: ['var(--color-primary)', 'var(--color-success)', 'var(--color-warning)', 'var(--color-danger)', 'var(--color-purple)'],
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
fontFamily: 'Outfit',
|
||||
@@ -335,7 +335,7 @@ export default function PlannerDashboard() {
|
||||
show: true,
|
||||
fontSize: '24px',
|
||||
fontWeight: 700,
|
||||
color: '#465FFF',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'Outfit',
|
||||
formatter: () => {
|
||||
const total = Object.values(stats.keywords.byStatus).reduce((a, b) => a + b, 0);
|
||||
@@ -366,7 +366,7 @@ export default function PlannerDashboard() {
|
||||
toolbar: { show: false },
|
||||
height: 300
|
||||
},
|
||||
colors: ['#10B981'],
|
||||
colors: ['var(--color-success)'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
@@ -510,21 +510,21 @@ export default function PlannerDashboard() {
|
||||
<Link
|
||||
key={module.title}
|
||||
to={module.path}
|
||||
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
className="rounded-2xl border-2 border-gray-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||
<Icon className="h-7 w-7" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">{module.description}</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-slate-900">{module.count}</div>
|
||||
<div className="text-xs text-slate-500">{module.metric}</div>
|
||||
<div className="text-2xl font-bold text-gray-900">{module.count}</div>
|
||||
<div className="text-xs text-gray-500">{module.metric}</div>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
@@ -547,17 +547,17 @@ export default function PlannerDashboard() {
|
||||
return (
|
||||
<div
|
||||
key={activity.id}
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-slate-200 bg-white hover:shadow-md transition"
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-gray-200 bg-white hover:shadow-md transition"
|
||||
>
|
||||
<div className={`size-10 rounded-lg bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center ${activity.color}`}>
|
||||
<div className={`size-10 rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center ${activity.color}`}>
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h4 className="font-semibold text-slate-900">{activity.type}</h4>
|
||||
<span className="text-xs text-slate-500">{formatTimeAgo(activity.timestamp)}</span>
|
||||
<h4 className="font-semibold text-gray-900">{activity.type}</h4>
|
||||
<span className="text-xs text-gray-500">{formatTimeAgo(activity.timestamp)}</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-600">{activity.description}</p>
|
||||
<p className="text-sm text-gray-600">{activity.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -638,58 +638,58 @@ export default function PlannerDashboard() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<Link
|
||||
to="/planner/keyword-opportunities"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<ListIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Add Keywords</h4>
|
||||
<p className="text-sm text-slate-600">Discover opportunities</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Add Keywords</h4>
|
||||
<p className="text-sm text-gray-600">Discover opportunities</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/planner/clusters"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#0bbf87] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-success-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-success)] to-[var(--color-success-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<GroupIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Auto Cluster</h4>
|
||||
<p className="text-sm text-slate-600">Group keywords</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Auto Cluster</h4>
|
||||
<p className="text-sm text-gray-600">Group keywords</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0bbf87] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-success-500 transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/planner/ideas"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#ff7a00] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-warning-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-warning)] to-[var(--color-warning-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<BoltIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Generate Ideas</h4>
|
||||
<p className="text-sm text-slate-600">Create content ideas</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Generate Ideas</h4>
|
||||
<p className="text-sm text-gray-600">Create content ideas</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#ff7a00] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-warning-500 transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/automation"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#5d4ae3] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-purple-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-purple)] to-[var(--color-purple-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<PlugInIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Setup Automation</h4>
|
||||
<p className="text-sm text-slate-600">Automate workflows</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Setup Automation</h4>
|
||||
<p className="text-sm text-gray-600">Automate workflows</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#5d4ae3] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-purple-500 transition" />
|
||||
</Link>
|
||||
</div>
|
||||
</ComponentCard>
|
||||
@@ -703,8 +703,8 @@ export default function PlannerDashboard() {
|
||||
<ListIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Keyword Discovery</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Keyword Discovery</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Discover high-volume keywords from our global database. Add keywords manually or import from keyword opportunities.
|
||||
</p>
|
||||
</div>
|
||||
@@ -714,8 +714,8 @@ export default function PlannerDashboard() {
|
||||
<GroupIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">AI Clustering</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">AI Clustering</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Automatically group related keywords into strategic clusters. Each cluster represents a content topic with shared search intent.
|
||||
</p>
|
||||
</div>
|
||||
@@ -725,8 +725,8 @@ export default function PlannerDashboard() {
|
||||
<BoltIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Idea Generation</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Idea Generation</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Generate content ideas from clusters using AI. Each idea includes title, outline, and target keywords for content creation.
|
||||
</p>
|
||||
</div>
|
||||
@@ -737,34 +737,34 @@ export default function PlannerDashboard() {
|
||||
<ComponentCard title="Getting Started" desc="Quick guide to using Planner">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0693e3] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-brand-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Add Keywords</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Add Keywords</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Start by adding keywords from the keyword opportunities page. You can search by volume, difficulty, or intent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0bbf87] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-success-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Cluster Keywords</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Cluster Keywords</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Use the auto-cluster feature to group related keywords. Review and refine clusters to match your content strategy.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#ff7a00] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-warning-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Generate Ideas</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Generate Ideas</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Create content ideas from your clusters. Queue ideas to the Writer module to start content creation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -168,12 +168,12 @@ export default function Industries() {
|
||||
{industry.topKeywords.slice(0, 5).map((keyword, idx) => (
|
||||
<div
|
||||
key={keyword.id || idx}
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800"
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-brand-50 dark:bg-brand-900/20 border border-brand-200 dark:border-brand-800"
|
||||
>
|
||||
<span className="text-xs font-medium text-blue-700 dark:text-blue-300">
|
||||
<span className="text-xs font-medium text-brand-700 dark:text-brand-300">
|
||||
{keyword.keyword}
|
||||
</span>
|
||||
<span className="text-xs text-blue-600 dark:text-blue-400 font-semibold">
|
||||
<span className="text-xs text-brand-600 dark:text-brand-400 font-semibold">
|
||||
{keyword.volume ? (keyword.volume >= 1000 ? `${(keyword.volume / 1000).toFixed(1)}k` : keyword.volume.toString()) : '-'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +173,7 @@ const CreditsAndBilling: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className={`font-bold ${transaction.amount > 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||||
<div className={`font-bold ${transaction.amount > 0 ? 'text-success-600' : 'text-error-600'}`}>
|
||||
{transaction.amount > 0 ? '+' : ''}{transaction.amount}
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ const CreditsAndBilling: React.FC = () => {
|
||||
{transaction.reference_id || '-'}
|
||||
</td>
|
||||
<td className={`px-6 py-4 whitespace-nowrap text-sm text-right font-bold ${
|
||||
transaction.amount > 0 ? 'text-green-600' : 'text-red-600'
|
||||
transaction.amount > 0 ? 'text-success-600' : 'text-error-600'
|
||||
}`}>
|
||||
{transaction.amount > 0 ? '+' : ''}{transaction.amount}
|
||||
</td>
|
||||
|
||||
@@ -718,7 +718,7 @@ export default function Integration() {
|
||||
</Label>
|
||||
|
||||
{/* Featured Image (full width) - Selectable */}
|
||||
<div className="p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-500 to-blue-500 text-white">
|
||||
<div className="p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-500 to-brand-500 text-white">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="font-medium">Featured Image</div>
|
||||
<div className="text-xs bg-white/20 px-2 py-1 rounded">
|
||||
|
||||
@@ -148,8 +148,8 @@ export default function Publishing() {
|
||||
</div>
|
||||
|
||||
{autoPublishEnabled && (
|
||||
<div className="mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-200">
|
||||
<div className="mt-4 p-4 bg-brand-50 dark:bg-brand-900/20 border border-brand-200 dark:border-brand-800 rounded-lg">
|
||||
<p className="text-sm text-brand-800 dark:text-brand-200">
|
||||
When you turn this on, articles will publish to your site right away. You can still review them first if you want
|
||||
</p>
|
||||
</div>
|
||||
@@ -207,8 +207,8 @@ export default function Publishing() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p className="text-sm text-amber-800 dark:text-amber-200">
|
||||
<div className="p-4 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-800 rounded-lg">
|
||||
<p className="text-sm text-warning-800 dark:text-warning-200">
|
||||
<strong>Note:</strong> Content will be automatically published every{' '}
|
||||
{syncInterval} {syncIntervalUnit} if it has status "review" and all images are generated.
|
||||
</p>
|
||||
|
||||
@@ -377,7 +377,7 @@ export default function Sites() {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
|
||||
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-brand-600 mx-auto"></div>
|
||||
<p className="text-gray-600 dark:text-gray-400">Loading sites...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -522,7 +522,7 @@ export default function Sites() {
|
||||
setSelectedSectors(selectedSectors.filter(s => s !== sector.slug));
|
||||
}
|
||||
}}
|
||||
className="mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
className="mt-1 h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-500"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-sm text-gray-900 dark:text-white">
|
||||
|
||||
@@ -40,18 +40,18 @@ interface SystemStatus {
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'healthy': return 'text-green-600 dark:text-green-400';
|
||||
case 'warning': return 'text-yellow-600 dark:text-yellow-400';
|
||||
case 'critical': return 'text-red-600 dark:text-red-400';
|
||||
case 'healthy': return 'text-success-600 dark:text-success-400';
|
||||
case 'warning': return 'text-warning-600 dark:text-warning-400';
|
||||
case 'critical': return 'text-error-600 dark:text-error-400';
|
||||
default: return 'text-gray-600 dark:text-gray-400';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case 'healthy': return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400';
|
||||
case 'warning': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400';
|
||||
case 'critical': return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400';
|
||||
case 'healthy': return 'bg-success-100 text-success-800 dark:bg-success-900/30 dark:text-success-400';
|
||||
case 'warning': return 'bg-warning-100 text-warning-800 dark:bg-warning-900/30 dark:text-warning-400';
|
||||
case 'critical': return 'bg-error-100 text-error-800 dark:bg-error-900/30 dark:text-error-400';
|
||||
default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400';
|
||||
}
|
||||
};
|
||||
@@ -99,7 +99,7 @@ export default function Status() {
|
||||
<>
|
||||
<PageMeta title="System Status - IGNY8" description="System monitoring" />
|
||||
<ComponentCard title="System Status" desc="Error loading system information">
|
||||
<div className="text-center py-8 text-red-600 dark:text-red-400">
|
||||
<div className="text-center py-8 text-error-600 dark:text-error-400">
|
||||
{error || 'Failed to load system status'}
|
||||
</div>
|
||||
</ComponentCard>
|
||||
@@ -126,8 +126,8 @@ export default function Status() {
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-4">
|
||||
<div
|
||||
className={`h-4 rounded-full ${
|
||||
(status.system?.cpu?.usage_percent || 0) < 80 ? 'bg-green-500' :
|
||||
(status.system?.cpu?.usage_percent || 0) < 95 ? 'bg-yellow-500' : 'bg-red-500'
|
||||
(status.system?.cpu?.usage_percent || 0) < 80 ? 'bg-success-500' :
|
||||
(status.system?.cpu?.usage_percent || 0) < 95 ? 'bg-warning-500' : 'bg-error-500'
|
||||
}`}
|
||||
style={{ width: `${status.system?.cpu?.usage_percent || 0}%` }}
|
||||
></div>
|
||||
@@ -148,8 +148,8 @@ export default function Status() {
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-4">
|
||||
<div
|
||||
className={`h-4 rounded-full ${
|
||||
(status.system?.memory?.usage_percent || 0) < 80 ? 'bg-green-500' :
|
||||
(status.system?.memory?.usage_percent || 0) < 95 ? 'bg-yellow-500' : 'bg-red-500'
|
||||
(status.system?.memory?.usage_percent || 0) < 80 ? 'bg-success-500' :
|
||||
(status.system?.memory?.usage_percent || 0) < 95 ? 'bg-warning-500' : 'bg-error-500'
|
||||
}`}
|
||||
style={{ width: `${status.system?.memory?.usage_percent || 0}%` }}
|
||||
></div>
|
||||
@@ -170,8 +170,8 @@ export default function Status() {
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-4">
|
||||
<div
|
||||
className={`h-4 rounded-full ${
|
||||
(status.system?.disk?.usage_percent || 0) < 80 ? 'bg-green-500' :
|
||||
(status.system?.disk?.usage_percent || 0) < 95 ? 'bg-yellow-500' : 'bg-red-500'
|
||||
(status.system?.disk?.usage_percent || 0) < 80 ? 'bg-success-500' :
|
||||
(status.system?.disk?.usage_percent || 0) < 95 ? 'bg-warning-500' : 'bg-error-500'
|
||||
}`}
|
||||
style={{ width: `${status.system?.disk?.usage_percent || 0}%` }}
|
||||
></div>
|
||||
|
||||
@@ -343,7 +343,7 @@ export default function WordPressIntegrationDebug() {
|
||||
if (initializing) {
|
||||
return (
|
||||
<div className="p-8 text-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-blue-600 dark:text-blue-400" />
|
||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-brand-600 dark:text-brand-400" />
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-2">Loading WordPress integration...</p>
|
||||
</div>
|
||||
);
|
||||
@@ -353,8 +353,8 @@ export default function WordPressIntegrationDebug() {
|
||||
if (!integrationId) {
|
||||
return (
|
||||
<div className="p-8">
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg p-6 text-center">
|
||||
<AlertTriangle className="h-12 w-12 text-yellow-600 dark:text-yellow-400 mx-auto mb-4" />
|
||||
<div className="bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-900/50 rounded-lg p-6 text-center">
|
||||
<AlertTriangle className="h-12 w-12 text-warning-600 dark:text-warning-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||
No WordPress Integration Found
|
||||
</h3>
|
||||
@@ -427,15 +427,15 @@ export default function WordPressIntegrationDebug() {
|
||||
<p className="text-gray-600 dark:text-gray-400">Checking for WordPress integration...</p>
|
||||
</div>
|
||||
) : !integrationId && activeSite ? (
|
||||
<div className="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg p-6">
|
||||
<div className="bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-900/50 rounded-lg p-6">
|
||||
<div className="flex items-start space-x-3">
|
||||
<AlertTriangle className="h-8 w-8 text-yellow-500 mt-0.5" />
|
||||
<AlertTriangle className="h-8 w-8 text-warning-500 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-yellow-800 dark:text-yellow-200">No WordPress Integration Found</p>
|
||||
<p className="text-sm text-yellow-600 dark:text-yellow-300 mt-2">
|
||||
<p className="text-lg font-semibold text-warning-800 dark:text-warning-200">No WordPress Integration Found</p>
|
||||
<p className="text-sm text-warning-600 dark:text-warning-300 mt-2">
|
||||
This site doesn't have a WordPress integration configured yet.
|
||||
</p>
|
||||
<p className="text-xs text-yellow-600 dark:text-yellow-400 mt-2">
|
||||
<p className="text-xs text-warning-600 dark:text-warning-400 mt-2">
|
||||
Please configure a WordPress integration in <span className="font-medium">Settings → Integration</span> page first.
|
||||
</p>
|
||||
</div>
|
||||
@@ -461,7 +461,7 @@ export default function WordPressIntegrationDebug() {
|
||||
<button
|
||||
onClick={testConnection}
|
||||
disabled={loading}
|
||||
className="inline-flex items-center px-3 py-1 text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 rounded-md disabled:opacity-50"
|
||||
className="inline-flex items-center px-3 py-1 text-xs bg-brand-100 hover:bg-brand-200 text-brand-700 rounded-md disabled:opacity-50"
|
||||
>
|
||||
<TestTube className="h-3 w-3 mr-1" />
|
||||
Test Connection
|
||||
@@ -469,7 +469,7 @@ export default function WordPressIntegrationDebug() {
|
||||
<button
|
||||
onClick={resyncSiteMetadata}
|
||||
disabled={loading}
|
||||
className="inline-flex items-center px-3 py-1 text-xs bg-yellow-100 hover:bg-yellow-200 text-yellow-700 rounded-md disabled:opacity-50"
|
||||
className="inline-flex items-center px-3 py-1 text-xs bg-warning-100 hover:bg-warning-200 text-warning-700 rounded-md disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className="h-3 w-3 mr-1" />
|
||||
Re-sync Metadata
|
||||
@@ -491,9 +491,9 @@ export default function WordPressIntegrationDebug() {
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium">API Connection</span>
|
||||
{integrationHealth.api_status === 'healthy' ? (
|
||||
<CheckCircle className="h-4 w-4 text-green-500" />
|
||||
<CheckCircle className="h-4 w-4 text-success-500" />
|
||||
) : (
|
||||
<XCircle className="h-4 w-4 text-red-500" />
|
||||
<XCircle className="h-4 w-4 text-error-500" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">{integrationHealth.api_message}</p>
|
||||
@@ -506,9 +506,9 @@ export default function WordPressIntegrationDebug() {
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium">Plugin Status</span>
|
||||
{integrationHealth.plugin_active ? (
|
||||
<CheckCircle className="h-4 w-4 text-green-500" />
|
||||
<CheckCircle className="h-4 w-4 text-success-500" />
|
||||
) : (
|
||||
<XCircle className="h-4 w-4 text-red-500" />
|
||||
<XCircle className="h-4 w-4 text-error-500" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -523,9 +523,9 @@ export default function WordPressIntegrationDebug() {
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium">Sync Status</span>
|
||||
{integrationHealth.sync_healthy ? (
|
||||
<CheckCircle className="h-4 w-4 text-green-500" />
|
||||
<CheckCircle className="h-4 w-4 text-success-500" />
|
||||
) : (
|
||||
<AlertTriangle className="h-4 w-4 text-yellow-500" />
|
||||
<AlertTriangle className="h-4 w-4 text-warning-500" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||
@@ -557,9 +557,9 @@ export default function WordPressIntegrationDebug() {
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center space-x-2 text-sm">
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${
|
||||
event.type === 'sync' ? 'bg-blue-100 text-blue-700' :
|
||||
event.type === 'error' ? 'bg-red-100 text-red-700' :
|
||||
event.type === 'webhook' ? 'bg-green-100 text-green-700' :
|
||||
event.type === 'sync' ? 'bg-brand-100 text-brand-700' :
|
||||
event.type === 'error' ? 'bg-error-100 text-error-700' :
|
||||
event.type === 'webhook' ? 'bg-success-100 text-success-700' :
|
||||
'bg-gray-100 text-gray-700'
|
||||
}`}>
|
||||
{event.type}
|
||||
@@ -569,7 +569,7 @@ export default function WordPressIntegrationDebug() {
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{event.description}</p>
|
||||
{event.details && (
|
||||
<details className="mt-2">
|
||||
<summary className="text-xs text-blue-600 cursor-pointer hover:underline">
|
||||
<summary className="text-xs text-brand-600 cursor-pointer hover:underline">
|
||||
View Details
|
||||
</summary>
|
||||
<pre className="mt-2 text-xs bg-gray-50 dark:bg-gray-700 p-2 rounded border overflow-x-auto">
|
||||
@@ -631,12 +631,12 @@ export default function WordPressIntegrationDebug() {
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
{validation.matches ? (
|
||||
<CheckCircle className="h-4 w-4 text-green-500" />
|
||||
<CheckCircle className="h-4 w-4 text-success-500" />
|
||||
) : (
|
||||
<div className="flex items-center space-x-1">
|
||||
<XCircle className="h-4 w-4 text-red-500" />
|
||||
<XCircle className="h-4 w-4 text-error-500" />
|
||||
{validation.error && (
|
||||
<span className="text-xs text-red-600">{validation.error}</span>
|
||||
<span className="text-xs text-error-600">{validation.error}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -684,18 +684,18 @@ export default function IndustriesSectorsKeywords() {
|
||||
{/* Show info banner when no sector is selected */}
|
||||
{!activeSector && activeSite && (
|
||||
<div className="mx-6 mt-6 mb-4">
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
||||
<div className="bg-brand-50 dark:bg-brand-900/20 border border-brand-200 dark:border-brand-800 rounded-lg p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<svg className="w-5 h-5 text-blue-600 dark:text-blue-400 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<svg className="w-5 h-5 text-brand-600 dark:text-brand-400 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-sm font-medium text-blue-900 dark:text-blue-200">
|
||||
<h3 className="text-sm font-medium text-brand-900 dark:text-brand-200">
|
||||
Choose a Topic Area First
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-blue-700 dark:text-blue-300">
|
||||
<p className="mt-1 text-sm text-brand-700 dark:text-brand-300">
|
||||
Pick a topic area first, then add keywords - You need to choose what you're writing about before adding search terms to target
|
||||
</p>
|
||||
</div>
|
||||
@@ -719,11 +719,11 @@ export default function IndustriesSectorsKeywords() {
|
||||
customActions={activeSite && activeSector ? (
|
||||
<div className="flex items-center gap-3 px-3 py-1.5 bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||
<span className="text-green-600 dark:text-green-400 font-bold">{addedCount}</span> in workflow
|
||||
<span className="text-success-600 dark:text-success-400 font-bold">{addedCount}</span> in workflow
|
||||
</span>
|
||||
<div className="w-px h-4 bg-gray-300 dark:bg-gray-600" />
|
||||
<span className="text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||
<span className="text-blue-600 dark:text-blue-400 font-bold">{availableCount}</span> available
|
||||
<span className="text-brand-600 dark:text-brand-400 font-bold">{availableCount}</span> available
|
||||
</span>
|
||||
{addedCount > 0 && (
|
||||
<>
|
||||
|
||||
@@ -228,7 +228,7 @@ export default function SiteContentManager() {
|
||||
{item.title || `Content #${item.id}`}
|
||||
</h3>
|
||||
<div className="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-500">
|
||||
<span className={`px-2 py-1 rounded ${item.status === 'published' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'}`}>
|
||||
<span className={`px-2 py-1 rounded ${item.status === 'published' ? 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'}`}>
|
||||
{item.status}
|
||||
</span>
|
||||
{item.content_type && <span>{item.content_type}</span>}
|
||||
|
||||
@@ -260,72 +260,72 @@ export default function SiteDashboard() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/pages`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<PageIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Manage Pages</h4>
|
||||
<p className="text-sm text-slate-600">View and edit pages</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Manage Pages</h4>
|
||||
<p className="text-sm text-gray-600">View and edit pages</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/content`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-success)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-success)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-success)] to-[var(--color-success-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<FileIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Manage Content</h4>
|
||||
<p className="text-sm text-slate-600">View and edit content</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Manage Content</h4>
|
||||
<p className="text-sm text-gray-600">View and edit content</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-success)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-success)] transition" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/settings?tab=integrations`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-purple)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-purple)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-purple)] to-[var(--color-purple-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<PlugInIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Integrations</h4>
|
||||
<p className="text-sm text-slate-600">Manage connections</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Integrations</h4>
|
||||
<p className="text-sm text-gray-600">Manage connections</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-purple)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-purple)] transition" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/sync`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-warning)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-warning)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-warning)] to-[var(--color-warning-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<BoltIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Sync Dashboard</h4>
|
||||
<p className="text-sm text-slate-600">View sync status</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Sync Dashboard</h4>
|
||||
<p className="text-sm text-gray-600">View sync status</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-warning)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-warning)] transition" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate(`/sites/${siteId}/deploy`)}
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<ArrowUpIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Deploy Site</h4>
|
||||
<p className="text-sm text-slate-600">Deploy to production</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Deploy Site</h4>
|
||||
<p className="text-sm text-gray-600">Deploy to production</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</button>
|
||||
</div>
|
||||
</ComponentCard>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function DeploymentPanel() {
|
||||
/>
|
||||
|
||||
<Card className="p-8 text-center">
|
||||
<AlertIcon className="w-16 h-16 text-amber-500 mx-auto mb-4" />
|
||||
<AlertIcon className="w-16 h-16 text-warning-500 mx-auto mb-4" />
|
||||
<h2 className="text-2xl font-bold mb-2">Feature Deprecated</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
The SiteBlueprint deployment system has been removed.
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function Editor() {
|
||||
/>
|
||||
|
||||
<Card className="p-8 text-center">
|
||||
<AlertIcon className="w-16 h-16 text-amber-500 mx-auto mb-4" />
|
||||
<AlertIcon className="w-16 h-16 text-warning-500 mx-auto mb-4" />
|
||||
<h2 className="text-2xl font-bold mb-2">Feature Deprecated</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
The SiteBlueprint page editor has been removed.
|
||||
|
||||
@@ -388,7 +388,7 @@ export default function SiteList() {
|
||||
const renderGridView = () => (
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{filteredSites.map((site) => (
|
||||
<Card key={site.id} className="rounded-xl border-2 border-slate-200 bg-white dark:border-gray-800 dark:bg-white/3 hover:border-[var(--color-primary)] hover:shadow-lg transition-all">
|
||||
<Card key={site.id} className="rounded-xl border-2 border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3 hover:border-[var(--color-primary)] hover:shadow-lg transition-all">
|
||||
<div className="relative p-4 pb-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="size-6 rounded-lg bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-md flex-shrink-0">
|
||||
@@ -611,12 +611,12 @@ export default function SiteList() {
|
||||
placeholder="Search sites..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="flex-1 min-w-[200px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="flex-1 min-w-[200px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
|
||||
/>
|
||||
<select
|
||||
value={siteTypeFilter}
|
||||
onChange={(e) => setSiteTypeFilter(e.target.value)}
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
|
||||
>
|
||||
{SITE_TYPES.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||
@@ -625,7 +625,7 @@ export default function SiteList() {
|
||||
<select
|
||||
value={hostingTypeFilter}
|
||||
onChange={(e) => setHostingTypeFilter(e.target.value)}
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
|
||||
>
|
||||
{HOSTING_TYPES.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||
@@ -634,7 +634,7 @@ export default function SiteList() {
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
className="flex-1 min-w-[140px] h-9 px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
|
||||
>
|
||||
{STATUS_OPTIONS.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||
|
||||
@@ -67,7 +67,7 @@ const DraggablePageItem: React.FC<{
|
||||
isDragging
|
||||
? 'opacity-50 border-brand-500 bg-brand-50 dark:bg-brand-900/20'
|
||||
: 'border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800'
|
||||
} ${isSelected ? 'bg-blue-50 dark:bg-blue-900/20 border-blue-300 dark:border-blue-700' : ''}`}
|
||||
} ${isSelected ? 'bg-brand-50 dark:bg-brand-900/20 border-brand-300 dark:border-brand-700' : ''}`}
|
||||
>
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<button
|
||||
@@ -304,7 +304,7 @@ export default function PageManager() {
|
||||
<>
|
||||
{/* Bulk Actions Bar */}
|
||||
{selectedPages.size > 0 && (
|
||||
<Card className="p-4 mb-4 bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800">
|
||||
<Card className="p-4 mb-4 bg-brand-50 dark:bg-brand-900/20 border-brand-200 dark:border-brand-800">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{selectedPages.size} page(s) selected
|
||||
@@ -328,7 +328,7 @@ export default function PageManager() {
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleBulkDelete}
|
||||
className="text-red-600 hover:text-red-700"
|
||||
className="text-error-600 hover:text-error-700"
|
||||
>
|
||||
<TrashBinIcon className="w-4 h-4 mr-1" />
|
||||
Delete Selected
|
||||
@@ -343,7 +343,7 @@ export default function PageManager() {
|
||||
|
||||
{/* Reorder Save Button */}
|
||||
{isReordering && (
|
||||
<Card className="p-4 mb-4 bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800">
|
||||
<Card className="p-4 mb-4 bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Page order changed. Save to apply changes.
|
||||
|
||||
@@ -306,7 +306,7 @@ export default function PostEditor() {
|
||||
<CheckCircleIcon className="w-4 h-4 inline mr-2" />
|
||||
Validation
|
||||
{validationResult && !validationResult.is_valid && (
|
||||
<span className="ml-2 px-2 py-0.5 text-xs bg-red-100 dark:bg-red-900 text-red-600 dark:text-red-400 rounded-full">
|
||||
<span className="ml-2 px-2 py-0.5 text-xs bg-error-100 dark:bg-error-900 text-error-600 dark:text-error-400 rounded-full">
|
||||
{validationResult.validation_errors.length}
|
||||
</span>
|
||||
)}
|
||||
@@ -395,7 +395,7 @@ export default function PostEditor() {
|
||||
{content.taxonomy_terms.map((term) => (
|
||||
<span
|
||||
key={term.id}
|
||||
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800 dark:bg-blue-500/20 dark:text-blue-300"
|
||||
className="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-brand-100 text-brand-800 dark:bg-brand-500/20 dark:text-brand-300"
|
||||
>
|
||||
{term.name} ({term.taxonomy})
|
||||
</span>
|
||||
@@ -456,19 +456,19 @@ export default function PostEditor() {
|
||||
{/* Validation Status */}
|
||||
<div className={`p-4 rounded-lg ${
|
||||
validationResult.is_valid
|
||||
? 'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800'
|
||||
: 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800'
|
||||
? 'bg-success-50 dark:bg-success-900/20 border border-success-200 dark:border-success-800'
|
||||
: 'bg-error-50 dark:bg-error-900/20 border border-error-200 dark:border-error-800'
|
||||
}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
{validationResult.is_valid ? (
|
||||
<CheckCircleIcon className="w-5 h-5 text-green-600 dark:text-green-400" />
|
||||
<CheckCircleIcon className="w-5 h-5 text-success-600 dark:text-success-400" />
|
||||
) : (
|
||||
<XCircleIcon className="w-5 h-5 text-red-600 dark:text-red-400" />
|
||||
<XCircleIcon className="w-5 h-5 text-error-600 dark:text-error-400" />
|
||||
)}
|
||||
<span className={`font-medium ${
|
||||
validationResult.is_valid
|
||||
? 'text-green-800 dark:text-green-300'
|
||||
: 'text-red-800 dark:text-red-300'
|
||||
? 'text-success-800 dark:text-success-300'
|
||||
: 'text-error-800 dark:text-error-300'
|
||||
}`}>
|
||||
{validationResult.is_valid
|
||||
? 'Content is valid and ready to publish'
|
||||
@@ -493,8 +493,8 @@ export default function PostEditor() {
|
||||
<span className="text-gray-600 dark:text-gray-400">Cluster Mapping:</span>
|
||||
<span className={`ml-2 font-medium ${
|
||||
validationResult.metadata.has_cluster_mapping
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
? 'text-success-600 dark:text-success-400'
|
||||
: 'text-error-600 dark:text-error-400'
|
||||
}`}>
|
||||
{validationResult.metadata.has_cluster_mapping ? 'Yes' : 'No'}
|
||||
</span>
|
||||
@@ -503,8 +503,8 @@ export default function PostEditor() {
|
||||
<span className="text-gray-600 dark:text-gray-400">Taxonomy Mapping:</span>
|
||||
<span className={`ml-2 font-medium ${
|
||||
validationResult.metadata.has_taxonomy_mapping
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
? 'text-success-600 dark:text-success-400'
|
||||
: 'text-error-600 dark:text-error-400'
|
||||
}`}>
|
||||
{validationResult.metadata.has_taxonomy_mapping ? 'Yes' : 'No'}
|
||||
</span>
|
||||
@@ -522,14 +522,14 @@ export default function PostEditor() {
|
||||
{validationResult.validation_errors.map((error, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-start gap-2 p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800"
|
||||
className="flex items-start gap-2 p-3 bg-error-50 dark:bg-error-900/20 rounded-lg border border-error-200 dark:border-error-800"
|
||||
>
|
||||
<AlertCircleIcon className="w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" />
|
||||
<AlertCircleIcon className="w-5 h-5 text-error-600 dark:text-error-400 flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium text-red-800 dark:text-red-300">
|
||||
<div className="text-sm font-medium text-error-800 dark:text-error-300">
|
||||
{error.field || error.code}
|
||||
</div>
|
||||
<div className="text-sm text-red-600 dark:text-red-400 mt-1">
|
||||
<div className="text-sm text-error-600 dark:text-error-400 mt-1">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
@@ -549,14 +549,14 @@ export default function PostEditor() {
|
||||
{validationResult.publish_errors.map((error, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-start gap-2 p-3 bg-orange-50 dark:bg-orange-900/20 rounded-lg border border-orange-200 dark:border-orange-800"
|
||||
className="flex items-start gap-2 p-3 bg-warning-50 dark:bg-warning-900/20 rounded-lg border border-warning-200 dark:border-warning-800"
|
||||
>
|
||||
<XCircleIcon className="w-5 h-5 text-orange-600 dark:text-orange-400 flex-shrink-0 mt-0.5" />
|
||||
<XCircleIcon className="w-5 h-5 text-warning-600 dark:text-warning-400 flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium text-orange-800 dark:text-orange-300">
|
||||
<div className="text-sm font-medium text-warning-800 dark:text-warning-300">
|
||||
{error.field || error.code}
|
||||
</div>
|
||||
<div className="text-sm text-orange-600 dark:text-orange-400 mt-1">
|
||||
<div className="text-sm text-warning-600 dark:text-warning-400 mt-1">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
@@ -645,8 +645,8 @@ export default function PostEditor() {
|
||||
</div>
|
||||
<div className={`text-sm font-medium ${
|
||||
validationResult.is_valid
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
? 'text-success-600 dark:text-success-400'
|
||||
: 'text-error-600 dark:text-error-400'
|
||||
}`}>
|
||||
{validationResult.is_valid ? '✓ Valid' : `✗ ${validationResult.validation_errors.length} error(s)`}
|
||||
</div>
|
||||
@@ -662,7 +662,7 @@ export default function PostEditor() {
|
||||
{content.cluster_id ? (
|
||||
<button
|
||||
onClick={() => navigate(`/planner/clusters/${content.cluster_id}`)}
|
||||
className="text-xs text-blue-600 dark:text-blue-400 hover:underline w-full text-left transition-colors focus:outline-none focus:ring-1 focus:ring-blue-500 rounded px-1"
|
||||
className="text-xs text-brand-600 dark:text-brand-400 hover:underline w-full text-left transition-colors focus:outline-none focus:ring-1 focus:ring-brand-500 rounded px-1"
|
||||
aria-label="View cluster details"
|
||||
>
|
||||
View Cluster →
|
||||
@@ -673,7 +673,7 @@ export default function PostEditor() {
|
||||
{content.taxonomy_id ? (
|
||||
<button
|
||||
onClick={() => navigate(`/sites/builder?taxonomy=${content.taxonomy_id}`)}
|
||||
className="text-xs text-blue-600 dark:text-blue-400 hover:underline w-full text-left transition-colors focus:outline-none focus:ring-1 focus:ring-blue-500 rounded px-1"
|
||||
className="text-xs text-brand-600 dark:text-brand-400 hover:underline w-full text-left transition-colors focus:outline-none focus:ring-1 focus:ring-brand-500 rounded px-1"
|
||||
aria-label="View taxonomy details"
|
||||
>
|
||||
View Taxonomy →
|
||||
|
||||
@@ -483,7 +483,7 @@ export default function SiteSettings() {
|
||||
<div className="flex items-center gap-3 ml-2">
|
||||
<span
|
||||
className={`inline-block w-6 h-6 rounded-full ${
|
||||
integrationStatus === 'connected' ? 'bg-green-500' :
|
||||
integrationStatus === 'connected' ? 'bg-success-500' :
|
||||
integrationStatus === 'configured' ? 'bg-brand-500' : 'bg-gray-300'
|
||||
}`}
|
||||
title={`Integration status: ${
|
||||
@@ -616,9 +616,9 @@ export default function SiteSettings() {
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md bg-gray-100 dark:bg-gray-800">
|
||||
<div className={`w-2 h-2 rounded-full ${
|
||||
wordPressIntegration.sync_status === 'success' ? 'bg-green-500' :
|
||||
wordPressIntegration.sync_status === 'failed' ? 'bg-red-500' :
|
||||
'bg-yellow-500'
|
||||
wordPressIntegration.sync_status === 'success' ? 'bg-success-500' :
|
||||
wordPressIntegration.sync_status === 'failed' ? 'bg-error-500' :
|
||||
'bg-warning-500'
|
||||
}`}></div>
|
||||
<span className="text-xs font-medium text-gray-700 dark:text-gray-300">
|
||||
{wordPressIntegration.sync_status === 'success' ? 'Synced' :
|
||||
@@ -693,7 +693,7 @@ export default function SiteSettings() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className={`px-2 py-1 text-xs rounded ${data.enabled ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'}`}>
|
||||
<span className={`px-2 py-1 text-xs rounded ${data.enabled ? 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'}`}>
|
||||
{data.enabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">Limit: {data.fetch_limit}</span>
|
||||
@@ -723,7 +723,7 @@ export default function SiteSettings() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className={`px-2 py-1 text-xs rounded ${data.enabled ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'}`}>
|
||||
<span className={`px-2 py-1 text-xs rounded ${data.enabled ? 'bg-success-100 text-success-800 dark:bg-success-900 dark:text-success-200' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'}`}>
|
||||
{data.enabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">Limit: {data.fetch_limit}</span>
|
||||
@@ -1088,7 +1088,7 @@ export default function SiteSettings() {
|
||||
setSelectedSectors(selectedSectors.filter(s => s !== sector.slug));
|
||||
}
|
||||
}}
|
||||
className="mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
className="mt-1 h-4 w-4 rounded border-gray-300 text-brand-600 focus:ring-brand-500"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-sm text-gray-900 dark:text-white">
|
||||
|
||||
@@ -105,12 +105,12 @@ export default function SyncDashboard() {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
case 'success':
|
||||
return <CheckCircleIcon className="w-5 h-5 text-green-600 dark:text-green-400" />;
|
||||
return <CheckCircleIcon className="w-5 h-5 text-success-600 dark:text-success-400" />;
|
||||
case 'warning':
|
||||
return <AlertIcon className="w-5 h-5 text-yellow-600 dark:text-yellow-400" />;
|
||||
return <AlertIcon className="w-5 h-5 text-warning-600 dark:text-warning-400" />;
|
||||
case 'error':
|
||||
case 'failed':
|
||||
return <ErrorIcon className="w-5 h-5 text-red-600 dark:text-red-400" />;
|
||||
return <ErrorIcon className="w-5 h-5 text-error-600 dark:text-error-400" />;
|
||||
default:
|
||||
return <TimeIcon className="w-5 h-5 text-gray-400" />;
|
||||
}
|
||||
@@ -241,8 +241,8 @@ export default function SyncDashboard() {
|
||||
</div>
|
||||
|
||||
{integration.error && (
|
||||
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
||||
<div className="text-sm text-red-800 dark:text-red-300">{integration.error}</div>
|
||||
<div className="mb-4 p-3 bg-error-50 dark:bg-error-900/20 border border-error-200 dark:border-error-800 rounded-lg">
|
||||
<div className="text-sm text-error-800 dark:text-error-300">{integration.error}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -313,11 +313,11 @@ export default function SyncDashboard() {
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{mismatches.taxonomies.missing_in_wordpress.length > 0 && (
|
||||
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||
<div className="text-sm font-medium text-yellow-800 dark:text-yellow-300 mb-1">
|
||||
<div className="p-3 bg-warning-50 dark:bg-warning-900/20 rounded-lg">
|
||||
<div className="text-sm font-medium text-warning-800 dark:text-warning-300 mb-1">
|
||||
Missing in WordPress ({mismatches.taxonomies.missing_in_wordpress.length})
|
||||
</div>
|
||||
<ul className="text-sm text-yellow-700 dark:text-yellow-400 space-y-1">
|
||||
<ul className="text-sm text-warning-700 dark:text-warning-400 space-y-1">
|
||||
{mismatches.taxonomies.missing_in_wordpress.slice(0, 5).map((item, idx) => (
|
||||
<li key={idx}>• {item.name} ({item.type})</li>
|
||||
))}
|
||||
@@ -325,11 +325,11 @@ export default function SyncDashboard() {
|
||||
</div>
|
||||
)}
|
||||
{mismatches.taxonomies.missing_in_igny8.length > 0 && (
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<div className="text-sm font-medium text-blue-800 dark:text-blue-300 mb-1">
|
||||
<div className="p-3 bg-brand-50 dark:bg-brand-900/20 rounded-lg">
|
||||
<div className="text-sm font-medium text-brand-800 dark:text-brand-300 mb-1">
|
||||
Missing in IGNY8 ({mismatches.taxonomies.missing_in_igny8.length})
|
||||
</div>
|
||||
<ul className="text-sm text-blue-700 dark:text-blue-400 space-y-1">
|
||||
<ul className="text-sm text-brand-700 dark:text-brand-400 space-y-1">
|
||||
{mismatches.taxonomies.missing_in_igny8.slice(0, 5).map((item, idx) => (
|
||||
<li key={idx}>• {item.name} ({item.type})</li>
|
||||
))}
|
||||
@@ -350,15 +350,15 @@ export default function SyncDashboard() {
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{mismatches.products.missing_in_wordpress.length > 0 && (
|
||||
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||
<div className="text-sm text-yellow-800 dark:text-yellow-300">
|
||||
<div className="p-3 bg-warning-50 dark:bg-warning-900/20 rounded-lg">
|
||||
<div className="text-sm text-warning-800 dark:text-warning-300">
|
||||
{mismatches.products.missing_in_wordpress.length} product(s) missing in WordPress
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{mismatches.products.missing_in_igny8.length > 0 && (
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<div className="text-sm text-blue-800 dark:text-blue-300">
|
||||
<div className="p-3 bg-brand-50 dark:bg-brand-900/20 rounded-lg">
|
||||
<div className="text-sm text-brand-800 dark:text-brand-300">
|
||||
{mismatches.products.missing_in_igny8.length} product(s) missing in IGNY8
|
||||
</div>
|
||||
</div>
|
||||
@@ -377,15 +377,15 @@ export default function SyncDashboard() {
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{mismatches.posts.missing_in_wordpress.length > 0 && (
|
||||
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||
<div className="text-sm text-yellow-800 dark:text-yellow-300">
|
||||
<div className="p-3 bg-warning-50 dark:bg-warning-900/20 rounded-lg">
|
||||
<div className="text-sm text-warning-800 dark:text-warning-300">
|
||||
{mismatches.posts.missing_in_wordpress.length} post(s) missing in WordPress
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{mismatches.posts.missing_in_igny8.length > 0 && (
|
||||
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||
<div className="text-sm text-blue-800 dark:text-blue-300">
|
||||
<div className="p-3 bg-brand-50 dark:bg-brand-900/20 rounded-lg">
|
||||
<div className="text-sm text-brand-800 dark:text-brand-300">
|
||||
{mismatches.posts.missing_in_igny8.length} post(s) missing in IGNY8
|
||||
</div>
|
||||
</div>
|
||||
@@ -449,7 +449,7 @@ export default function SyncDashboard() {
|
||||
</Badge>
|
||||
</div>
|
||||
{log.error && (
|
||||
<div className="mt-2 text-xs text-red-600 dark:text-red-400">
|
||||
<div className="mt-2 text-xs text-error-600 dark:text-error-400">
|
||||
{log.error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -134,7 +134,7 @@ export default function ThinkerDashboard() {
|
||||
colors: ["var(--color-warning)", "var(--color-primary)", "var(--color-purple)", "var(--color-success)"],
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: { colors: "#6b7280" },
|
||||
labels: { colors: "var(--color-gray-500)" },
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
@@ -191,29 +191,29 @@ export default function ThinkerDashboard() {
|
||||
<Link
|
||||
key={module.title}
|
||||
to={module.path}
|
||||
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
className="rounded-2xl border-2 border-gray-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||
<Icon className="h-7 w-7" />
|
||||
</div>
|
||||
{module.status === "coming-soon" && (
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-semibold bg-slate-100 text-slate-600">
|
||||
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-semibold bg-gray-100 text-gray-600">
|
||||
Soon
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">{module.description}</p>
|
||||
{module.count > 0 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-2xl font-bold text-slate-900">{module.count}</span>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
<span className="text-2xl font-bold text-gray-900">{module.count}</span>
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
</div>
|
||||
)}
|
||||
{module.status === "coming-soon" && (
|
||||
<div className="mt-4 pt-4 border-t border-slate-200">
|
||||
<span className="text-xs text-slate-500">Coming soon</span>
|
||||
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||
<span className="text-xs text-gray-500">Coming soon</span>
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
@@ -235,17 +235,17 @@ export default function ThinkerDashboard() {
|
||||
{recentPrompts.map((prompt) => (
|
||||
<div
|
||||
key={prompt.id}
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-slate-200 bg-white hover:shadow-md transition"
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-gray-200 bg-white hover:shadow-md transition"
|
||||
>
|
||||
<div className="size-10 rounded-lg bg-gradient-to-br from-[var(--color-warning)] to-[var(--color-warning-dark)] flex items-center justify-center text-white shadow-md">
|
||||
<FileTextIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h4 className="font-semibold text-slate-900">{prompt.name}</h4>
|
||||
<h4 className="font-semibold text-gray-900">{prompt.name}</h4>
|
||||
<span className="text-xs font-semibold text-[var(--color-primary)]">{prompt.usage} uses</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-xs text-slate-600">
|
||||
<div className="flex items-center gap-3 text-xs text-gray-600">
|
||||
<span>{prompt.category}</span>
|
||||
<span>•</span>
|
||||
<span>{prompt.lastUsed}</span>
|
||||
@@ -272,8 +272,8 @@ export default function ThinkerDashboard() {
|
||||
className="!justify-start !h-auto !py-4"
|
||||
>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">New Prompt</h4>
|
||||
<p className="text-sm text-slate-600">Create a reusable prompt template</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">New Prompt</h4>
|
||||
<p className="text-sm text-gray-600">Create a reusable prompt template</p>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
@@ -289,8 +289,8 @@ export default function ThinkerDashboard() {
|
||||
className="!justify-start !h-auto !py-4"
|
||||
>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">New Author Profile</h4>
|
||||
<p className="text-sm text-slate-600">Define a writing voice and style</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">New Author Profile</h4>
|
||||
<p className="text-sm text-gray-600">Define a writing voice and style</p>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
@@ -306,8 +306,8 @@ export default function ThinkerDashboard() {
|
||||
className="!justify-start !h-auto !py-4"
|
||||
>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">New Strategy</h4>
|
||||
<p className="text-sm text-slate-600">Build a content playbook</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">New Strategy</h4>
|
||||
<p className="text-sm text-gray-600">Build a content playbook</p>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -322,8 +322,8 @@ export default function ThinkerDashboard() {
|
||||
<BoltIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Centralized Control</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Centralized Control</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Manage all AI prompts, author voices, and brand guidelines in one place. Changes sync automatically to all content generation.
|
||||
</p>
|
||||
</div>
|
||||
@@ -333,8 +333,8 @@ export default function ThinkerDashboard() {
|
||||
<CheckCircleIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Version Control</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Version Control</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Track changes to prompts and strategies with full version history. Roll back to previous versions when needed.
|
||||
</p>
|
||||
</div>
|
||||
@@ -344,8 +344,8 @@ export default function ThinkerDashboard() {
|
||||
<ShootingStarIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Automated Enforcement</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Automated Enforcement</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Every piece of content automatically uses your defined prompts, author profiles, and brand guidelines.
|
||||
</p>
|
||||
</div>
|
||||
@@ -356,34 +356,34 @@ export default function ThinkerDashboard() {
|
||||
<ComponentCard title="Getting Started" desc="Quick guide to using Thinker">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0693e3] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-brand-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Create Author Profiles</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Create Author Profiles</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Define writing voices and styles that match your brand. Each profile can have unique tone, structure, and guidelines.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0bbf87] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-success-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Build Prompt Library</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Build Prompt Library</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Create reusable prompt templates for different content types. Use variables to make prompts dynamic and flexible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#5d4ae3] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-purple-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Define Strategies</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Define Strategies</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Create content playbooks that combine prompts, profiles, and guidelines. Apply strategies to specific clusters or content types.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -272,7 +272,7 @@ export default function WriterDashboard() {
|
||||
description: `${stats?.content.published || 0} pieces published to WordPress`,
|
||||
timestamp: new Date(Date.now() - 30 * 60 * 1000),
|
||||
icon: PaperPlaneIcon,
|
||||
color: "text-green-600",
|
||||
color: "text-success-600",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -280,7 +280,7 @@ export default function WriterDashboard() {
|
||||
description: `${stats?.content.total || 0} content pieces created`,
|
||||
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
||||
icon: PencilIcon,
|
||||
color: "text-blue-600",
|
||||
color: "text-brand-600",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@@ -288,7 +288,7 @@ export default function WriterDashboard() {
|
||||
description: `${stats?.images.generated || 0} images created`,
|
||||
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000),
|
||||
icon: BoxIcon,
|
||||
color: "text-orange-600",
|
||||
color: "text-warning-600",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -305,18 +305,18 @@ export default function WriterDashboard() {
|
||||
},
|
||||
xaxis: {
|
||||
categories: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
labels: { style: { colors: "#6b7280" } },
|
||||
labels: { style: { colors: "var(--color-gray-500)" } },
|
||||
},
|
||||
yaxis: {
|
||||
labels: { style: { colors: "#6b7280" } },
|
||||
labels: { style: { colors: "var(--color-gray-500)" } },
|
||||
},
|
||||
legend: {
|
||||
position: "top",
|
||||
labels: { colors: "#6b7280" },
|
||||
labels: { colors: "var(--color-gray-500)" },
|
||||
},
|
||||
colors: ["var(--color-primary)", "var(--color-success)", "var(--color-warning)"],
|
||||
grid: {
|
||||
borderColor: "#e5e7eb",
|
||||
borderColor: "var(--color-gray-200)",
|
||||
},
|
||||
fill: {
|
||||
type: "gradient",
|
||||
@@ -352,7 +352,7 @@ export default function WriterDashboard() {
|
||||
toolbar: { show: false }
|
||||
},
|
||||
labels: Object.keys(stats.tasks.byStatus).filter(key => stats.tasks.byStatus[key] > 0),
|
||||
colors: ['#465FFF', '#F59E0B', '#10B981', '#EF4444', '#8B5CF6'],
|
||||
colors: ['var(--color-primary)', 'var(--color-warning)', 'var(--color-success)', 'var(--color-danger)', 'var(--color-purple)'],
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
fontFamily: 'Outfit',
|
||||
@@ -372,7 +372,7 @@ export default function WriterDashboard() {
|
||||
show: true,
|
||||
fontSize: '24px',
|
||||
fontWeight: 700,
|
||||
color: '#465FFF',
|
||||
color: 'var(--color-primary)',
|
||||
fontFamily: 'Outfit',
|
||||
formatter: () => {
|
||||
const total = Object.values(stats.tasks.byStatus).reduce((a: number, b: number) => a + b, 0);
|
||||
@@ -403,7 +403,7 @@ export default function WriterDashboard() {
|
||||
toolbar: { show: false },
|
||||
height: 300
|
||||
},
|
||||
colors: ['#465FFF', '#F59E0B', '#10B981'],
|
||||
colors: ['var(--color-primary)', 'var(--color-warning)', 'var(--color-success)'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: false,
|
||||
@@ -540,21 +540,21 @@ export default function WriterDashboard() {
|
||||
<Link
|
||||
key={module.title}
|
||||
to={module.path}
|
||||
className="rounded-2xl border-2 border-slate-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
className="rounded-2xl border-2 border-gray-200 bg-white p-6 hover:shadow-xl hover:-translate-y-1 transition-all group"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className={`inline-flex size-14 rounded-xl bg-gradient-to-br ${module.color} items-center justify-center text-white shadow-lg`}>
|
||||
<Icon className="h-7 w-7" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-slate-600 mb-4">{module.description}</p>
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-2">{module.title}</h3>
|
||||
<p className="text-sm text-gray-600 mb-4">{module.description}</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-slate-900">{module.count}</div>
|
||||
<div className="text-xs text-slate-500">{module.metric}</div>
|
||||
<div className="text-2xl font-bold text-gray-900">{module.count}</div>
|
||||
<div className="text-xs text-gray-500">{module.metric}</div>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] group-hover:translate-x-1 transition" />
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
@@ -577,17 +577,17 @@ export default function WriterDashboard() {
|
||||
return (
|
||||
<div
|
||||
key={activity.id}
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-slate-200 bg-white hover:shadow-md transition"
|
||||
className="flex items-center gap-4 p-4 rounded-lg border border-gray-200 bg-white hover:shadow-md transition"
|
||||
>
|
||||
<div className={`size-10 rounded-lg bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center ${activity.color}`}>
|
||||
<div className={`size-10 rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center ${activity.color}`}>
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h4 className="font-semibold text-slate-900">{activity.type}</h4>
|
||||
<span className="text-xs text-slate-500">{formatTimeAgo(activity.timestamp)}</span>
|
||||
<h4 className="font-semibold text-gray-900">{activity.type}</h4>
|
||||
<span className="text-xs text-gray-500">{formatTimeAgo(activity.timestamp)}</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-600">{activity.description}</p>
|
||||
<p className="text-sm text-gray-600">{activity.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -674,58 +674,58 @@ export default function WriterDashboard() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<Link
|
||||
to="/writer/tasks"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-[var(--color-primary)] hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<FileTextIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Create Task</h4>
|
||||
<p className="text-sm text-slate-600">New writing task</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Create Task</h4>
|
||||
<p className="text-sm text-gray-600">New writing task</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-[var(--color-primary)] transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/writer/content"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#0bbf87] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-success-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-success)] to-[var(--color-success-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<PencilIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Generate Content</h4>
|
||||
<p className="text-sm text-slate-600">AI content creation</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Generate Content</h4>
|
||||
<p className="text-sm text-gray-600">AI content creation</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#0bbf87] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-success-500 transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/writer/images"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#ff7a00] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-warning-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-warning)] to-[var(--color-warning-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<BoxIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Generate Images</h4>
|
||||
<p className="text-sm text-slate-600">Create visuals</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Generate Images</h4>
|
||||
<p className="text-sm text-gray-600">Create visuals</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#ff7a00] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-warning-500 transition" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/writer/published"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-slate-200 bg-white hover:border-[#5d4ae3] hover:shadow-lg transition-all group"
|
||||
className="flex items-center gap-4 p-6 rounded-xl border-2 border-gray-200 bg-white hover:border-purple-500 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="size-12 rounded-xl bg-gradient-to-br from-[var(--color-purple)] to-[var(--color-purple-dark)] flex items-center justify-center text-white shadow-lg">
|
||||
<PaperPlaneIcon className="h-6 w-6" />
|
||||
</div>
|
||||
<div className="flex-1 text-left">
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Publish Content</h4>
|
||||
<p className="text-sm text-slate-600">Publish to WordPress</p>
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Publish Content</h4>
|
||||
<p className="text-sm text-gray-600">Publish to WordPress</p>
|
||||
</div>
|
||||
<ArrowRightIcon className="h-5 w-5 text-slate-400 group-hover:text-[#5d4ae3] transition" />
|
||||
<ArrowRightIcon className="h-5 w-5 text-gray-400 group-hover:text-purple-500 transition" />
|
||||
</Link>
|
||||
</div>
|
||||
</ComponentCard>
|
||||
@@ -739,8 +739,8 @@ export default function WriterDashboard() {
|
||||
<FileTextIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Task Creation</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Task Creation</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Create writing tasks from content ideas. Each task includes target keywords, outline, and word count requirements.
|
||||
</p>
|
||||
</div>
|
||||
@@ -750,8 +750,8 @@ export default function WriterDashboard() {
|
||||
<PencilIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">AI Content Generation</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">AI Content Generation</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Generate full content pieces using AI. Content is created based on your prompts, author profiles, and brand guidelines.
|
||||
</p>
|
||||
</div>
|
||||
@@ -761,8 +761,8 @@ export default function WriterDashboard() {
|
||||
<BoxIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Image Generation</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Image Generation</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Automatically generate featured images and in-article images for your content. Images are optimized for SEO and engagement.
|
||||
</p>
|
||||
</div>
|
||||
@@ -773,34 +773,34 @@ export default function WriterDashboard() {
|
||||
<ComponentCard title="Getting Started" desc="Quick guide to using Writer">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0693e3] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-brand-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
1
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Create Tasks</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Create Tasks</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Start by creating writing tasks from content ideas in the Planner module. Tasks define what content needs to be written.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#0bbf87] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-success-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Generate Content</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Generate Content</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Use AI to generate content from tasks. Review and edit generated content before publishing.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-[#ff7a00] text-white flex items-center justify-center font-bold text-sm">
|
||||
<div className="flex-shrink-0 size-8 rounded-full bg-warning-500 text-white flex items-center justify-center font-bold text-sm">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-slate-900 mb-1">Publish</h4>
|
||||
<p className="text-sm text-slate-600">
|
||||
<h4 className="font-semibold text-gray-900 mb-1">Publish</h4>
|
||||
<p className="text-sm text-gray-600">
|
||||
Once content is reviewed and images are generated, publish directly to WordPress or export for manual publishing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -310,14 +310,14 @@ export default function AccountSettingsPage() {
|
||||
{activeTab === 'account' && (
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
{error && (
|
||||
<div className="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
||||
<p className="text-red-800 dark:text-red-200">{error}</p>
|
||||
<div className="p-4 bg-error-50 dark:bg-error-900/20 border border-error-200 dark:border-error-800 rounded-lg">
|
||||
<p className="text-error-800 dark:text-error-200">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<div className="p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
||||
<p className="text-green-800 dark:text-green-200">{success}</p>
|
||||
<div className="p-4 bg-success-50 dark:bg-success-900/20 border border-success-200 dark:border-success-800 rounded-lg">
|
||||
<p className="text-success-800 dark:text-success-200">{success}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -345,8 +345,8 @@ export default function ContentSettingsPage() {
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
<Card className="p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg">
|
||||
<FileText className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
||||
<div className="p-2 bg-brand-100 dark:bg-brand-900/30 rounded-lg">
|
||||
<FileText className="w-5 h-5 text-brand-600 dark:text-brand-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">Content Generation</h2>
|
||||
@@ -423,8 +423,8 @@ export default function ContentSettingsPage() {
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
<Card className="p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 bg-green-100 dark:bg-green-900/30 rounded-lg">
|
||||
<Send className="w-5 h-5 text-green-600 dark:text-green-400" />
|
||||
<div className="p-2 bg-success-100 dark:bg-success-900/30 rounded-lg">
|
||||
<Send className="w-5 h-5 text-success-600 dark:text-success-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">WordPress Publishing</h2>
|
||||
@@ -451,8 +451,8 @@ export default function ContentSettingsPage() {
|
||||
</div>
|
||||
|
||||
{publishingSettings.autoPublishEnabled && (
|
||||
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
|
||||
<p className="text-sm text-blue-800 dark:text-blue-200">
|
||||
<div className="mt-4 p-3 bg-brand-50 dark:bg-brand-900/20 border border-brand-200 dark:border-brand-800 rounded-lg">
|
||||
<p className="text-sm text-brand-800 dark:text-brand-200">
|
||||
Articles will be published automatically once they pass review. You can still manually review them first if needed.
|
||||
</p>
|
||||
</div>
|
||||
@@ -549,7 +549,7 @@ export default function ContentSettingsPage() {
|
||||
{/* Row 2: Featured Image Size */}
|
||||
<div>
|
||||
<Label className="mb-2">Featured Image</Label>
|
||||
<div className="p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-500 to-blue-500 text-white">
|
||||
<div className="p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-500 to-brand-500 text-white">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="font-medium">Featured Image Size</div>
|
||||
<div className="text-xs bg-white/20 px-2 py-1 rounded">Always Enabled</div>
|
||||
|
||||
@@ -68,13 +68,13 @@ export default function NotificationsPage() {
|
||||
const getSeverityIcon = (severity: string) => {
|
||||
switch (severity) {
|
||||
case 'success':
|
||||
return <CheckCircle className="w-5 h-5 text-green-500" />;
|
||||
return <CheckCircle className="w-5 h-5 text-success-500" />;
|
||||
case 'warning':
|
||||
return <AlertTriangle className="w-5 h-5 text-yellow-500" />;
|
||||
return <AlertTriangle className="w-5 h-5 text-warning-500" />;
|
||||
case 'error':
|
||||
return <XCircle className="w-5 h-5 text-red-500" />;
|
||||
return <XCircle className="w-5 h-5 text-error-500" />;
|
||||
default:
|
||||
return <Info className="w-5 h-5 text-blue-500" />;
|
||||
return <Info className="w-5 h-5 text-brand-500" />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -190,7 +190,7 @@ export default function NotificationsPage() {
|
||||
</h1>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{unreadCount > 0 ? (
|
||||
<span className="font-medium text-blue-600 dark:text-blue-400">
|
||||
<span className="font-medium text-brand-600 dark:text-brand-400">
|
||||
{unreadCount} unread notification{unreadCount !== 1 ? 's' : ''}
|
||||
</span>
|
||||
) : (
|
||||
@@ -313,7 +313,7 @@ export default function NotificationsPage() {
|
||||
<Card className="overflow-hidden">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center p-12">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-brand-600"></div>
|
||||
</div>
|
||||
) : filteredNotifications.length === 0 ? (
|
||||
<div className="text-center p-12">
|
||||
@@ -330,7 +330,7 @@ export default function NotificationsPage() {
|
||||
<div
|
||||
key={notification.id}
|
||||
className={`p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors ${
|
||||
!notification.is_read ? 'bg-blue-50 dark:bg-blue-900/10' : ''
|
||||
!notification.is_read ? 'bg-brand-50 dark:bg-brand-900/10' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
@@ -347,7 +347,7 @@ export default function NotificationsPage() {
|
||||
className={`text-base font-medium ${
|
||||
notification.is_read
|
||||
? 'text-gray-900 dark:text-white'
|
||||
: 'text-blue-900 dark:text-blue-100'
|
||||
: 'text-brand-900 dark:text-brand-100'
|
||||
}`}
|
||||
>
|
||||
{notification.title}
|
||||
@@ -387,7 +387,7 @@ export default function NotificationsPage() {
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleDelete(notification.id)}
|
||||
className="text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||
className="text-error-600 hover:text-error-700 hover:bg-error-50 dark:hover:bg-error-900/20"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
|
||||
@@ -381,7 +381,7 @@ export default function PlansAndBillingPage() {
|
||||
|
||||
{/* Activation / pending payment notice */}
|
||||
{!hasActivePlan && (
|
||||
<div className="mb-4 p-4 rounded-lg border border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
|
||||
<div className="mb-4 p-4 rounded-lg border border-warning-200 bg-warning-50 text-warning-800 dark:border-warning-800 dark:bg-warning-900/20 dark:text-warning-200">
|
||||
No active plan. Choose a plan below to activate your account.
|
||||
</div>
|
||||
)}
|
||||
@@ -392,9 +392,9 @@ export default function PlansAndBillingPage() {
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg flex items-center gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-red-600" />
|
||||
<p className="text-red-800 dark:text-red-200">{error}</p>
|
||||
<div className="mb-6 p-4 bg-error-50 dark:bg-error-900/20 border border-error-200 dark:border-error-800 rounded-lg flex items-center gap-3">
|
||||
<AlertCircle className="w-5 h-5 text-error-600" />
|
||||
<p className="text-error-800 dark:text-error-200">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -409,7 +409,7 @@ export default function PlansAndBillingPage() {
|
||||
<Card className="p-6 lg:col-span-2">
|
||||
<h2 className="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Your Current Plan</h2>
|
||||
{!hasActivePlan && (
|
||||
<div className="p-4 mb-4 rounded-lg border border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200 flex items-start gap-3">
|
||||
<div className="p-4 mb-4 rounded-lg border border-warning-200 bg-warning-50 text-warning-800 dark:border-warning-800 dark:bg-warning-900/20 dark:text-warning-200 flex items-start gap-3">
|
||||
<AlertCircle className="w-5 h-5 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="font-medium">No Active Plan</p>
|
||||
@@ -843,24 +843,24 @@ export default function PlansAndBillingPage() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex items-start gap-3 p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<AlertCircle className="w-5 h-5 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="text-sm text-amber-800 dark:text-amber-200">
|
||||
<div className="flex items-start gap-3 p-4 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-800 rounded-lg">
|
||||
<AlertCircle className="w-5 h-5 text-warning-600 dark:text-warning-400 mt-0.5 flex-shrink-0" />
|
||||
<div className="text-sm text-warning-800 dark:text-warning-200">
|
||||
<p className="font-medium mb-1">Are you sure you want to cancel?</p>
|
||||
<p>Your subscription will remain active until the end of your current billing period. After that:</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-2 pl-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-red-500 mt-1">•</span>
|
||||
<span className="text-error-500 mt-1">•</span>
|
||||
<span>You'll lose access to premium features</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-red-500 mt-1">•</span>
|
||||
<span className="text-error-500 mt-1">•</span>
|
||||
<span>Remaining credits will be preserved for 30 days</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-red-500 mt-1">•</span>
|
||||
<span className="text-error-500 mt-1">•</span>
|
||||
<span>You can resubscribe anytime to restore access</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -221,9 +221,9 @@ export default function PurchaseCreditsPage() {
|
||||
<h3 className="text-lg font-semibold mb-4">Submit Payment Proof</h3>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4 flex items-start gap-2">
|
||||
<AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-red-800 text-sm">{error}</p>
|
||||
<div className="bg-error-50 border border-error-200 rounded-lg p-4 mb-4 flex items-start gap-2">
|
||||
<AlertCircle className="w-5 h-5 text-error-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-error-800 text-sm">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -298,9 +298,9 @@ export default function PurchaseCreditsPage() {
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6 flex items-start gap-2">
|
||||
<AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-red-800">{error}</p>
|
||||
<div className="bg-error-50 border border-error-200 rounded-lg p-4 mb-6 flex items-start gap-2">
|
||||
<AlertCircle className="w-5 h-5 text-error-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-error-800">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -316,10 +316,10 @@ export default function PurchaseCreditsPage() {
|
||||
selectedPackage?.id === pkg.id
|
||||
? 'border-[var(--color-brand-500)] bg-[var(--color-brand-50)]'
|
||||
: 'border-gray-200 hover:border-[var(--color-brand-300)] bg-white'
|
||||
} ${pkg.is_featured ? 'ring-2 ring-yellow-400' : ''}`}
|
||||
} ${pkg.is_featured ? 'ring-2 ring-warning-400' : ''}`}
|
||||
>
|
||||
{pkg.is_featured && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-yellow-400 text-yellow-900 text-xs font-bold px-3 py-1 rounded-full">
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-warning-400 text-warning-900 text-xs font-bold px-3 py-1 rounded-full">
|
||||
FEATURED
|
||||
</div>
|
||||
)}
|
||||
@@ -332,7 +332,7 @@ export default function PurchaseCreditsPage() {
|
||||
<div className="text-sm text-gray-600 mb-3">credits</div>
|
||||
<div className="text-2xl font-bold mb-1">${pkg.price}</div>
|
||||
{pkg.discount_percentage > 0 && (
|
||||
<div className="text-sm text-green-600 font-semibold">
|
||||
<div className="text-sm text-success-600 font-semibold">
|
||||
Save {pkg.discount_percentage}%
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -140,14 +140,14 @@ export default function UsageAnalyticsPage() {
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 bg-gradient-to-br from-indigo-50 to-indigo-100 dark:from-indigo-900/20 dark:to-indigo-800/10 border-indigo-200 dark:border-indigo-700">
|
||||
<Card className="p-4 bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/10 border-purple-200 dark:border-purple-700">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-indigo-500 rounded-lg">
|
||||
<div className="p-2 bg-purple-500 rounded-lg">
|
||||
<Calendar className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-indigo-700 dark:text-indigo-300">Usage %</div>
|
||||
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
|
||||
<div className="text-xs text-purple-700 dark:text-purple-300">Usage %</div>
|
||||
<div className="text-2xl font-bold text-purple-600 dark:text-purple-400">
|
||||
{creditBalance.plan_credits_per_month > 0
|
||||
? Math.round((creditBalance.credits_used_this_month / creditBalance.plan_credits_per_month) * 100)
|
||||
: 0}%
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function ProfileSettingsPage() {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
|
||||
className="flex items-center gap-2 px-4 py-2 bg-brand-600 text-white rounded-lg hover:bg-brand-700 disabled:opacity-50"
|
||||
>
|
||||
{saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
|
||||
{saving ? 'Saving...' : '✓ Save My Settings'}
|
||||
@@ -60,7 +60,7 @@ export default function ProfileSettingsPage() {
|
||||
type="text"
|
||||
value={profile.firstName}
|
||||
onChange={(e) => setProfile({ ...profile, firstName: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -71,7 +71,7 @@ export default function ProfileSettingsPage() {
|
||||
type="text"
|
||||
value={profile.lastName}
|
||||
onChange={(e) => setProfile({ ...profile, lastName: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -82,7 +82,7 @@ export default function ProfileSettingsPage() {
|
||||
type="email"
|
||||
value={profile.email}
|
||||
onChange={(e) => setProfile({ ...profile, email: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -93,7 +93,7 @@ export default function ProfileSettingsPage() {
|
||||
type="tel"
|
||||
value={profile.phone}
|
||||
onChange={(e) => setProfile({ ...profile, phone: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,7 +109,7 @@ export default function ProfileSettingsPage() {
|
||||
<select
|
||||
value={profile.timezone}
|
||||
onChange={(e) => setProfile({ ...profile, timezone: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
>
|
||||
<option value="America/New_York">Eastern Time</option>
|
||||
<option value="America/Chicago">Central Time</option>
|
||||
@@ -125,7 +125,7 @@ export default function ProfileSettingsPage() {
|
||||
<select
|
||||
value={profile.language}
|
||||
onChange={(e) => setProfile({ ...profile, language: e.target.value })}
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800"
|
||||
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-brand-500 dark:bg-gray-800"
|
||||
>
|
||||
<option value="en">English</option>
|
||||
<option value="es">Spanish</option>
|
||||
@@ -152,7 +152,7 @@ export default function ProfileSettingsPage() {
|
||||
type="checkbox"
|
||||
checked={profile.emailNotifications}
|
||||
onChange={(e) => setProfile({ ...profile, emailNotifications: e.target.checked })}
|
||||
className="w-5 h-5 text-blue-600 rounded focus:ring-blue-500"
|
||||
className="w-5 h-5 text-brand-600 rounded focus:ring-brand-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -166,7 +166,7 @@ export default function ProfileSettingsPage() {
|
||||
type="checkbox"
|
||||
checked={profile.marketingEmails}
|
||||
onChange={(e) => setProfile({ ...profile, marketingEmails: e.target.checked })}
|
||||
className="w-5 h-5 text-blue-600 rounded focus:ring-blue-500"
|
||||
className="w-5 h-5 text-brand-600 rounded focus:ring-brand-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user