final polish 3
This commit is contained in:
@@ -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]);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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[]>([]);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user