final polish 3

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-27 15:55:54 +00:00
parent b9e4b6f7e2
commit 3ea7d4f933
22 changed files with 958 additions and 9 deletions

View File

@@ -8,7 +8,7 @@ import { useSiteStore } from '../../store/siteStore';
import { useSectorStore } from '../../store/sectorStore';
import { trackLoading } from './LoadingStateMonitor';
import { useErrorHandler } from '../../hooks/useErrorHandler';
import { usePageContext } from '../../context/PageContext';
import { usePageContext, SelectorVisibility } from '../../context/PageContext';
interface PageHeaderProps {
title: string;
@@ -28,6 +28,13 @@ interface PageHeaderProps {
workflowInsights?: any[]; // Kept for backwards compat but not rendered
/** Right-side actions slot */
actions?: ReactNode;
/**
* Controls site/sector selector visibility in AppHeader per audit Section 1:
* - 'both': Show both site and sector selectors (Planner, Writer pages) - DEFAULT
* - 'site-only': Show only site selector (Automation page)
* - 'none': Hide both selectors (Account, Billing, Thinker pages)
*/
selectorVisibility?: SelectorVisibility;
}
export default function PageHeader({
@@ -42,6 +49,7 @@ export default function PageHeader({
badge,
hideSiteSector = false,
actions,
selectorVisibility = 'both',
}: PageHeaderProps) {
const { activeSite } = useSiteStore();
const { loadSectorsForSite } = useSectorStore();
@@ -54,9 +62,9 @@ export default function PageHeader({
const parentModule = parent || breadcrumb;
// Update page context with title and badge info for AppHeader
const pageInfoKey = useMemo(() => `${title}|${parentModule}`, [title, parentModule]);
const pageInfoKey = useMemo(() => `${title}|${parentModule}|${selectorVisibility}`, [title, parentModule, selectorVisibility]);
useEffect(() => {
setPageInfo({ title, parent: parentModule, badge });
setPageInfo({ title, parent: parentModule, badge, selectorVisibility });
return () => setPageInfo(null);
}, [pageInfoKey, badge?.color]);

View File

@@ -1,9 +1,19 @@
/**
* Page Context - Shares current page info with header
* Allows pages to set title, parent module, badge for display in AppHeader
* Also controls page-specific visibility of site/sector selectors
*/
import React, { createContext, useContext, useState, ReactNode } from 'react';
/**
* Selector visibility configuration per audit Section 1 requirements:
* - 'both': Show both site and sector selectors (Planner, Writer pages)
* - 'site-only': Show only site selector (Automation page)
* - 'none': Hide both selectors (Account, Billing, Thinker pages)
* Default: 'both' (for backward compatibility)
*/
export type SelectorVisibility = 'both' | 'site-only' | 'none';
interface PageInfo {
title: string;
parent?: string; // Parent module name (e.g., "Planner", "Writer")
@@ -11,6 +21,8 @@ interface PageInfo {
icon: ReactNode;
color: 'blue' | 'green' | 'purple' | 'orange' | 'red' | 'indigo' | 'yellow' | 'pink' | 'emerald' | 'cyan' | 'amber' | 'teal';
};
/** Controls site/sector selector visibility in AppHeader. Default: 'both' */
selectorVisibility?: SelectorVisibility;
}
interface PageContextType {

View File

@@ -117,10 +117,14 @@ const AppHeader: React.FC = () => {
{/* Header Metrics */}
<HeaderMetrics />
{/* Site and Sector Selector - Desktop */}
<div className="hidden lg:flex items-center">
<SiteAndSectorSelector />
</div>
{/* Site and Sector Selector - Desktop (visibility controlled by page context) */}
{pageInfo?.selectorVisibility !== 'none' && (
<div className="hidden lg:flex items-center">
<SiteAndSectorSelector
hideSectorSelector={pageInfo?.selectorVisibility === 'site-only'}
/>
</div>
)}
{/* Search Icon */}
<button

View File

@@ -5,6 +5,7 @@
import React, { useState, useEffect } from 'react';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { useSiteStore } from '../../store/siteStore';
import { usePageContext } from '../../context/PageContext';
import { automationService, AutomationRun, AutomationConfig, PipelineStage } from '../../services/automationService';
import {
fetchKeywords,
@@ -48,6 +49,7 @@ const STAGE_CONFIG = [
const AutomationPage: React.FC = () => {
const { activeSite } = useSiteStore();
const { setPageInfo } = usePageContext();
const toast = useToast();
const [config, setConfig] = useState<AutomationConfig | null>(null);
const [currentRun, setCurrentRun] = useState<AutomationRun | null>(null);
@@ -58,6 +60,16 @@ const AutomationPage: React.FC = () => {
const [loading, setLoading] = useState(true);
const [estimate, setEstimate] = useState<{ estimated_credits: number; current_balance: number; sufficient: boolean } | null>(null);
// Set page context for AppHeader - site-only selector per audit Section 1
useEffect(() => {
setPageInfo({
title: 'Automation',
badge: { icon: <BoltIcon />, color: 'teal' },
selectorVisibility: 'site-only',
});
return () => setPageInfo(null);
}, [setPageInfo]);
useEffect(() => {
if (!activeSite) return;
loadData();

View File

@@ -10,6 +10,7 @@ import PageHeader from "../../components/common/PageHeader";
import WorkflowGuide from "../../components/onboarding/WorkflowGuide";
import { useOnboardingStore } from "../../store/onboardingStore";
import { useBillingStore } from "../../store/billingStore";
import { usePageContext } from "../../context/PageContext";
import { Card } from "../../components/ui/card";
import { ProgressBar } from "../../components/ui/progress";
import { ApexOptions } from "apexcharts";
@@ -246,6 +247,7 @@ export default function Home() {
const { isGuideDismissed, showGuide, loadFromBackend } = useOnboardingStore();
const { user } = useAuthStore();
const { balance, loadBalance } = useBillingStore();
const { setPageInfo } = usePageContext();
const [insights, setInsights] = useState<AppInsights | null>(null);
const [loading, setLoading] = useState(true);
@@ -259,6 +261,17 @@ export default function Home() {
const [isSiteSelectorOpen, setIsSiteSelectorOpen] = useState(false);
const siteSelectorRef = useRef<HTMLButtonElement>(null);
// Set page context for AppHeader - site-only selector (no sector) per audit Section 1
// Dashboard has its own site selector with "All Sites" option so we hide AppHeader selector
useEffect(() => {
setPageInfo({
title: 'Dashboard',
badge: { icon: <GridIcon />, color: 'blue' },
selectorVisibility: 'site-only',
});
return () => setPageInfo(null);
}, [setPageInfo]);
// Progress tracking state
const [progress, setProgress] = useState({
hasSiteWithSectors: false,

View File

@@ -1,5 +1,6 @@
import { useState, useRef, useEffect } from "react";
import PageMeta from "../../components/common/PageMeta";
import { usePageContext } from "../../context/PageContext";
import { Accordion, AccordionItem } from "../../components/ui/accordion";
import { Card } from "../../components/ui/card";
import Badge from "../../components/ui/badge/Badge";
@@ -9,7 +10,8 @@ import {
CheckCircleIcon,
ArrowRightIcon,
FileIcon,
GroupIcon
GroupIcon,
DocsIcon
} from "../../icons";
interface TableOfContentsItem {
@@ -21,6 +23,17 @@ interface TableOfContentsItem {
export default function Help() {
const [activeSection, setActiveSection] = useState<string | null>(null);
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({});
const { setPageInfo } = usePageContext();
// Set page context for AppHeader - no selectors for help pages per audit Section 1
useEffect(() => {
setPageInfo({
title: 'Help & Documentation',
badge: { icon: <DocsIcon />, color: 'cyan' },
selectorVisibility: 'none',
});
return () => setPageInfo(null);
}, [setPageInfo]);
const tableOfContents: TableOfContentsItem[] = [
{ id: "getting-started", title: "Getting Started", level: 1 },

View File

@@ -105,6 +105,7 @@ export default function AuthorProfiles() {
title="Writing Styles"
badge={{ icon: <UserIcon />, color: 'blue' }}
breadcrumb="Thinker / Author Profiles"
selectorVisibility="none"
/>
<div className="mb-6 flex justify-between items-center">
<Button onClick={handleCreate} variant="primary">

View File

@@ -147,7 +147,10 @@ export default function ThinkerDashboard() {
return (
<>
<PageMeta title="Strategy Dashboard - IGNY8" description="Manage your content strategy" />
<PageHeader title="Strategy Dashboard" />
<PageHeader
title="Strategy Dashboard"
selectorVisibility="none"
/>
<div className="space-y-6">
{/* Key Metrics */}

View File

@@ -11,6 +11,7 @@ export default function ImageTesting() {
title="Image Settings"
badge={{ icon: <ImageIcon />, color: 'indigo' }}
breadcrumb="Thinker / Image Testing"
selectorVisibility="none"
/>
<ComponentCard title="Coming Soon" desc="AI image testing">
<div className="text-center py-8">

View File

@@ -205,6 +205,7 @@ export default function Prompts() {
title="Prompt Library"
badge={{ icon: <BoltIcon />, color: 'orange' }}
breadcrumb="Thinker / Prompts"
selectorVisibility="none"
/>
<div className="p-6">

View File

@@ -11,6 +11,7 @@ export default function Strategies() {
title="Content Plans"
badge={{ icon: <ShootingStarIcon />, color: 'purple' }}
breadcrumb="Thinker / Strategies"
selectorVisibility="none"
/>
<ComponentCard title="Coming Soon" desc="Content strategies">
<div className="text-center py-8">

View File

@@ -13,6 +13,7 @@ import { Card } from '../../components/ui/card';
import Button from '../../components/ui/button/Button';
import Badge from '../../components/ui/badge/Badge';
import PageMeta from '../../components/common/PageMeta';
import { usePageContext } from '../../context/PageContext';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { useAuthStore } from '../../store/authStore';
import {
@@ -40,6 +41,7 @@ export default function AccountSettingsPage() {
const toast = useToast();
const location = useLocation();
const { user, refreshUser } = useAuthStore();
const { setPageInfo } = usePageContext();
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [loading, setLoading] = useState(true);
@@ -47,6 +49,16 @@ export default function AccountSettingsPage() {
const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<string>('');
// Set page context for AppHeader - no selectors for account pages per audit Section 1
useEffect(() => {
setPageInfo({
title: 'Account Settings',
badge: { icon: <Settings className="w-4 h-4" />, color: 'indigo' },
selectorVisibility: 'none',
});
return () => setPageInfo(null);
}, [setPageInfo]);
// Account settings state
const [settings, setSettings] = useState<AccountSettings | null>(null);
const [accountForm, setAccountForm] = useState({

View File

@@ -16,6 +16,7 @@ import { Card } from '../../components/ui/card';
import Badge from '../../components/ui/badge/Badge';
import Button from '../../components/ui/button/Button';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { usePageContext } from '../../context/PageContext';
import { PricingPlan } from '../../components/ui/pricing-table';
import PricingTable1 from '../../components/ui/pricing-table/pricing-table-1';
import CreditCostBreakdownPanel from '../../components/billing/CreditCostBreakdownPanel';
@@ -59,6 +60,7 @@ function getTabFromPath(pathname: string): TabType {
export default function PlansAndBillingPage() {
const location = useLocation();
const { setPageInfo } = usePageContext();
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [loading, setLoading] = useState(true);
@@ -67,6 +69,16 @@ export default function PlansAndBillingPage() {
const [purchaseLoadingId, setPurchaseLoadingId] = useState<number | null>(null);
const [showCancelConfirm, setShowCancelConfirm] = useState(false);
// Set page context for AppHeader - no selectors for billing pages per audit Section 1
useEffect(() => {
setPageInfo({
title: 'Plans & Billing',
badge: { icon: <CreditCard className="w-4 h-4" />, color: 'purple' },
selectorVisibility: 'none',
});
return () => setPageInfo(null);
}, [setPageInfo]);
// Data states
const [creditBalance, setCreditBalance] = useState<CreditBalance | null>(null);
const [packages, setPackages] = useState<CreditPackage[]>([]);

View File

@@ -8,6 +8,7 @@ import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { TrendingUp, Activity, BarChart3, Zap, Calendar } from 'lucide-react';
import PageMeta from '../../components/common/PageMeta';
import { usePageContext } from '../../context/PageContext';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { getUsageAnalytics, UsageAnalytics, getCreditBalance, type CreditBalance } from '../../services/billing.api';
import { Card } from '../../components/ui/card';
@@ -28,6 +29,7 @@ function getTabFromPath(pathname: string): TabType {
export default function UsageAnalyticsPage() {
const toast = useToast();
const location = useLocation();
const { setPageInfo } = usePageContext();
// Derive active tab from URL path
const activeTab = getTabFromPath(location.pathname);
const [analytics, setAnalytics] = useState<UsageAnalytics | null>(null);
@@ -35,6 +37,16 @@ export default function UsageAnalyticsPage() {
const [loading, setLoading] = useState(true);
const [period, setPeriod] = useState(30);
// Set page context for AppHeader - no selectors for usage pages per audit Section 1
useEffect(() => {
setPageInfo({
title: 'Usage & Analytics',
badge: { icon: <TrendingUp className="w-4 h-4" />, color: 'emerald' },
selectorVisibility: 'none',
});
return () => setPageInfo(null);
}, [setPageInfo]);
useEffect(() => {
loadData();
}, [period]);