Create /app/(portal)/subscription/page.tsx
Show the current user's subscription status at the top:
"Your current plan: [Plan Name]" with status badge (Active/Cancelled/Past Due)
"Next billing date: [date]" if active
"Manage Billing" button → calls /api/stripe/portal → redirect to Stripe portal
Below: show all four plan cards side by side:
Observer (Free), Starter (£150/mo), Professional (£450/mo), Enterprise
Highlight the user's current plan with an ixg-green border
For each plan the user can upgrade to: "Upgrade to [Plan]" button
→ calls /api/stripe/create-checkout → redirect to Stripe Checkout
Observer plan: "Your current free plan" (no button)
Enterprise: "Contact IXG Team" → mailto:info@ixgltd.co.uk
Create /app/(portal)/subscription/success/page.tsx
Simple page: "Subscription Activated. Welcome to IXG [Plan]!"
Button: "Go to Dashboard"
Create the buyer portal layout and main dashboard.
LAYOUT: /app/(portal)/layout.tsx
Sidebar navigation (left, fixed, 240px wide):
Top: IXG LTD logo text in ixg-green, role badge "Buyer" in small grey text.
Navigation items (use lucide-react icons):
Dashboard (LayoutDashboard icon) → /portal/dashboard
Batch Tracker (Package icon) → /portal/batches
Document Hub (FileText icon) → /portal/documents
Compliance (ShieldCheck icon) → /portal/compliance
Subscription (CreditCard icon) → /portal/subscription
Bottom: User name + "Sign Out" button (calls supabase.auth.signOut)
Main content area: right of sidebar, scrollable, white background.
Top bar: Page title (dynamic) + Notifications bell icon.
Sidebar bg: ixg-green (#1A3D2B), text: white, active item: gold left border.
PAGE: /app/(portal)/dashboard/page.tsx
Welcome message: "Good morning, [user.full_name]" (dynamic time greeting).
ROW 1 — 4 KPI cards:
Active Batches — count of batches with status NOT IN (delivered,qc_fail)
In Transit — count of batches with status="in_transit"
Pending Delivery — count with status IN (at_uk_port,customs)
This Month's Deliveries — count with status="delivered" this calendar month
ROW 2 — Recent Batches Table (last 5 batches):
Columns: Batch Reference | Grade | Weight (kg) | Status | ETA UK | View
Status shown as coloured badge:
in_transit → blue | at_uk_port → orange | delivered → green | qc_fail → red
"View" → link to /portal/batches/[id]
"View all batches →" link below the table.
ROW 3 — Recent Documents (last 3 documents):
Columns: File Name | Type | Batch Reference | Date | Download
"View all documents →" link.
Data fetched server-side using Supabase server client.
Show skeleton loaders while data loads.
Create the batch tracker list page at /app/(portal)/batches/page.tsx
FILTERS at top of page:
Status dropdown: All | In Transit | At UK Port | Customs | Delivered
Grade dropdown: All | Grade A | Grade B | Grade C
Date range picker: "From" and "To" date inputs
Search input: search by batch reference
"Clear filters" link
BATCH TABLE below filters:
Columns: Batch Reference | Origin | Grade | Weight (kg) | Harvest Date
| QC Status | Shipment Status | ETA UK | Action
QC Status badge: Pass (green) | Fail (red) | Pending (grey)
Shipment Status badge: colour-coded as defined in P08
Action: "View Details" button → /portal/batches/[id]
Pagination: 20 rows per page, page numbers at bottom
Empty state: "No batches found matching your filters."
Show total count: "Showing X of Y batches"
Fetch data from processing_batches joined with qc_results and shipments.
Apply filters as database query WHERE clauses (not client-side filtering).
Observer plan users see only the 5 most recent batches with an upgrade prompt.
Create /app/(portal)/batches/[id]/page.tsx — the full batch detail view.
SECTION 1 — Batch Header:
Batch reference (large, bold) + Grade badge + Status badge.
Four detail chips: Origin Facility | Harvest Date | Weight | Created By
"Back to Batch List" breadcrumb link.
SECTION 2 — Status Timeline:
Horizontal stepper showing the full journey:
Harvested → QC Testing → QC Passed → Packed → Departed Nigeria
→ In Transit → Arrived UK Port → Customs Clearance → Delivered
Current step highlighted in ixg-green. Completed steps with tick.
Pending steps in grey. Failed steps (qc_fail) in red.
SECTION 3 — Two column layout:
LEFT — QC Results:
Table of QC parameters:
FFA %: [value] — Target: ≤ 3% — Status: Pass/Fail
Moisture %: [value] — Target: ≤ 0.1% — Status: Pass/Fail
Peroxide Value: [value] — Target: ≤ 10 meq/kg — Status: Pass/Fail
Visual Grade: [grade] — Pass/Fail
Overall QC result: large green/red badge.
Lab reference number and test date.
RIGHT — Shipment Details:
Table: Vessel Name | Container Ref | BL Number | ETD Nigeria | ETA UK
Port of Origin | Port of Destination | Freight Forwarder
Current shipment status badge.
SECTION 4 — Documents:
Grid of document cards for this batch.
Each card: file icon + file name + doc type badge + upload date + "Download" button.
Download button fetches a signed URL from Supabase Storage and opens it.
Professional plan users see all documents.
Starter plan users see CoA and regulatory certs only.
Observer users see a prompt: "Upgrade to Starter to access documents."
SECTION 5 — ESG Report (Professional plan only):
Show data from esg_field_reports: women employed, total workers,
fair wage paid, safety equipment used, community notes.
If not Professional: show upgrade prompt.
Fetch all batch data in a single server component with parallel queries.
Create /app/(portal)/documents/page.tsx — buyer-facing document hub.
FILTERS at top:
Doc Type dropdown: All | Certificate of Analysis | Regulatory | Invoice
| Packing List | Bill of Lading | Insurance | Other
Batch Reference search input
Date range picker
DOCUMENT TABLE:
Columns: File Name | Type | Batch Reference | Uploaded | Size | Download
Download: creates a signed Supabase Storage URL (60-minute expiry)
and opens file in new tab
File type badge: CoA (green) | Regulatory (blue) | Invoice (grey) | etc.
Empty state with icon: "No documents found."
SUMMARY STATS at top (small cards):
Total Documents | CoAs Available | Regulatory Certs | Last Upload Date
Access control:
Buyer role: sees all documents with visibility="buyer"
Professional plan: also sees documents with visibility="ops" (full access)
Observer/Starter: CoA and regulatory_certs only
Create document upload functionality for the IXG ops team.
SUPABASE STORAGE SETUP:
Create a Supabase Storage bucket called "ixg-documents" (private bucket).
RLS: authenticated users with role=ops or role=admin can upload.
Buyers can only read files (via signed URL).
API ROUTE: /app/api/documents/upload/route.ts (POST)
Accepts multipart form data: file, batch_id, doc_type, visibility
Validates: file size ≤ 50MB, allowed types: PDF, JPG, PNG, XLSX
Uploads to Supabase Storage at path: ixg-documents/{batch_id}/
Inserts record into documents table
Returns:
COMPONENT: /components/ops/DocumentUpload.tsx
Drag-and-drop upload zone (use shadcn or a simple custom component).
File picker fallback.
Fields: Document Type dropdown, Batch Reference lookup (searchable dropdown
fetching from processing_batches), Visibility selector (buyer/ops/admin).
Progress bar during upload.
Success: show file name and "Upload another?" button.
Error: show descriptive error message.
Add this component to the ops batch detail page at /ops/batches/[id].
Create the IXG Ops team interface.
LAYOUT: /app/(ops)/layout.tsx
Same sidebar structure as buyer portal but with ops-specific navigation:
Ops Dashboard → /ops/dashboard
Batch Management → /ops/batches
QC Entry → /ops/qc
Shipments → /ops/shipments
ESG Reports → /ops/esg
Documents → /ops/documents
CAPA Records → /ops/capa
Role badge shows "Operations Team".
Same ixg-green sidebar as buyer, but accent different navigation items in gold.
PAGE: /app/(ops)/dashboard/page.tsx
OPS METRICS (4 KPI cards):
Batches Pending QC — status="harvested" or status="processing"
QC Passed This Month
Shipments In Transit
Documents Uploaded This Month
ACTION ITEMS (flagged tasks):
Any batch with status="harvested" for more than 3 days → flag as "Awaiting QC"
Any shipment with eta_uk in next 3 days → flag as "Arriving Soon"
Any CAPA with status="open" and due_date passed → flag as "Overdue CAPA"
Show as yellow warning cards with "Take Action" links.
RECENT BATCHES TABLE — all batches ordered by created_at DESC, limit 10.
Columns: Batch Ref | Grade | Weight | QC Status | Ship Status | Created | Edit
"Edit" → /ops/batches/[id]/edit
QUICK ACTIONS (three buttons):
"Add New Batch" | "Enter QC Results" | "Update Shipment"
Create the ops batch management forms.
PAGE 1: /app/(ops)/batches/new/page.tsx — Create New Batch
Form fields:
Batch Reference (auto-generated: IXG-BATCH-YYYY-NNN, but editable)
Harvest Date (date picker)
Weight (kg) (number input)
Grade (select: A / B / C)
Origin Facility (pre-filled: "ASI - Katsina State, Nigeria", editable)
Notes (textarea)
Submit: insert to processing_batches with status="harvested"
Success: redirect to /ops/batches/[newId]/edit
and send email notification via Resend to ops team:
Subject: "New batch [reference] logged — QC pending"
PAGE 2: /app/(ops)/qc/[batchId]/page.tsx — Enter QC Results
Show batch reference and weight at top.
QC form fields:
FFA % (decimal, required) — show target: ≤ 3%
Moisture % (decimal, required) — show target: ≤ 0.1%
Peroxide Value meq/kg (decimal) — show target: ≤ 10
Visual Grade (select: A / B / C / Fail)
Lab Reference (text)
Tested By (text)
On submit:
Calculate overall_pass: true only if FFA≤3, moisture≤0.1, peroxide≤10,
visual_grade != "Fail"
Insert to qc_results table
Update processing_batches.status to "qc_pass" or "qc_fail"
If qc_fail: automatically create a CAPA record with issue_description=
"QC failure on batch [reference]", status="open"
Send email to ops team via Resend with QC result summary
Show inline validation: highlight fields in red if value exceeds target.
PAGE 3: /app/(ops)/batches/[id]/edit/page.tsx — Edit Batch Status
Allow ops to update: status, notes, and linked shipment details.
Status dropdown with all valid status transitions.
Save button → update processing_batches.
Create the admin panel for IXG platform administrators.
LAYOUT: /app/(admin)/layout.tsx
Sidebar with admin navigation:
Admin Dashboard → /admin/dashboard
User Management → /admin/users
Subscriptions → /admin/subscriptions
Platform Analytics → /admin/analytics
System Settings → /admin/settings
Role badge: "Administrator" in gold.
PAGE: /admin/dashboard/page.tsx
PLATFORM KPIs (6 cards):
Total Registered Users | Active Subscriptions | MRR (£)
Total Batches | Batches This Month | Documents Uploaded
MRR = count of active Starter subs × 150 + count of active Pro subs × 450
PAGE: /admin/users/page.tsx
Table of all users:
Columns: Name | Company | Email | Role | Plan | Joined | Actions
Actions: "Edit Role" (dropdown to change role) | "View Profile"
"Invite User" button — admin can add users with pre-assigned role
(calls supabase.auth.admin.inviteUserByEmail with role metadata)
Search users by name, email, or company.
Filter by role: All | Buyer | Ops | Admin
PAGE: /admin/subscriptions/page.tsx
Table of all subscriptions:
Columns: User | Company | Plan | Status | Started | Next Renewal | Stripe ID
Filter by status: All | Active | Cancelled | Past Due
Total MRR displayed prominently at top.
Create the IXG AI chatbot using GPT-4o-mini.
API ROUTE: /app/api/chat/route.ts (POST)
Verify user is authenticated and has Professional plan subscription.
If not Professional: return 403 with { error: "Upgrade to Professional plan" }.
Accept: { messages: [{role, content}], sessionId }
Build system prompt:
"You are the IXG Platform Assistant, a specialist in Nigerian shea butter
supply chain management for UK buyers. You help users understand:
- Batch quality parameters (FFA ≤3%, moisture ≤0.1%, peroxide ≤10 meq/kg)
- IXG's export process from Katsina State, Nigeria to UK ports
- UK compliance requirements including DFTI and ETDA 2023
- Platform features and how to use them
- Shea butter quality grades (A, B, C) and their applications
- The supply chain route: ASI facility → Apapa Port → Felixstowe
Be professional, concise, and accurate.
If asked about specific batch data you do not have, direct the user
to the Batch Tracker. Do not invent data.
Always respond in English."
Call OpenAI chat completions with model: "gpt-4o-mini"
max_tokens: 500, temperature: 0.3
Stream the response using the Vercel AI SDK (install ai package).
Log the interaction to ai_interactions table (user_id, session_id,
message_role, message_content, tokens_used).
COMPONENT: /components/portal/Chatbot.tsx
Floating chat button (bottom right corner): circular ixg-green button
with MessageCircle icon and "Ask IXG Assistant" tooltip.
Click → opens chat panel (right-side slide-in, 380px wide).
Chat panel header: "IXG Platform Assistant" + Close button.
Message area: scrollable list of user (right, gold bubble) and
assistant (left, white bubble with ixg-green avatar).
Input area: text input + Send button.
Typing indicator (three dots animation) while streaming.
Show suggested opening questions as clickable chips when chat is empty:
"What are IXG's quality standards?"
"How do I download a CoA certificate?"
"What does FFA percentage mean?"
"How long does shipping take?"
Use the ai/react useChat hook for streaming.
Add Chatbot component to /app/(portal)/layout.tsx so it appears on all portal pages.
Create a centralised email notification service using Resend.
SERVICE FILE: /lib/email/sendEmail.ts
Function: sendEmail({ to, subject, html })
Uses Resend SDK: import { Resend } from "resend"
from: process.env.RESEND_FROM_EMAIL (noreply@ixgltd.co.uk)
Log errors but do not throw — email failure should not break the app.
EMAIL TEMPLATES — create each as a TypeScript function returning HTML string.
Path: /lib/email/templates/
Template 1: welcomeEmail({ userName, companyName, planName })
Subject: "Welcome to IXG Ltd Platform, [userName]"
Body: IXG branding (dark green header, gold accent), welcome message,
summary of their plan features, "Go to Dashboard" CTA button.
Template 2: newBatchEmail({ batchReference, grade, weightKg, harvestDate })
Subject: "New Batch [reference] Available on IXG Platform"
Body: batch summary table, "View Batch Details" CTA.
Sent to: all active Starter and Professional subscribers.
Template 3: qcResultEmail({ batchReference, pass, ffa, moisture, peroxide })
Subject: "QC Result: Batch [reference] — [PASS/FAIL]"
Body: QC parameters table with Pass/Fail per parameter, overall result banner
(green for pass, red for fail), "View Batch" CTA.
Sent to: IXG ops team (role="ops") always.
For PASS: also sent to all active subscribers.
Template 4: shipmentUpdateEmail({ batchReference, newStatus, etaUk })
Subject: "Shipment Update: Batch [reference] — [Status]"
Body: current status badge, ETA, shipment details, "Track Batch" CTA.
Sent to: all active subscribers.
Template 5: paymentFailedEmail({ userName, planName, retryDate })
Subject: "Action Required: IXG Subscription Payment Failed"
Body: clear explanation, "Update Payment Method" button → Stripe portal.
NOTIFICATION TRIGGER FUNCTION: /lib/email/notifySubscribers.ts
Query all users with active Starter or Professional subscriptions.
Send the appropriate template to each.
Used by P14 (batch creation) and the Stripe webhook (P06).
Create /app/(portal)/compliance/page.tsx — the buyer compliance reference hub.
SECTION 1 — UK Regulatory Standards Summary:
Info cards for each standard IXG complies with:
Card 1: DFTI (Digital Free Trade Initiative)
"IXG operates in full conformity with DFTI requirements for digital
trade documentation, including the Electronic Trade Documents Act 2023."
Card 2: ETDA 2023 (Electronic Trade Documents Act)
"All IXG export documents — including Bills of Lading and Certificates
of Analysis — are issued in electronic format, legally recognised under
ETDA 2023 (in force 20 September 2023)."
Card 3: BTOM (Border Target Operating Model)
"Shea butter exports comply with UK phytosanitary checks under BTOM.
IXG holds pre-export phytosanitary certificates from NAQS Nigeria."
Card 4: EUDR (EU Deforestation Regulation)
"IXG is preparing EUDR geo-referenced proof of origin for all batches.
Compliance deadline: 30 June 2027 (SME)."
SECTION 2 — Quality Standards Table:
Table with IXG's quality parameters vs targets:
Parameter | IXG Target | Industry Standard | Testing Body
FFA % | ≤ 3% | ≤ 4% | SGS Nigeria / Intertek Lagos
Moisture & Impurities | ≤ 0.1% | ≤ 0.2% | SGS Nigeria / Intertek Lagos
Peroxide Value | ≤ 10 meq/kg | ≤ 15 meq/kg | SGS Nigeria / Intertek Lagos
Aflatoxins | ≤ 10 ppb | ≤ 15 ppb | SGS Nigeria / Intertek Lagos
Colour | Ivory to white | — | Visual inspection
SECTION 3 — Export Route:
Simple text description:
"Origin: ASI Facility, Katsina State, Nigeria
Export Port: Apapa Port, Lagos (CN code: 1515.90)
Freight Route: Apapa Port → Port of Felixstowe or Southampton
Transit Time: 14–18 days | Pre-Shipment Inspection: SGS Nigeria / Intertek Lagos"
SECTION 4 — Certificate Download:
"Download IXG Platform Quality Charter" button (link to a PDF in Supabase Storage).
Note: Create a placeholder PDF and upload manually to Supabase Storage.
Apply final polish and error handling across the entire platform.
ERROR BOUNDARIES:
Create /app/error.tsx — global error page with IXG branding.
"Something went wrong. Please try again or contact info@ixgltd.co.uk"
"Back to Dashboard" button.
Create /app/not-found.tsx — 404 page.
"Page not found. The batch you're looking for may have moved."
LOADING STATES:
Create /app/loading.tsx — global loading with IXG spinner (green circular).
Add Suspense boundaries with skeleton loaders to all data-fetching pages:
Batch list, document hub, dashboard KPIs.
EMPTY STATES:
For every table or list that can be empty, show a helpful empty state with icon.
Batch list empty: "No batches yet. IXG ops team will add batches shortly."
Documents empty: "No documents available for your current subscription plan."
TOAST NOTIFICATIONS:
Ensure all form submissions show:
Success toast (green): specific confirmation message
Error toast (red): "Something went wrong: [error message]"
Use shadcn Toaster — add to the root layout.
SEO METADATA — add to all public pages:
Title: "[Page Name] | IXG Ltd — Shea Butter Supply Chain Platform"
Description: relevant page description
OG tags: og:title, og:description, og:url
Use Next.js generateMetadata() for dynamic routes.
FAVICON:
Create a simple favicon using the IXG initials "IXG" on dark green background.
Add to /app/favicon.ico and /public/favicon.ico.
MOBILE RESPONSIVENESS:
Check the portal sidebar collapses to a hamburger menu on screens < 768px.
Ensure all tables are horizontally scrollable on mobile.
Ensure all forms are full-width on mobile.