many changes for modules widgets and colors and styling

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-31 23:52:43 +00:00
parent b61bd6e64d
commit 89b64cd737
34 changed files with 2450 additions and 1985 deletions

View File

@@ -38,14 +38,24 @@ import {
ArrowRightIcon
} from '../../icons';
/**
* Pipeline stage configuration with consistent design system colors:
* - Keywords → Clusters: brand/primary (keywords side) to purple (clusters side)
* - Clusters → Ideas: purple to warning/amber
* - Ideas → Tasks: warning/amber to brand/primary
* - Tasks → Content: brand/primary to success/green
* - Content → Image Prompts: success to purple
* - Image Prompts → Images: purple
* - Review Gate: warning/amber
*/
const STAGE_CONFIG = [
{ 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-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' },
{ icon: ListIcon, color: 'from-brand-500 to-brand-600', textColor: 'text-brand-600 dark:text-brand-400', bgColor: 'bg-brand-100 dark:bg-brand-900/30', hoverColor: 'hover:border-brand-500', name: 'Keywords → Clusters' },
{ icon: GroupIcon, color: 'from-purple-500 to-purple-600', textColor: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-100 dark:bg-purple-900/30', hoverColor: 'hover:border-purple-500', name: 'Clusters → Ideas' },
{ icon: BoltIcon, color: 'from-warning-500 to-warning-600', textColor: 'text-warning-600 dark:text-warning-400', bgColor: 'bg-warning-100 dark:bg-warning-900/30', hoverColor: 'hover:border-warning-500', name: 'Ideas → Tasks' },
{ icon: CheckCircleIcon, color: 'from-brand-500 to-brand-600', textColor: 'text-brand-600 dark:text-brand-400', bgColor: 'bg-brand-100 dark:bg-brand-900/30', hoverColor: 'hover:border-brand-500', name: 'Tasks → Content' },
{ icon: PencilIcon, color: 'from-success-500 to-success-600', textColor: 'text-success-600 dark:text-success-400', bgColor: 'bg-success-100 dark:bg-success-900/30', hoverColor: 'hover:border-success-500', name: 'Content → Image Prompts' },
{ icon: FileIcon, color: 'from-purple-500 to-purple-600', textColor: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-100 dark:bg-purple-900/30', hoverColor: 'hover:border-purple-500', name: 'Image Prompts → Images' },
{ icon: PaperPlaneIcon, color: 'from-success-500 to-success-600', textColor: 'text-success-600 dark:text-success-400', bgColor: 'bg-success-100 dark:bg-success-900/30', hoverColor: 'hover:border-success-500', name: 'Review Gate' },
];
const AutomationPage: React.FC = () => {
@@ -421,8 +431,8 @@ const AutomationPage: React.FC = () => {
{/* Compact Schedule & Controls Panel */}
{config && (
<ComponentCard className="border-0 overflow-hidden rounded-2xl bg-gradient-to-br from-brand-600 to-brand-700">
<div className="flex flex-col lg:flex-row items-center lg:items-center justify-between gap-3">
<ComponentCard className="border-0 overflow-hidden rounded-2xl bg-gradient-to-br from-brand-600 to-brand-700 [&>div]:!py-1.5 [&>div]:!px-4">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-4 flex-wrap">
<div className="flex items-center gap-2">
{config.is_enabled ? (
@@ -454,24 +464,27 @@ const AutomationPage: React.FC = () => {
)}
</div>
</div>
<div className={`max-w-sm rounded-lg border-2 p-2 transition-all flex items-center gap-3 shadow-sm
${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-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-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'}
{/* Ready to Run Card - Inline horizontal */}
<div className={`flex items-center gap-2 px-3 py-1.5 rounded-lg border-2 transition-all
${currentRun?.status === 'running' ? 'border-brand-300 bg-white' : currentRun?.status === 'paused' ? 'border-warning-300 bg-white' : totalPending > 0 ? 'border-success-300 bg-white' : 'border-white/30 bg-white/10'}`}>
<div className={`size-6 rounded-md flex items-center justify-center flex-shrink-0
${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-3.5 text-white" /> : currentRun?.status === 'running' ? <BoltIcon className="size-3.5 text-white" /> : currentRun?.status === 'paused' ? <ClockIcon className="size-3.5 text-white" /> : <BoltIcon className="size-3.5 text-white" />}
</div>
<div className="flex items-center gap-3">
<span className={`text-sm font-semibold ${totalPending > 0 || currentRun ? 'text-gray-900' : 'text-white/90'}`}>
{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'}
</span>
<span className={`text-xs ${totalPending > 0 || currentRun ? 'text-gray-600' : 'text-white/70'}`}>
{currentRun ? `Started: ${new Date(currentRun.started_at).toLocaleTimeString()}` : (totalPending > 0 ? `${totalPending} items in pipeline` : 'All stages clear')}
</span>
</div>
</div>
<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>
</div>
<div className="flex items-center gap-2">
<Button
onClick={() => setShowConfigModal(true)}
@@ -588,20 +601,20 @@ const AutomationPage: React.FC = () => {
</div>
{/* Ideas */}
<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="bg-gradient-to-br from-warning-50 to-warning-100 dark:from-warning-900/20 dark:to-warning-800/20 rounded-xl p-4 border-2 border-warning-200 dark:border-warning-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-purple-500 to-purple-600 flex items-center justify-center">
<CheckCircleIcon className="size-5 text-white" />
<div className="size-10 rounded-lg bg-gradient-to-br from-warning-500 to-warning-600 flex items-center justify-center">
<BoltIcon className="size-5 text-white" />
</div>
<div className="text-sm font-bold text-purple-900 dark:text-purple-100">Ideas</div>
<div className="text-sm font-bold text-warning-900 dark:text-warning-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-purple-900">{total}</div>
<div className="text-3xl font-bold text-warning-900">{total}</div>
</div>
);
})()}
@@ -613,9 +626,9 @@ const AutomationPage: React.FC = () => {
const completed = res?.completed ?? pipelineOverview[2]?.counts?.completed ?? metrics?.ideas?.completed ?? 0;
return (
renderMetricRow([
{ label: 'New:', value: newCount, colorCls: 'text-purple-700' },
{ label: 'Queued:', value: queued, colorCls: 'text-purple-700' },
{ label: 'Completed:', value: completed, colorCls: 'text-purple-700' },
{ label: 'New:', value: newCount, colorCls: 'text-warning-700' },
{ label: 'Queued:', value: queued, colorCls: 'text-warning-700' },
{ label: 'Completed:', value: completed, colorCls: 'text-warning-700' },
])
);
})()}

View File

@@ -3,7 +3,6 @@ import { useNavigate } from 'react-router-dom';
import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { linkerApi } from '../../api/linker.api';
import { fetchContent, Content as ContentType } from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
@@ -241,40 +240,7 @@ export default function LinkerContentList() {
</div>
)}
{/* Module Metrics Footer */}
<ModuleMetricsFooter
metrics={[
{
title: 'Total Content',
value: totalCount.toLocaleString(),
subtitle: `${content.filter(c => (c.internal_links?.length || 0) > 0).length} with links`,
icon: <FileIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/linker/content',
},
{
title: 'Links Added',
value: content.reduce((sum, c) => sum + (c.internal_links?.length || 0), 0).toLocaleString(),
subtitle: `${Object.keys(linkResults).length} processed`,
icon: <PlugInIcon className="w-5 h-5" />,
accentColor: 'purple',
},
{
title: 'Avg Links/Content',
value: content.length > 0
? (content.reduce((sum, c) => sum + (c.internal_links?.length || 0), 0) / content.length).toFixed(1)
: '0',
subtitle: `${content.filter(c => c.linker_version && c.linker_version > 0).length} optimized`,
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'green',
},
]}
progress={{
label: 'Content Linking Progress',
value: totalCount > 0 ? Math.round((content.filter(c => (c.internal_links?.length || 0) > 0).length / totalCount) * 100) : 0,
color: 'primary',
}}
/>
{/* Module footer placeholder - module on hold */}
</div>
</>
);

View File

@@ -3,7 +3,6 @@ import { useNavigate } from 'react-router-dom';
import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
import { optimizerApi, EntryPoint } from '../../api/optimizer.api';
import { fetchContent, Content as ContentType } from '../../services/api';
import { useToast } from '../../components/ui/toast/ToastContainer';
@@ -319,46 +318,7 @@ export default function OptimizerContentSelector() {
</div>
)}
{/* Module Metrics Footer */}
<ModuleMetricsFooter
metrics={[
{
title: 'Total Content',
value: totalCount.toLocaleString(),
subtitle: `${filteredContent.length} filtered`,
icon: <FileIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/optimizer/content',
},
{
title: 'Optimized',
value: content.filter(c => c.optimizer_version && c.optimizer_version > 0).length.toLocaleString(),
subtitle: `${processing.length} processing`,
icon: <BoltIcon className="w-5 h-5" />,
accentColor: 'orange',
},
{
title: 'Avg Score',
value: content.length > 0 && content.some(c => c.optimization_scores?.overall_score)
? (content
.filter(c => c.optimization_scores?.overall_score)
.reduce((sum, c) => sum + (c.optimization_scores?.overall_score || 0), 0) /
content.filter(c => c.optimization_scores?.overall_score).length
).toFixed(1)
: '-',
subtitle: `${content.filter(c => c.optimization_scores?.overall_score && c.optimization_scores.overall_score >= 80).length} high score`,
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'green',
},
]}
progress={{
label: 'Content Optimization Progress',
value: totalCount > 0
? Math.round((content.filter(c => c.optimizer_version && c.optimizer_version > 0).length / totalCount) * 100)
: 0,
color: 'warning',
}}
/>
{/* Module footer placeholder - module on hold */}
</div>
</>
);

View File

@@ -29,7 +29,7 @@ import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
export default function Clusters() {
const toast = useToast();
@@ -560,8 +560,8 @@ export default function Clusters() {
}}
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Workflow Widget */}
<StandardThreeWidgetFooter
submoduleColor="green"
pageProgress={{
title: 'Page Progress',
@@ -580,62 +580,15 @@ export default function Clusters() {
hint: totalReady > 0
? `${totalReady} clusters ready for idea generation`
: 'All clusters have ideas!',
statusInsight: totalReady > 0
? `Select clusters and generate ideas to create content topics.`
: totalWithIdeas > 0
? `Ideas generated. Go to Ideas page to queue them for writing.`
: `No clusters yet. Run clustering on Keywords page first.`,
}}
moduleStats={{
title: 'Planner Module',
pipeline: [
{
fromLabel: 'Keywords',
fromValue: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0),
fromHref: '/planner/keywords',
actionLabel: 'Auto Cluster',
toLabel: 'Clusters',
toValue: totalCount,
progress: 100,
color: 'blue',
},
{
fromLabel: 'Clusters',
fromValue: totalCount,
actionLabel: 'Generate Ideas',
toLabel: 'Ideas',
toValue: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0),
toHref: '/planner/ideas',
progress: totalCount > 0 ? Math.round((clusters.filter(c => (c.ideas_count || 0) > 0).length / totalCount) * 100) : 0,
color: 'green',
},
{
fromLabel: 'Ideas',
fromValue: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0),
fromHref: '/planner/ideas',
actionLabel: 'Create Tasks',
toLabel: 'Tasks',
toValue: 0,
toHref: '/writer/tasks',
progress: 0,
color: 'amber',
},
],
links: [
{ label: 'Keywords', href: '/planner/keywords' },
{ label: 'Clusters', href: '/planner/clusters' },
{ label: 'Ideas', href: '/planner/ideas' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0), color: 'blue' },
{ label: 'Clusters Created', value: totalCount, color: 'green' },
{ label: 'Ideas Generated', value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0), color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: 0, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="planner"
showCredits={true}
analyticsHref="/account/usage"
/>
{/* Progress Modal for AI Functions */}

View File

@@ -30,7 +30,7 @@ import { createIdeasPageConfig } from '../../config/pages/ideas.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
export default function Ideas() {
const toast = useToast();
@@ -483,8 +483,8 @@ export default function Ideas() {
}}
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Workflow Widget */}
<StandardThreeWidgetFooter
submoduleColor="amber"
pageProgress={{
title: 'Page Progress',
@@ -503,62 +503,15 @@ export default function Ideas() {
hint: totalPending > 0
? `${totalPending} ideas ready to become tasks`
: 'All ideas converted!',
statusInsight: totalPending > 0
? `Select ideas and queue them to Writer to start content generation.`
: totalInTasks > 0
? `Ideas queued. Go to Writer Tasks to generate content.`
: `No ideas yet. Generate ideas from Clusters page.`,
}}
moduleStats={{
title: 'Planner Module',
pipeline: [
{
fromLabel: 'Keywords',
fromValue: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0),
fromHref: '/planner/keywords',
actionLabel: 'Auto Cluster',
toLabel: 'Clusters',
toValue: clusters.length,
toHref: '/planner/clusters',
progress: 100,
color: 'blue',
},
{
fromLabel: 'Clusters',
fromValue: clusters.length,
fromHref: '/planner/clusters',
actionLabel: 'Generate Ideas',
toLabel: 'Ideas',
toValue: totalCount,
progress: 100,
color: 'green',
},
{
fromLabel: 'Ideas',
fromValue: totalCount,
actionLabel: 'Create Tasks',
toLabel: 'Tasks',
toValue: ideas.filter(i => i.status === 'queued' || i.status === 'completed').length,
toHref: '/writer/tasks',
progress: totalCount > 0 ? Math.round((ideas.filter(i => i.status === 'queued' || i.status === 'completed').length / totalCount) * 100) : 0,
color: 'amber',
},
],
links: [
{ label: 'Keywords', href: '/planner/keywords' },
{ label: 'Clusters', href: '/planner/clusters' },
{ label: 'Ideas', href: '/planner/ideas' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0), color: 'blue' },
{ label: 'Clusters Created', value: clusters.length, color: 'green' },
{ label: 'Ideas Generated', value: totalCount, color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: ideas.filter(i => i.status === 'completed').length, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="planner"
showCredits={true}
analyticsHref="/account/usage"
/>
{/* Progress Modal for AI Functions */}

View File

@@ -26,7 +26,7 @@ import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
import FormModal from '../../components/common/FormModal';
import ProgressModal from '../../components/common/ProgressModal';
@@ -780,8 +780,8 @@ export default function Keywords() {
}}
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Workflow Widget */}
<StandardThreeWidgetFooter
submoduleColor="blue"
pageProgress={{
title: 'Page Progress',
@@ -800,65 +800,15 @@ export default function Keywords() {
hint: totalUnmapped > 0
? `${totalUnmapped} keywords ready to cluster`
: 'All keywords clustered!',
statusInsight: totalUnmapped > 0
? `Select unmapped keywords and run clustering to group them into topics.`
: totalClustered > 0
? `Keywords are clustered. Go to Clusters to generate content ideas.`
: `Add keywords to begin. Import from CSV or add manually.`,
}}
moduleStats={{
title: 'Planner Module',
pipeline: [
{
fromLabel: 'Keywords',
fromValue: totalCount,
actionLabel: 'Auto Cluster',
toLabel: 'Clusters',
toValue: clusters.length,
toHref: '/planner/clusters',
progress: totalCount > 0 ? Math.round((keywords.filter(k => k.cluster_id).length / totalCount) * 100) : 0,
color: 'blue',
},
{
fromLabel: 'Clusters',
fromValue: clusters.length,
fromHref: '/planner/clusters',
actionLabel: 'Generate Ideas',
toLabel: 'Ideas',
toValue: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0),
toHref: '/planner/ideas',
progress: clusters.length > 0 ? Math.round((clusters.filter(c => (c.ideas_count || 0) > 0).length / clusters.length) * 100) : 0,
color: 'green',
},
{
fromLabel: 'Ideas',
fromValue: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0),
fromHref: '/planner/ideas',
actionLabel: 'Create Tasks',
toLabel: 'Tasks',
toValue: 0,
toHref: '/writer/tasks',
progress: 0,
color: 'amber',
},
],
links: [
{ label: 'Keywords', href: '/planner/keywords' },
{ label: 'Clusters', href: '/planner/clusters' },
{ label: 'Ideas', href: '/planner/ideas' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: keywords.filter(k => k.cluster_id).length, color: 'blue' },
{ label: 'Clusters Created', value: clusters.length, color: 'green' },
{ label: 'Ideas Generated', value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0), color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: 0, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
creditsUsed: 0,
operationsCount: 0,
analyticsHref: '/account/usage',
}}
module="planner"
showCredits={true}
analyticsHref="/account/usage"
/>
{/* Create/Edit Modal */}

View File

@@ -24,7 +24,7 @@ import { createApprovedPageConfig } from '../../config/pages/approved.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
export default function Approved() {
const toast = useToast();
@@ -417,7 +417,7 @@ export default function Approved() {
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
<StandardThreeWidgetFooter
submoduleColor="green"
pageProgress={{
title: 'Page Progress',
@@ -435,65 +435,13 @@ export default function Approved() {
hint: content.filter(c => !c.external_id).length > 0
? `${content.filter(c => !c.external_id).length} article${content.filter(c => !c.external_id).length !== 1 ? 's' : ''} pending sync to site`
: 'All articles synced to site!',
statusInsight: content.filter(c => !c.external_id).length > 0
? `Select articles and publish to your WordPress site.`
: totalCount > 0
? `All content published! Check your site for live articles.`
: `No approved content. Approve articles from Review page.`,
}}
moduleStats={{
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: 0,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: 0,
toHref: '/writer/content',
progress: 0,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: 0,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: totalImagesCount,
toHref: '/writer/images',
progress: 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: 0,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: totalCount,
toHref: '/writer/published',
progress: 100,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/published' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: 0, color: 'blue' },
{ label: 'Clusters Created', value: 0, color: 'green' },
{ label: 'Ideas Generated', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: 0, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Articles Published', value: totalCount, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="writer"
/>
</>
);

View File

@@ -24,7 +24,7 @@ import { usePageSizeStore } from '../../store/pageSizeStore';
import ProgressModal from '../../components/common/ProgressModal';
import { useProgressModal } from '../../hooks/useProgressModal';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
import { PencilSquareIcon } from '@heroicons/react/24/outline';
export default function Content() {
@@ -348,83 +348,35 @@ export default function Content() {
getItemDisplayName={(row: ContentType) => row.title || `Content #${row.id}`}
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Workflow Widget */}
<StandardThreeWidgetFooter
submoduleColor="blue"
pageProgress={{
title: 'Page Progress',
submoduleColor: 'blue',
metrics: [
{ label: 'Drafts', value: content.filter(c => c.status === 'draft').length },
{ label: 'Has Images', value: content.filter(c => c.has_generated_images).length, percentage: `${content.filter(c => c.status === 'draft').length > 0 ? Math.round((content.filter(c => c.has_generated_images).length / content.filter(c => c.status === 'draft').length) * 100) : 0}%` },
{ label: 'In Review', value: content.filter(c => c.status === 'review').length },
{ label: 'Published', value: content.filter(c => c.status === 'published').length },
{ label: 'Drafts', value: totalDraft },
{ label: 'Has Images', value: content.filter(c => c.has_generated_images).length, percentage: `${totalDraft > 0 ? Math.round((content.filter(c => c.has_generated_images).length / totalDraft) * 100) : 0}%` },
{ label: 'In Review', value: totalReview },
{ label: 'Published', value: totalPublished },
],
progress: {
value: content.filter(c => c.status === 'draft').length > 0 ? Math.round((content.filter(c => c.has_generated_images).length / content.filter(c => c.status === 'draft').length) * 100) : 0,
value: totalDraft > 0 ? Math.round((content.filter(c => c.has_generated_images).length / totalDraft) * 100) : 0,
label: 'Have Images',
color: 'blue',
},
hint: content.filter(c => c.status === 'draft' && !c.has_generated_images).length > 0
? `${content.filter(c => c.status === 'draft' && !c.has_generated_images).length} drafts need images before review`
: 'All drafts have images!',
statusInsight: content.filter(c => c.status === 'draft' && !c.has_generated_images).length > 0
? `Generate images for drafts, then submit to Review.`
: totalDraft > 0
? `Select drafts and submit to Review for approval.`
: `No drafts. Generate content from Tasks page.`,
}}
moduleStats={{
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: totalCount,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: content.filter(c => c.status === 'draft').length,
progress: 100,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: content.filter(c => c.status === 'draft').length,
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: content.filter(c => c.has_generated_images).length,
toHref: '/writer/images',
progress: content.filter(c => c.status === 'draft').length > 0 ? Math.round((content.filter(c => c.has_generated_images).length / content.filter(c => c.status === 'draft').length) * 100) : 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: content.filter(c => c.status === 'review').length,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: content.filter(c => c.status === 'published').length,
toHref: '/writer/published',
progress: content.filter(c => c.status === 'review').length > 0 ? Math.round((content.filter(c => c.status === 'published').length / (content.filter(c => c.status === 'review').length + content.filter(c => c.status === 'published').length)) * 100) : 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/published' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: 0, color: 'blue' },
{ label: 'Clusters Created', value: 0, color: 'green' },
{ label: 'Ideas Generated', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: totalCount, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: content.filter(c => c.status === 'published').length, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="writer"
showCredits={true}
analyticsHref="/account/usage"
/>
{/* Progress Modal for AI Functions */}

View File

@@ -27,7 +27,7 @@ import ImageQueueModal, { ImageQueueItem } from '../../components/common/ImageQu
import SingleRecordStatusUpdateModal from '../../components/common/SingleRecordStatusUpdateModal';
import PageHeader from '../../components/common/PageHeader';
import { Modal } from '../../components/ui/modal';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
export default function Images() {
const toast = useToast();
@@ -664,84 +664,35 @@ export default function Images() {
)}
</Modal>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Workflow Widget */}
<StandardThreeWidgetFooter
submoduleColor="purple"
pageProgress={{
title: 'Page Progress',
submoduleColor: 'purple',
metrics: [
{ label: 'Total', value: totalCount },
{ label: 'Generated', value: images.filter(i => i.images?.some(img => img.status === 'generated')).length, percentage: `${totalCount > 0 ? Math.round((images.filter(i => i.images?.some(img => img.status === 'generated')).length / totalCount) * 100) : 0}%` },
{ label: 'Pending', value: images.filter(i => i.images?.some(img => img.status === 'pending')).length },
{ label: 'Content Items', value: totalCount },
{ label: 'Complete', value: totalComplete, percentage: `${totalCount > 0 ? Math.round((totalComplete / totalCount) * 100) : 0}%` },
{ label: 'Partial', value: totalPartial },
{ label: 'Pending', value: totalPending },
],
progress: {
value: totalCount > 0 ? Math.round((images.filter(i => i.images?.some(img => img.status === 'generated')).length / totalCount) * 100) : 0,
label: 'Generated',
value: totalCount > 0 ? Math.round((totalComplete / totalCount) * 100) : 0,
label: 'Complete',
color: 'purple',
},
hint: images.filter(i => i.images?.some(img => img.status === 'pending')).length > 0
? `${images.filter(i => i.images?.some(img => img.status === 'pending')).length} content item${images.filter(i => i.images?.some(img => img.status === 'pending')).length !== 1 ? 's' : ''} need image generation`
hint: totalPending > 0
? `${totalPending} content item${totalPending !== 1 ? 's' : ''} need image generation`
: 'All images generated!',
statusInsight: totalPending > 0
? `Select content items and generate images for articles.`
: totalComplete > 0
? `Images ready. Submit content to Review for publishing.`
: `No content with image prompts. Generate content first.`,
}}
moduleStats={{
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: 0,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: 0,
toHref: '/writer/content',
progress: 0,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: 0,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: totalImagesCount,
toHref: '/writer/images',
progress: 100,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: 0,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: 0,
toHref: '/writer/published',
progress: 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/published' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: 0, color: 'blue' },
{ label: 'Clusters Created', value: 0, color: 'green' },
{ label: 'Ideas Generated', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: 0, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Articles Published', value: 0, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="writer"
showCredits={true}
analyticsHref="/account/usage"
/>
</>
);

View File

@@ -21,7 +21,7 @@ import { createReviewPageConfig } from '../../config/pages/review.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
export default function Review() {
const toast = useToast();
@@ -487,7 +487,7 @@ export default function Review() {
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
<StandardThreeWidgetFooter
submoduleColor="amber"
pageProgress={{
title: 'Page Progress',
@@ -504,65 +504,11 @@ export default function Review() {
hint: totalCount > 0
? `${totalCount} article${totalCount !== 1 ? 's' : ''} ready for review and publishing`
: 'No content pending review',
statusInsight: totalCount > 0
? `Review content, edit if needed, then approve for publishing.`
: `No content in review. Submit drafts from Content page.`,
}}
moduleStats={{
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: totalTasks,
fromHref: '/writer/tasks',
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: totalDrafts,
toHref: '/writer/content',
progress: totalTasks > 0 ? Math.round((totalDrafts / totalTasks) * 100) : 0,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: totalDrafts,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: totalImagesCount,
toHref: '/writer/images',
progress: totalDrafts > 0 ? Math.round((totalImagesCount / totalDrafts) * 100) : 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: totalCount,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: totalApproved,
toHref: '/writer/approved',
progress: totalCount > 0 ? Math.round((totalApproved / (totalCount + totalApproved)) * 100) : 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/approved' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: 0, color: 'blue' },
{ label: 'Clusters Created', value: 0, color: 'green' },
{ label: 'Ideas Generated', value: 0, color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: totalDrafts + totalCount + totalApproved, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Articles Published', value: totalApproved, color: 'green' },
],
analyticsHref: '/account/usage',
}}
module="writer"
/>
</>
);

View File

@@ -31,7 +31,7 @@ import { createTasksPageConfig } from '../../config/pages/tasks.config';
import { useSectorStore } from '../../store/sectorStore';
import { usePageSizeStore } from '../../store/pageSizeStore';
import PageHeader from '../../components/common/PageHeader';
import ThreeWidgetFooter from '../../components/dashboard/ThreeWidgetFooter';
import StandardThreeWidgetFooter from '../../components/dashboard/StandardThreeWidgetFooter';
import { DocumentTextIcon } from '@heroicons/react/24/outline';
export default function Tasks() {
@@ -546,84 +546,35 @@ export default function Tasks() {
}}
/>
{/* Three Widget Footer - Section 3 Layout */}
<ThreeWidgetFooter
{/* Three Widget Footer - Section 3 Layout with Standardized Widgets */}
<StandardThreeWidgetFooter
submoduleColor="blue"
module="writer"
pageProgress={{
title: 'Page Progress',
submoduleColor: 'blue',
metrics: [
{ label: 'Total', value: totalCount },
{ label: 'Complete', value: tasks.filter(t => t.status === 'completed').length, percentage: `${totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0}%` },
{ label: 'Queue', value: tasks.filter(t => t.status === 'queued').length },
{ label: 'Processing', value: tasks.filter(t => t.status === 'in_progress').length },
{ label: 'Complete', value: totalCompleted, percentage: `${totalCount > 0 ? Math.round((totalCompleted / totalCount) * 100) : 0}%` },
{ label: 'Queue', value: totalQueued },
{ label: 'Processing', value: totalProcessing },
],
progress: {
value: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
value: totalCount > 0 ? Math.round((totalCompleted / totalCount) * 100) : 0,
label: 'Generated',
color: 'blue',
},
hint: tasks.filter(t => t.status === 'queued').length > 0
? `${tasks.filter(t => t.status === 'queued').length} tasks in queue for content generation`
hint: totalQueued > 0
? `${totalQueued} tasks in queue for content generation`
: 'All tasks processed!',
statusInsight: totalQueued > 0
? `Select tasks and run content generation to create articles.`
: totalCompleted > 0
? `Content generated. Review drafts on the Content page.`
: `No tasks yet. Queue ideas from the Planner Ideas page.`,
}}
moduleStats={{
title: 'Writer Module',
pipeline: [
{
fromLabel: 'Tasks',
fromValue: totalCount,
actionLabel: 'Generate Content',
toLabel: 'Drafts',
toValue: tasks.filter(t => t.status === 'completed').length,
toHref: '/writer/content',
progress: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
color: 'blue',
},
{
fromLabel: 'Drafts',
fromValue: tasks.filter(t => t.status === 'completed').length,
fromHref: '/writer/content',
actionLabel: 'Generate Images',
toLabel: 'Images',
toValue: totalImagesCount,
toHref: '/writer/images',
progress: 0,
color: 'purple',
},
{
fromLabel: 'Ready',
fromValue: 0,
fromHref: '/writer/review',
actionLabel: 'Review & Publish',
toLabel: 'Published',
toValue: 0,
toHref: '/writer/published',
progress: 0,
color: 'green',
},
],
links: [
{ label: 'Tasks', href: '/writer/tasks' },
{ label: 'Content', href: '/writer/content' },
{ label: 'Images', href: '/writer/images' },
{ label: 'Published', href: '/writer/published' },
],
}}
completion={{
title: 'Workflow Completion',
plannerItems: [
{ label: 'Keywords Clustered', value: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0), color: 'blue' },
{ label: 'Clusters Created', value: clusters.length, color: 'green' },
{ label: 'Ideas Generated', value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0), color: 'amber' },
],
writerItems: [
{ label: 'Content Generated', value: tasks.filter(t => t.status === 'completed').length, color: 'blue' },
{ label: 'Images Created', value: totalImagesCount, color: 'purple' },
{ label: 'Published', value: 0, color: 'green' },
],
analyticsHref: '/account/usage',
}}
showCredits={true}
analyticsHref="/account/usage"
/>
{/* Progress Modal for AI Functions */}

View File

@@ -0,0 +1,260 @@
/**
* Privacy Policy Page
*/
import { Link } from 'react-router-dom';
import PageMeta from '../../components/common/PageMeta';
import { ChevronLeftIcon } from '../../icons';
export default function Privacy() {
return (
<>
<PageMeta
title="Privacy Policy - IGNY8"
description="IGNY8 Privacy Policy - Learn how we protect your data"
/>
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
<div className="max-w-4xl mx-auto px-4 py-12 sm:px-6 lg:px-8">
{/* Back Link */}
<Link
to="/"
className="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 mb-8"
>
<ChevronLeftIcon className="size-5" />
Back to Home
</Link>
{/* Header */}
<div className="text-center mb-12">
<Link to="/" className="inline-block mb-6">
<img
src="/igny8-logo-trnsp.png"
alt="IGNY8"
className="h-12 w-auto mx-auto"
/>
</Link>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Privacy Policy
</h1>
<p className="text-gray-500 dark:text-gray-400">
Last updated: December 31, 2024
</p>
</div>
{/* Content */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-8 sm:p-12 space-y-8">
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
1. Introduction
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
At IGNY8, we take your privacy seriously. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you use our AI-powered content creation platform. Please read this privacy policy carefully.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
2. Information We Collect
</h2>
<h3 className="text-lg font-medium text-gray-800 dark:text-gray-200 mb-3">
Personal Information
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We may collect personal information that you voluntarily provide when registering for an account, including:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4 mb-4">
<li>Name and email address</li>
<li>Account credentials</li>
<li>Billing information and payment details</li>
<li>Company/organization name</li>
<li>Website URLs you connect to our service</li>
</ul>
<h3 className="text-lg font-medium text-gray-800 dark:text-gray-200 mb-3">
Usage Information
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We automatically collect certain information when you use our Service:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Device and browser information</li>
<li>IP address and location data</li>
<li>Pages visited and features used</li>
<li>Content and keywords you create or analyze</li>
<li>Usage patterns and preferences</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
3. How We Use Your Information
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We use the collected information for various purposes:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>To provide and maintain our Service</li>
<li>To process your transactions and manage your subscription</li>
<li>To send you service-related communications</li>
<li>To improve and personalize your experience</li>
<li>To analyze usage patterns and optimize our Service</li>
<li>To protect against fraud and unauthorized access</li>
<li>To comply with legal obligations</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
4. AI-Generated Content
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
When you use our AI content generation features:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Your input prompts may be processed by third-party AI providers</li>
<li>Generated content is stored in your account</li>
<li>We do not use your specific content to train AI models</li>
<li>You retain ownership of the content you generate</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
5. Data Sharing and Disclosure
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We may share your information with:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li><strong>Service Providers:</strong> Third parties that help us operate our Service (payment processors, hosting providers, AI service providers)</li>
<li><strong>Analytics Partners:</strong> To help us understand usage patterns</li>
<li><strong>Legal Requirements:</strong> When required by law or to protect our rights</li>
<li><strong>Business Transfers:</strong> In connection with a merger, acquisition, or sale of assets</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mt-4">
We do not sell your personal information to third parties.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
6. Data Security
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We implement appropriate technical and organizational measures to protect your data:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Encryption of data in transit and at rest</li>
<li>Regular security audits and updates</li>
<li>Access controls and authentication measures</li>
<li>Employee training on data protection</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
7. Data Retention
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
We retain your personal information for as long as your account is active or as needed to provide you services. We may retain certain information as required by law or for legitimate business purposes such as fraud prevention and analytics.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
8. Your Rights
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
Depending on your location, you may have the following rights:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li><strong>Access:</strong> Request a copy of your personal data</li>
<li><strong>Correction:</strong> Request correction of inaccurate data</li>
<li><strong>Deletion:</strong> Request deletion of your data</li>
<li><strong>Portability:</strong> Request transfer of your data</li>
<li><strong>Objection:</strong> Object to certain processing activities</li>
<li><strong>Withdrawal:</strong> Withdraw consent where processing is based on consent</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mt-4">
To exercise these rights, contact us at privacy@igny8.com.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
9. Cookies and Tracking
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
We use cookies and similar technologies to:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Keep you logged in</li>
<li>Remember your preferences</li>
<li>Analyze how you use our Service</li>
<li>Improve our Service</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mt-4">
You can manage cookie preferences through your browser settings.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
10. International Data Transfers
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for such transfers in compliance with applicable data protection laws.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
11. Children's Privacy
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
Our Service is not intended for individuals under the age of 18. We do not knowingly collect personal information from children. If we become aware that we have collected personal information from a child, we will take steps to delete such information.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
12. Changes to This Policy
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last updated" date. We encourage you to review this Privacy Policy periodically.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
13. Contact Us
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
If you have questions or concerns about this Privacy Policy, please contact us at:
</p>
<div className="mt-4 space-y-2">
<p className="text-gray-600 dark:text-gray-300">
<strong>Email:</strong>{' '}
<span className="text-brand-500">privacy@igny8.com</span>
</p>
<p className="text-gray-600 dark:text-gray-300">
<strong>General Inquiries:</strong>{' '}
<span className="text-brand-500">support@igny8.com</span>
</p>
</div>
</section>
</div>
{/* Footer Links */}
<div className="mt-8 text-center text-sm text-gray-500 dark:text-gray-400">
<Link to="/terms" className="hover:text-brand-500 mr-4">
Terms and Conditions
</Link>
<Link to="/signup" className="hover:text-brand-500">
Back to Sign Up
</Link>
</div>
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,186 @@
/**
* Terms and Conditions Page
*/
import { Link } from 'react-router-dom';
import PageMeta from '../../components/common/PageMeta';
import { ChevronLeftIcon } from '../../icons';
export default function Terms() {
return (
<>
<PageMeta
title="Terms and Conditions - IGNY8"
description="IGNY8 Terms and Conditions - Read our terms of service"
/>
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
<div className="max-w-4xl mx-auto px-4 py-12 sm:px-6 lg:px-8">
{/* Back Link */}
<Link
to="/"
className="inline-flex items-center text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 mb-8"
>
<ChevronLeftIcon className="size-5" />
Back to Home
</Link>
{/* Header */}
<div className="text-center mb-12">
<Link to="/" className="inline-block mb-6">
<img
src="/igny8-logo-trnsp.png"
alt="IGNY8"
className="h-12 w-auto mx-auto"
/>
</Link>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Terms and Conditions
</h1>
<p className="text-gray-500 dark:text-gray-400">
Last updated: December 31, 2024
</p>
</div>
{/* Content */}
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-8 sm:p-12 space-y-8">
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
1. Acceptance of Terms
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
By accessing and using IGNY8 ("the Service"), you accept and agree to be bound by the terms and provision of this agreement. If you do not agree to abide by these terms, please do not use this Service.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
2. Description of Service
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
IGNY8 is an AI-powered content creation and SEO optimization platform that provides:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Keyword research and clustering tools</li>
<li>AI-generated content creation</li>
<li>Image generation capabilities</li>
<li>Content optimization features</li>
<li>WordPress publishing integration</li>
<li>Analytics and reporting</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
3. User Accounts
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
To use certain features of the Service, you must register for an account. When you register:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>You agree to provide accurate, current, and complete information</li>
<li>You are responsible for maintaining the security of your account</li>
<li>You are responsible for all activities that occur under your account</li>
<li>You must notify us immediately of any unauthorized use of your account</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
4. Billing and Credits
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
Our Service operates on a credit-based system:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Credits are consumed when using AI features</li>
<li>Unused credits may expire according to your plan terms</li>
<li>Refunds are subject to our refund policy</li>
<li>Prices are subject to change with notice</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
5. Acceptable Use
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
You agree not to use the Service to:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>Generate illegal, harmful, or offensive content</li>
<li>Violate intellectual property rights</li>
<li>Attempt to circumvent usage limits or security measures</li>
<li>Resell or redistribute the Service without authorization</li>
<li>Use automated systems to access the Service in a manner that exceeds reasonable usage</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
6. Intellectual Property
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-4">
Content generated through our Service:
</p>
<ul className="list-disc list-inside text-gray-600 dark:text-gray-300 space-y-2 ml-4">
<li>You retain ownership of content you create using the Service</li>
<li>You grant us a license to process your content to provide the Service</li>
<li>AI-generated content is provided "as-is" and you are responsible for reviewing and editing</li>
<li>The Service itself, including all code, designs, and features, remains our property</li>
</ul>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
7. Limitation of Liability
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
IGNY8 and its affiliates shall not be liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use of the Service. Our total liability shall not exceed the amount you paid for the Service in the twelve months preceding the claim.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
8. Termination
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
We may terminate or suspend your access to the Service immediately, without prior notice, for conduct that we believe violates these Terms or is harmful to other users, us, or third parties, or for any other reason at our sole discretion.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
9. Changes to Terms
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
We reserve the right to modify these terms at any time. We will notify users of any material changes via email or through the Service. Your continued use of the Service after such modifications constitutes acceptance of the updated terms.
</p>
</section>
<section>
<h2 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
10. Contact Information
</h2>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
If you have any questions about these Terms, please contact us at:
</p>
<p className="text-brand-500 mt-2">
support@igny8.com
</p>
</section>
</div>
{/* Footer Links */}
<div className="mt-8 text-center text-sm text-gray-500 dark:text-gray-400">
<Link to="/privacy" className="hover:text-brand-500 mr-4">
Privacy Policy
</Link>
<Link to="/signup" className="hover:text-brand-500">
Back to Sign Up
</Link>
</div>
</div>
</div>
</>
);
}