Streamline your content workflow from ideation to client approval
TaxFlow Agency Portal is a comprehensive content management system designed for SEO agencies to create, manage, and collaborate on blog content with their clients. The platform integrates AI-powered content generation (via Google Gemini) with a structured workflow that guides articles through titles, outlines, drafts, and client review stages.
- Client Management: Create and manage client profiles with default tone of voice and content rules
- Campaign Management: Organize content creation by campaigns with strategy goals and target audiences
- Article Workflow: Structured workflow from title generation → outline → draft → client review
- AI Content Generation:
- Generate SEO-optimized blog titles
- Create structured outlines
- Generate full blog drafts
- Refine content based on feedback
- SEO analysis and keyword suggestions
- Client Collaboration: Integrated review system where clients can approve or request revisions
- Status Tracking: Real-time status updates throughout the article lifecycle
NEEDS_TITLES- Agency generates article titlesAWAITING_REVIEW_TITLES- Client reviews and selects a titleTITLES_APPROVED- Agency creates outlineAWAITING_REVIEW_OUTLINE- Client reviews outlineOUTLINE_APPROVED- Agency creates draftAWAITING_REVIEW_DRAFT- Client reviews draftDRAFT_APPROVED- Article completedNEEDS_REVISION- Client requests changes
- Frontend: React 19 + TypeScript
- Build Tool: Vite 6
- UI Components: Lucide React (icons)
- Database: Supabase (PostgreSQL)
- AI Integration: Google Gemini API (
@google/genai) - Charts: Recharts, D3.js
- Styling: Tailwind CSS (via inline classes)
Before you begin, ensure you have the following installed:
- Node.js (v18 or higher recommended)
- npm or yarn package manager
- Supabase Account (free tier works)
- Google Gemini API Key (get from Google AI Studio)
git clone <repository-url>
cd SEO-2-version-mainnpm installCreate a .env.local file in the root directory:
API_KEY=your_gemini_api_key_hereNote: The Gemini API key is used by services/geminiService.ts for AI content generation.
- Go to Supabase Dashboard
- Create a new project
- Note your Project URL and Anon Key from Project Settings > API
Edit services/supabaseClient.js:
const supabaseUrl = 'YOUR_SUPABASE_PROJECT_URL';
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY';-
Open Supabase Dashboard > SQL Editor
-
Run
supabase_setup.sqlto create initial tables:-- This creates: clients, campaigns, articles tables -
Run
supabase_schema_update.sqlto add additional fields and constraints:-- This adds: last_updated, selected_title, status constraints, indexes
Important: Run supabase_setup.sql first, then supabase_schema_update.sql.
npm run devThe application will be available at http://localhost:5173 (or the port shown in terminal).
The Supabase client is configured in services/supabaseClient.js. You need to:
- Replace
YOUR_SUPABASE_PROJECT_URLwith your Supabase project URL - Replace
YOUR_SUPABASE_ANON_KEYwith your Supabase anon/public key
Where to find these values:
- Supabase Dashboard > Project Settings > API
- Project URL: Under "Project URL"
- Anon Key: Under "Project API keys" > "anon" / "public"
The Gemini API key is read from environment variables:
- Development: Set
API_KEYin.env.local - Production: Set
API_KEYin your hosting platform's environment variables
The service automatically uses process.env.API_KEY (see services/geminiService.ts).
clients
id(UUID, Primary Key)name(TEXT, Required)tone_of_voice(TEXT)strict_rules(TEXT)industry,website,contact_person,email,phone(TEXT, Optional)created_at(TIMESTAMP)
campaigns
id(UUID, Primary Key)client_id(UUID, Foreign Key → clients.id)name(TEXT, Required)strategy_goals(TEXT)target_audience(TEXT)keywords(TEXT[])status(TEXT: 'ACTIVE' | 'ARCHIVED')created_at(TIMESTAMP)
articles
id(UUID, Primary Key)campaign_id(UUID, Foreign Key → campaigns.id)title(TEXT, Required)status(TEXT, Required - see workflow states above)proposed_titles(TEXT[])selected_title(TEXT)outline_content(TEXT)draft_content(TEXT)client_comments(JSONB)created_at(TIMESTAMP)last_updated(TIMESTAMP, Auto-updated)
clients (1) ──< (many) campaigns (1) ──< (many) articles
- Navigate to Clients in the sidebar
- Click "Add New Client"
- Fill in:
- Client name (required)
- Tone of voice (optional, used as default for AI generation)
- Strict rules (optional, content guidelines)
- Additional contact information
- Click "Save Client"
- Go to Dashboard
- Click "Start Campaign"
- Select a client from the dropdown
- Enter campaign name and strategy goals
- Click "Create Campaign"
- Open a campaign from the Dashboard
- Click "Create Article"
- Enter article topic/title
- Click "Create"
- Click on an article with status "Needs Titles"
- Click "Generate Titles"
- Review AI-generated titles
- Click "Submit to Client" to send for review
- Article status changes to
AWAITING_REVIEW_TITLES - Client selects a title in Client Portal
- Status updates to
TITLES_APPROVED
- Article status becomes
TITLES_APPROVED - Click "Generate Outline"
- Review and refine outline
- Click "Submit to Client" → Status:
AWAITING_REVIEW_OUTLINE
- Client approves or requests changes
- If approved: Status →
OUTLINE_APPROVED - If rejected: Status →
NEEDS_REVISION(with comments)
- After outline approval, click "Generate Draft"
- Review and refine content
- Click "Submit to Client" → Status:
AWAITING_REVIEW_DRAFT
- Client approves or requests changes
- If approved: Status →
DRAFT_APPROVED(Complete!) - If rejected: Status →
NEEDS_REVISION
SEO-2-version-main/
├── components/ # React components
│ ├── Dashboard.tsx # Main dashboard with campaigns
│ ├── CampaignDetail.tsx # Campaign view with articles
│ ├── ProjectWorkspace.tsx # Article editing workspace
│ ├── ClientManagement.tsx # Client CRUD interface
│ ├── ClientReviewPage.tsx # Client review interface
│ ├── StageTitles.tsx # Title generation stage
│ ├── StageOutline.tsx # Outline generation stage
│ ├── StageDraft.tsx # Draft generation stage
│ └── StatusBadge.tsx # Status display component
├── constants/
│ └── status.ts # Article status constants
├── docs/ # Documentation
│ ├── integration-guide.md
│ ├── client-portal-setup.md
│ └── interaction-flow.md
├── services/
│ ├── supabaseClient.js # Supabase client configuration
│ ├── geminiService.ts # AI content generation service
│ └── store.ts # Legacy store (deprecated)
├── types.ts # TypeScript type definitions
├── App.tsx # Main application component
├── index.tsx # Application entry point
├── supabase_setup.sql # Initial database schema
├── supabase_schema_update.sql # Schema updates
└── package.json # Dependencies and scripts
This Agency Portal works in conjunction with a separate Client Portal application. The two portals share the same Supabase database and use synchronized status constants.
-
Agency Portal (this project):
- Creates clients, campaigns, and articles
- Generates content using AI
- Submits content for client review
- Receives feedback and makes revisions
-
Client Portal (SEO-client-version):
- Reviews articles at various stages
- Approves or rejects content
- Adds comments and feedback
- Selects titles from proposals
- Database: Both portals connect to the same Supabase project
- Status Constants: Both use identical status values from
constants/status.ts - Data Flow: Articles move through states that trigger actions in both portals
See detailed integration guide: docs/integration-guide.md
See client portal setup: docs/client-portal-setup.md
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview- TypeScript: Strict mode enabled
- Components: Functional components with hooks
- State Management: React useState/useEffect (no Redux)
- API Calls: Supabase client for database operations
- AI Calls: Gemini service for content generation
-
New Database Fields:
- Add column in Supabase SQL Editor
- Update TypeScript interfaces in
types.ts - Update component data mapping
-
New Status States:
- Add to
constants/status.ts - Update
StatusBadge.tsxfor display - Update workflow logic in components
- Add to
-
New AI Features:
- Add function to
services/geminiService.ts - Call from component using async/await
- Handle errors gracefully
- Add function to
Cause: Supabase client configuration incorrect Solution:
- Check
services/supabaseClient.jshas correct URL and key - Verify Supabase project is active
- Check browser console for errors
Cause: Client ID mismatch or client doesn't exist Solution:
- Ensure client is created in Clients page first
- Check that client ID is a valid UUID
- Verify database connection
Cause: Component not fetching from Supabase Solution:
- Check component imports
supabasefromservices/supabaseClient.js - Verify database queries use correct table/column names
- Check browser network tab for failed requests
Cause: Missing or invalid Gemini API key Solution:
- Verify
API_KEYis set in.env.local - Check API key is valid at Google AI Studio
- Check browser console for API errors
Cause: Status constants mismatch between portals Solution:
- Ensure both portals use same
constants/status.tsvalues - Check database status column matches constants
- Verify status transitions follow workflow rules
-- WARNING: This deletes all data
DROP TABLE IF EXISTS articles CASCADE;
DROP TABLE IF EXISTS campaigns CASCADE;
DROP TABLE IF EXISTS clients CASCADE;
-- Then re-run supabase_setup.sql and supabase_schema_update.sql-- View all unique status values
SELECT DISTINCT status FROM articles;
-- Check for invalid statuses
SELECT * FROM articles
WHERE status NOT IN (
'NEEDS_TITLES', 'AWAITING_REVIEW_TITLES', 'TITLES_APPROVED',
'AWAITING_REVIEW_OUTLINE', 'OUTLINE_APPROVED',
'AWAITING_REVIEW_DRAFT', 'DRAFT_APPROVED', 'NEEDS_REVISION'
);- Check browser console for errors
- Check Supabase Dashboard > Logs for database errors
- Review
docs/folder for detailed guides - Verify environment variables are set correctly
[Add your license here]
[Add contribution guidelines here]
For issues and questions, please create an issue or contact [your support channel].
Built with ❤️ for SEO agencies