From 341650bddc8f8f30791140bde6afdb97ff93b86f Mon Sep 17 00:00:00 2001 From: alorig <220087330+alorig@users.noreply.github.com> Date: Sat, 29 Nov 2025 11:24:35 +0500 Subject: [PATCH] Revert "test if works or revert" This reverts commit e9e0de40d01f1b5d231f9bb1f18a12e57b644818. --- frontend/src/App.tsx | 4 +- .../components/common/ContentImageCell.tsx | 21 ++ frontend/src/config/pages/content.config.tsx | 8 +- frontend/src/config/pages/review.config.tsx | 94 ++++----- frontend/src/hooks/useFeatureFlag.ts | 24 --- frontend/src/layout/AppSidebar.tsx | 2 +- frontend/src/pages/Writer/Content.tsx | 23 ++- .../src/templates/ContentViewTemplate.tsx | 88 +------- master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md | 190 ------------------ master-docs/CONTENT-MANAGER-REMOVAL-TESTS.md | 53 ----- 10 files changed, 85 insertions(+), 422 deletions(-) delete mode 100644 frontend/src/hooks/useFeatureFlag.ts delete mode 100644 master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md delete mode 100644 master-docs/CONTENT-MANAGER-REMOVAL-TESTS.md diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d5cbfe25..3982f674 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -210,8 +210,8 @@ export default function App() { } /> - {/* Writer Module - Redirect dashboard to tasks */} - } /> + {/* Writer Module - Redirect dashboard to content */} + } /> diff --git a/frontend/src/components/common/ContentImageCell.tsx b/frontend/src/components/common/ContentImageCell.tsx index 81302f29..85027bcd 100644 --- a/frontend/src/components/common/ContentImageCell.tsx +++ b/frontend/src/components/common/ContentImageCell.tsx @@ -59,8 +59,29 @@ export default function ContentImageCell({ image, maxPromptLength = 100 }: Conte ); } + const prompt = image.prompt || ''; + const shouldTruncate = prompt.length > maxPromptLength; + const displayPrompt = showFullPrompt || !shouldTruncate ? prompt : `${prompt.substring(0, maxPromptLength)}...`; + return (
+ {/* Prompt Text */} + {prompt && ( +
+

+ {displayPrompt} + {shouldTruncate && ( + + )} +

+
+ )} + {/* Image Display */}
{image.status === 'pending' && ( diff --git a/frontend/src/config/pages/content.config.tsx b/frontend/src/config/pages/content.config.tsx index a4f5f480..ec60b83f 100644 --- a/frontend/src/config/pages/content.config.tsx +++ b/frontend/src/config/pages/content.config.tsx @@ -83,10 +83,8 @@ export const createContentPageConfig = ( setStatusFilter: (value: string) => void; setCurrentPage: (page: number) => void; onViewContent?: (row: Content) => void; - enableToggleContent?: boolean; // If false, do not add toggleable content column behavior } ): ContentPageConfig => { - const enableToggle = handlers.enableToggleContent !== false; const showSectorColumn = !handlers.activeSector; const statusColors: Record = { @@ -101,9 +99,9 @@ export const createContentPageConfig = ( ...titleColumn, sortable: true, sortField: 'title', - toggleable: enableToggle, - toggleContentKey: enableToggle ? 'content_html' : undefined, - toggleContentLabel: enableToggle ? 'Generated Content' : undefined, + toggleable: true, + toggleContentKey: 'content_html', + toggleContentLabel: 'Generated Content', render: (value: string, row: Content) => (
{handlers.onViewContent ? ( diff --git a/frontend/src/config/pages/review.config.tsx b/frontend/src/config/pages/review.config.tsx index e801a228..658d9837 100644 --- a/frontend/src/config/pages/review.config.tsx +++ b/frontend/src/config/pages/review.config.tsx @@ -4,7 +4,6 @@ */ import { Content } from '../../services/api'; -import { Link } from 'react-router-dom'; import Badge from '../../components/ui/badge/Badge'; import { formatRelativeDate } from '../../utils/date'; import { CheckCircleIcon } from '../../icons'; @@ -57,61 +56,44 @@ export function createReviewPageConfig(params: { const showSectorColumn = !params.activeSector; const columns: ColumnConfig[] = [ - // Title first, then categories and tags (moved after title per change request) - { - key: 'title', - label: 'Title', - sortable: true, - sortField: 'title', - toggleable: true, - toggleContentKey: 'content_html', - toggleContentLabel: 'Generated Content', - render: (value: string, row: Content) => ( -
- - {value || `Content #${row.id}`} - -
- ), - }, - { - key: 'categories', - label: 'Categories', - sortable: false, - width: '180px', - render: (_value: any, row: Content) => { - const categories = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'category') || []; - if (!categories.length) return -; - return ( -
- {categories.map((cat: any) => ( - {cat.name} - ))} -
- ); - }, - toggleable: false, - defaultVisible: true, - }, - { - key: 'tags', - label: 'Tags', - sortable: false, - width: '180px', - render: (_value: any, row: Content) => { - const tags = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'tag') || []; - if (!tags.length) return -; - return ( -
- {tags.map((tag: any) => ( - {tag.name} - ))} -
- ); - }, - toggleable: false, - defaultVisible: true, - }, + { + key: 'categories', + label: 'Categories', + sortable: false, + width: '180px', + render: (_value: any, row: Content) => { + const categories = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'category') || []; + if (!categories.length) return -; + return ( +
+ {categories.map((cat: any) => ( + {cat.name} + ))} +
+ ); + }, + toggleable: true, + defaultVisible: false, + }, + { + key: 'tags', + label: 'Tags', + sortable: false, + width: '180px', + render: (_value: any, row: Content) => { + const tags = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'tag') || []; + if (!tags.length) return -; + return ( +
+ {tags.map((tag: any) => ( + {tag.name} + ))} +
+ ); + }, + toggleable: true, + defaultVisible: false, + }, { key: 'title', label: 'Title', diff --git a/frontend/src/hooks/useFeatureFlag.ts b/frontend/src/hooks/useFeatureFlag.ts deleted file mode 100644 index 20e1dd98..00000000 --- a/frontend/src/hooks/useFeatureFlag.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useMemo } from 'react'; -import { useSettingsStore } from '../store/settingsStore'; - -// Simple client-side feature flag hook backed by accountSettings -export function useFeatureFlag(key: string): boolean { - const accountSettings = useSettingsStore((s) => s.accountSettings); - const setting = accountSettings?.[key]; - const enabled = useMemo(() => { - if (!setting || !setting.config) return false; - try { - // Expect config to be boolean or { enabled: boolean } - if (typeof setting.config === 'boolean') return Boolean(setting.config); - if (typeof setting.config === 'object' && setting.config !== null && 'enabled' in setting.config) { - return Boolean((setting.config as any).enabled); - } - return Boolean(setting.config); - } catch { - return false; - } - }, [setting]); - return enabled; -} - - diff --git a/frontend/src/layout/AppSidebar.tsx b/frontend/src/layout/AppSidebar.tsx index 3363c263..7869013a 100644 --- a/frontend/src/layout/AppSidebar.tsx +++ b/frontend/src/layout/AppSidebar.tsx @@ -130,7 +130,7 @@ const AppSidebar: React.FC = () => { workflowItems.push({ icon: , name: "Writer", - path: "/writer/tasks", // Default to tasks (changed from content) + path: "/writer/content", // Default to content, submenus shown as in-page navigation }); } diff --git a/frontend/src/pages/Writer/Content.tsx b/frontend/src/pages/Writer/Content.tsx index 133fe7a4..dda9f989 100644 --- a/frontend/src/pages/Writer/Content.tsx +++ b/frontend/src/pages/Writer/Content.tsx @@ -20,6 +20,7 @@ import { useSectorStore } from '../../store/sectorStore'; import { usePageSizeStore } from '../../store/pageSizeStore'; import ProgressModal from '../../components/common/ProgressModal'; import { useProgressModal } from '../../hooks/useProgressModal'; +import ContentViewerModal from '../../components/common/ContentViewerModal'; import PageHeader from '../../components/common/PageHeader'; import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs'; import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter'; @@ -49,6 +50,9 @@ export default function Content() { const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); const [showContent, setShowContent] = useState(false); + // Content viewer modal state + const [isViewerModalOpen, setIsViewerModalOpen] = useState(false); + const [viewerContent, setViewerContent] = useState(null); // Progress modal for AI functions const progressModal = useProgressModal(); @@ -134,10 +138,11 @@ export default function Content() { setCurrentPage(1); }; - // Handle view content - navigate to content view instead of opening modal + // Handle view content const handleViewContent = useCallback((row: ContentType) => { - navigate(`/writer/content/${row.id}`); - }, [navigate]); + setViewerContent(row); + setIsViewerModalOpen(true); + }, []); // Create page config const pageConfig = useMemo(() => { @@ -149,7 +154,6 @@ export default function Content() { setStatusFilter, setCurrentPage, onViewContent: handleViewContent, - enableToggleContent: false, // Disable dropdown toggle on this page; open full view instead }); }, [ activeSector, @@ -296,7 +300,16 @@ export default function Content() { }} /> - {/* Content view opens in its own route; modal removed */} + {/* Content Viewer Modal */} + { + setIsViewerModalOpen(false); + setViewerContent(null); + }} + title={viewerContent?.title || 'Content'} + contentHtml={viewerContent?.content_html || ''} + /> {/* Progress Modal for AI Functions */} { - const siteId = content?.site ?? content?.site_id ?? null; - if (!siteId || !content?.id) { - toast.error('Site or content id missing'); - return; - } - navigate(`/sites/${siteId}/posts/${content.id}/edit`); - }; - - const handleGenerateImages = async () => { - if (!content?.id) return; - try { - setGeneratingImages(true); - const result = await generateImagePrompts([content.id]); - if (result && result.success) { - toast.success('Image generation started'); - // If async task_id returned, open progress modal elsewhere; refresh images after short delay - setTimeout(() => window.location.reload(), 1500); - } else { - toast.error(result?.error || 'Failed to start image generation'); - } - } catch (e: any) { - toast.error(`Failed to generate images: ${e?.message || e}`); - } finally { - setGeneratingImages(false); - } - }; - - const handlePublish = async () => { - if (!content?.id) return; - try { - setPublishing(true); - const result = await publishContent(content.id); - if (result && (result.external_url || result.external_id)) { - toast.success('Content published successfully'); - // Reload to show updated external_id/status - setTimeout(() => window.location.reload(), 800); - } else { - toast.error('Failed to publish content'); - } - } catch (e: any) { - toast.error(`Publish failed: ${e?.message || e}`); - } finally { - setPublishing(false); - } - }; - if (loading) { return (
@@ -733,7 +677,7 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
{/* Header Section */}
-
+
@@ -750,34 +694,6 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten

)}
-
- {featureEnabled && (content.status === 'draft' || content.status === 'review') && ( - - )} - {featureEnabled && content.status === 'draft' && ( - - )} - {featureEnabled && content.status === 'review' && ( - - )} -
diff --git a/master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md b/master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md deleted file mode 100644 index 799471e0..00000000 --- a/master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md +++ /dev/null @@ -1,190 +0,0 @@ -# Content Manager Removal & Refactor Plan - -**Purpose**: Provide a safe, staged, reversible plan to remove the Content Manager UI and perform the requested content-manager refactor while preserving Writer pages and integrations. This file is a single-source plan for developers and QA. - ---- - -## Objectives - -- Safely remove/hide the Content Manager dashboard UI and related code paths without breaking Writer flows: Tasks, Content (detail), Images, Published, Sites, Site integration, and WordPress sync. -- Implement requested UI changes (three buttons, metadata block, table column changes) behind a feature flag. -- Ensure background syncs, plugin webhooks, and publish adapters continue to function. -- Do not perform destructive DB schema removals during initial rollout; deprecate first, remove later after validations. - ---- - -## High-level Phases - -1. Preparation & discovery (completed) -2. Feature-flag gating & compatibility proxies -3. Frontend hide + UI refactor (non-destructive) -4. Backend normalization & compatibility layer -5. Staged cleanup & code removal -6. Tests, rollout, monitoring, and rollback - ---- - -## Feature flag and admin control - -- Add server-side feature flag `feature.content_manager_refactor` (default OFF in production). -- Add admin option `enable_content_manager_removal_mode` to WP plugin and backend config for controlled toggles. -- All major UI changes and route behavior changes must be gated behind the feature flag. - ---- - -## Per-file action plan (first sprint - non-destructive) - -- frontend/src/layout/AppSidebar.tsx - - Change Writer menu link default from `/writer/content` to `/writer/tasks` (behind flag). - -- frontend/src/config/routes.config.ts - - Ensure `/writer` redirects to `/writer/tasks` when flag enabled. - -- frontend/src/pages/Writer/Content.tsx - - Hide inline body toggle and modal behavior behind flag; ensure title click navigates to content detail. - -- frontend/src/components/common/ToggleTableRow.tsx - - Remove usage on Content list (do not delete component globally). - -- frontend/src/pages/Writer/ContentView.tsx (or ContentViewTemplate) - - Add metadata block (Cluster, Sector, Categories, Tags). - - Add three buttons with conditional visibility (Edit content, Generate images, Publish). - -- frontend/src/pages/Writer/Review.tsx - - Add Categories and Tags columns after Title; make Title clickable to open ContentView. - -- frontend/src/pages/Writer/Images.tsx - - Remove image prompt column; render image and status badge stacked. - -- frontend/src/config/pages/content.config.tsx and table-actions.config.tsx - - Add/adjust columns and action visibility rules to support the UI changes. - -- frontend/src/templates/TablePageTemplate.tsx - - Persist column selector state per-page using localStorage key `table-columns:${routePath}`. - -- frontend/src/services/api.ts - - Keep `publishContent`, `unpublishContent`, `generateImagePrompts`, `fetchContentById` unchanged; add wrappers only if needed for compatibility. - -- backend/igny8_core/modules/writer/views.py - - Keep `ContentViewSet` endpoints; ensure `publish()` writes `external_id` & `external_url` and has robust validation. - -- backend/igny8_core/business/integration/services/content_sync_service.py - - Normalize status usage (`published` not `publish`) and set canonical `content.external_id` on successful publish. - -- backend/igny8_core/business/publishing/services/adapters/wordpress_adapter.py - - Confirm adapter returns `external_id` and `url` reliably and handles media errors gracefully. - -- igny8-wp-integration/sync/igny8-to-wp.php - - Add fallback: if `task['content_id']` is present, fetch `GET /v1/writer/content/{content_id}/` and use `content_html` for post body; fallback to legacy `task['content']` if content fetch fails. - -- igny8-wp-integration/includes/functions.php - - Ensure cron jobs and connection toggles can be disabled via admin option to stop sync during rollback. - ---- - -## UI Requirements & Exact Behavior - -1. Content detail `/writer/content/:id` (ContentView) - - Metadata block (top-right or top section): - - Cluster: label + cluster name (link to cluster detail if clicked) - - Sector: label + value - - Categories and Tags: chips below Sector - - Buttons (top toolbar): - - Edit content - - Visible when `status` ∈ {`draft`, `review`}. - - Action: navigate to PostEditor (use existing PostEditor route, pass `content_id`). - - Generate images - - Visible when `status` == `draft`. - - Action: call `POST /v1/writer/content/{id}/generate_image_prompts/`; show progress modal if `task_id` returned. - - Publish - - Visible when `status` == `review`. - - Action: call `POST /v1/writer/content/{id}/publish/`; on success display toast with `external_url` and refresh. - -2. `/writer/content` list - - Remove dropdown row-toggle that shows body. - - Clicking Title navigates to `/writer/content/{id}` (no modal). - -3. `/writer/review` - - Table columns: [Select] [Title (clickable)] [Categories] [Tags] [Cluster] [Status] [Actions]. - -4. `/writer/images` - - Remove 'image prompt' column. - - Image column shows: thumbnail (top) and status badge (below) as vertical stack in a single column. - -5. Table column persistence - - Store selected columns per route in `localStorage` key `table-columns:${routePath}`. - - On mount, load and apply selection. - ---- - -## Data flow & canonical fields (reference) - -- Task (lightweight): id, title, cluster_id, content_type, content_structure, taxonomy_term_id, keywords (M2M), status (`queued`|`completed`). -- Content (canonical body): id, title, content_html, ai_raw/json (if present), content_type, content_structure, cluster_id, taxonomy_terms (M2M), source (`igny8`|`wordpress`), status (`draft`|`published`), external_id, external_url. -- WordPress: expect adapter to return `external_id` and `url`. - ---- - -## Backend normalization & compatibility - -- Replace any uses of `status == 'publish'` with `status == 'published'` (e.g., in `ContentSyncService`). -- Standardize storing WP post id in `content.external_id` (not only in `content.metadata['wordpress_id']`). -- Keep old metadata keys supported for one release by copying `metadata['wordpress_id']` → `external_id` (migration or compatibility write). -- Plugin fallback: if webhook provides `task.content_id`, plugin must fetch content detail for `content_html`. - ---- - -## Tests & verification - -- Unit tests (backend) - - `ContentViewSet.publish()` updates `external_id` & `external_url` and content status. - - `ContentSyncService` picks up `published` content and updates `external_id`. -- Frontend unit tests - - ContentView button visibility by status. - - Review table shows categories/tags and clickable title. -- Integration/E2E tests - - End-to-end flow: Idea → Task → Generate content → verify Content saved → generate images → publish → WP post created → `content.external_id` present. -- Plugin webhook simulation - - Simulate `task_published` with `content_id` present; verify WP post has full body from `content_html` and metadata keys set. - ---- - -## Rollback & monitoring - -- Quick rollback: flip `feature.content_manager_refactor` OFF. -- If publish failures spike: set WP plugin option `igny8_connection_enabled` = 0 to stop cron and inbound sync. -- Instrument logs for publish path and plugin webhook calls; monitor error rates, failed publishes, missing credentials. - ---- - -## Timeline & sprint breakdown (first sprint - non-destructive) - -- Day 0.5: Feature flag & admin option + small plugin fallback change. -- Day 1–2: Frontend changes (hide old UI, implement ContentView metadata + buttons behind flag, review/images updates, column persistence). -- Day 0.5: Backend normalization: `ContentSyncService` status normalization and `external_id` writes; confirm `publish()` stable. -- Day 1: Tests, staging deploy, integration E2E. -- Day 0.5: Staged canary release and monitoring. - ---- - -## Deliverables for first sprint - -- `master-docs/CONTENT-MANAGER-REMOVAL-PLAN.md` (this file) -- Feature flag implemented and documented -- Frontend UI changes behind flag: ContentView metadata/buttons, Review table updates, Images table updates, Column persistence -- Backend fixes: status normalization, external_id unification -- WP plugin fallback to fetch `content_html` when `content_id` present -- Tests and staging validation - ---- - -## Next steps I will take if you approve - -1. Produce exact per-file diffs for the first sprint (safe edits). -2. Optionally implement the edits and run the test suite in staging. - -If you want me to generate the diffs now, say "generate diffs" and I will produce the changes for review. - ---- - -End of plan. diff --git a/master-docs/CONTENT-MANAGER-REMOVAL-TESTS.md b/master-docs/CONTENT-MANAGER-REMOVAL-TESTS.md deleted file mode 100644 index 2665b373..00000000 --- a/master-docs/CONTENT-MANAGER-REMOVAL-TESTS.md +++ /dev/null @@ -1,53 +0,0 @@ -# Content Manager Removal — Tests, E2E Flows & Rollback - -Purpose: a concise, actionable test plan and rollback instructions to validate the first non-destructive sprint before full cleanup. - -1) Smoke tests (manual or scriptable) -- Create content from Planner → verify Content record exists with `content_html` populated. -- Generate image prompts for a Content item (draft) → verify image prompt records created and UI shows "Image Prompts Generated". -- Generate images → verify images tasks start and images appear in `/writer/images`. -- Move Content to `review` status → call Publish → verify backend returns `external_id` and `external_url` set on Content and WP plugin creates post. -- Trigger plugin webhook simulation with `task` payload that includes `content_id` → verify WP post body uses `content_html`. - -2) Unit tests (backend) -- `ContentViewSet.publish()`: - - publishes and sets `external_id` and `external_url`. - - rejects publish when site credentials missing. -- `ContentSyncService._sync_to_wordpress`: - - queries `status='published'` (not 'publish'). - - writes `content.external_id` on success. - -3) Frontend unit tests -- `ContentViewTemplate`: - - buttons visibility by `status` (`draft`: Edit + Generate; `review`: Edit + Publish). - - Edit button navigates to `/sites/{siteId}/posts/{contentId}/edit`. -- `Review` page: - - Title renders as link to `/writer/content/{id}`. - - Categories and Tags columns appear and persist via ColumnSelector. -- `Images` page: - - Content image cells do not show prompt text, only image and status badge. - -4) E2E test (recommended - scriptable) -- Flow: - 1. Create Idea → create Task → generate content (AI) → assert Content record created. - 2. For created Content (status draft) call generate image prompts → start generation → wait for images generated. - 3. Change status to review → call publish endpoint → assert WP post exists via plugin test endpoint and `external_id` present. - -5) Monitoring & metrics to watch in staging -- Publish success rate (per-minute/hour). -- WP plugin webhook failures and missing credentials. -- Content with `content_id` but empty `content_html`. - -6) Rollback steps (fast) -- Flip account setting `feature.content_manager_refactor` OFF (server-side account setting) — this hides/refuses new UI. -- If publish failures spike, set WP plugin option `igny8_connection_enabled` = 0 to stop outbound syncs and webhooks. -- Revert UI commits in the release branch and redeploy. - -7) Test artifacts -- Store E2E run logs and failing request/response pairs in `staging/e2e-runs/{timestamp}/`. - -Notes -- Automate the E2E with Cypress or Playwright; prefer Playwright for headless CI runs. -- Use test WP site with test credentials; do not use production credentials for staging tests. - -