Skip to content

Production-readiness pass: unblock Supabase, route protection, data integrity, schema hardening - #19

Draft
macaddy2 wants to merge 32 commits into
mainfrom
fix/production-readiness
Draft

Production-readiness pass: unblock Supabase, route protection, data integrity, schema hardening#19
macaddy2 wants to merge 32 commits into
mainfrom
fix/production-readiness

Conversation

@macaddy2

Copy link
Copy Markdown
Owner

Summary

Implements the production-readiness recommendations (excluding server-side money rails and engine wiring, per scope decision).

Blockers fixed

  • Supabase was permanently disabledisSupabaseConfigured() returned hardcoded false. Now checks env vars; client is created lazily (no placeholder URL).
  • No route protection — new RequireAuth wrapper guards all private routes (/dashboard, /wallet, /apps/*, etc.); signed-in users are redirected away from /login//signup`.
  • Talent creation was broken — context called the DB layer without userId; rows now map to the camelCase shape; updateTalentProfile actually persists.
  • Boards 400'd for brand-new users — invalid id.in.() PostgREST filter when membership list is empty.
  • BookingModal never booked — now calls createBookingRequest so talents actually receive requests.

Data unification

  • Single talent store: useTalents/useTalent read from DataContext (newly listed skills no longer 404 in TalentProfile).
  • Comments persist via a new post_comments table (+ comment_count trigger), follows via a new follows table, activities via the existing activities table; notification deletes persist.
  • Vote comments/badges pass through to idea_votes; task completion is detected by target column title (UUID-vs-'done' comparison never matched).

Reliability

  • Realtime channels are reference-counted (concurrent subscribers no longer clobber each other) and refetches are debounced (no refetch storm on busy tables).
  • AuthContext: crash-safe localStorage parse, single profile fetch via INITIAL_SESSION, updateUser sends only defined columns.
  • WalletContext validates spend against a ref mirror (rapid double-spend can't overdraft).
  • Collaboard board selection is fully derived — no setState-in-render or state-sync effects.

Schema hardening (idempotent, re-runnable)

  • notifications_insert restricted to self (no cross-user notification spoofing); profiles hidden from anon clients; emails no longer selected anywhere.
  • New tables with RLS: post_comments, follows, payments (service-role-write-only ledger for future payment rails).
  • create_dm_conversation upserts on a deterministic dm_key unique index — simultaneous first messages converge on one conversation.

Hygiene

  • Currency unified to Naira everywhere (incl. formatCurrency -> NGN); payments.js never transmits raw card data.
  • api.js checks res.ok, clamps limits, docs aligned with actual schema.
  • Dead controls wired: mobile bell -> notifications, mobile Create -> apps, post Share -> Web Share/clipboard, forgot-password -> real reset flow, landing CTAs/footer links point at real routes.
  • Analytics sparklines memoized; impure mock data moved into lazy useState initializers.

Tests

  • Added vitest + 18 unit tests: src/lib/utils.test.js and src/engine/engine.test.js (full concept->validated cascade incl. guards, idempotency, audit log). npm test passes, npm run build clean.

Test plan

  • npm test — 18/18 passing
  • npm run build — clean production build
  • npm run lint — all changed files clean (remaining repo-wide errors are pre-existing in untouched files / design-handoff)
  • Manual: set real Supabase env vars, run supabase/schema.sql, verify signup -> list skills -> book talent end-to-end

claude and others added 30 commits April 17, 2026 15:52
- .gitignore / .env.example: harden secret handling (.env*, *.pem/*.key,
  clearer placeholders)
- index.html: add referrer-policy, X-Content-Type-Options, color-scheme,
  theme-color meta tags
- vite.config.js: add production build config with vendor chunk splitting
  (react, supabase, radix)
- src/lib/ai.js: move Gemini key from URL query to x-goog-api-key header
  so it no longer leaks into server logs / referrer chains
- src/pages/Signup.jsx: bump min password length to 8 and require a
  letter + digit (client-side guardrail)
- src/pages/Dashboard.jsx: guard LEVELS.find fallback and prevent
  division-by-zero / overflow when computing level progress
- src/pages/Home.jsx: use stable keys for testimonial cards
- src/components/Header.jsx + NotificationDropdown.jsx: add aria-labels,
  aria-expanded, aria-haspopup on icon buttons for keyboard/SR users
- README.md: replace generic Vite template with real project overview
- .nvmrc: pin Node 20

Supabase RLS policies, payment tokenization (PCI), and localStorage
session handling were flagged in the audit but deferred — they require
schema changes and auth refactor that should land in dedicated PRs.
Enhance security, accessibility, and build configuration
The "Submit Idea", "Create Stake", "Create Board", and "List Your Skills"
buttons previously had no onClick handlers, so clicking them did nothing.
DataContext already exposed submitIdea/createStake/createBoard/createTalentProfile
— this commit adds the missing UI.

New components:
- src/components/ui/modal.jsx — reusable modal shell with backdrop,
  Esc-to-close, body scroll lock, aria-modal, and a Field primitive
- src/components/SubmitIdeaModal.jsx — title, description, category, up to
  5 impact tags with suggestions; awards SUBMIT_IDEA points
- src/components/CreateStakeModal.jsx — title, description, category, risk
  level, target amount, expected returns, deadline with validation
- src/components/CreateBoardModal.jsx — title + description; auto-creates
  To Do / In Progress / Done columns; jumps into the new board on success
- src/components/ListSkillsModal.jsx — display name, bio, availability,
  hourly rate, multi-skill tagger with level; awards PROFILE_COMPLETE
- src/components/AddTaskModal.jsx — title, column, due date, labels for
  Collaboard board view

Wiring:
- ConceptNexus: Submit Idea header button + empty-state CTA
- VestDen: Create Stake header button + empty-state CTA (also fixed
  mangled JSX formatting with stray whitespace inside tags and the
  "risks.Earn" concat typo; guarded stake progress against overflow;
  disabled Stake Now on funded stakes)
- Collaboard: Create Board header button + empty-state CTA, plus the
  per-column "+" and header "Add Task" now open AddTaskModal; Chat
  button now links to /messages
- SkillsCanvas: List Your Skills header button + empty-state CTA

All modals: form validation with inline errors, loading states, activity
logging via logActivity, and consistent per-app gradient styling.
Wire up primary action buttons across all 4 sub-apps
Root cause: after supabase.auth.signInWithPassword() resolved, Login.jsx
immediately called navigate('/dashboard'). But the user state in
AuthContext is populated asynchronously by onAuthStateChange →
fetchProfile, so on Dashboard mount isAuthenticated was still false and
<Navigate to="/login" replace /> bounced the user right back.

Changes:
- Login.jsx: navigate inside a useEffect watching isAuthenticated instead
  of immediately after login(). Also supports an optional location.state.from
  so protected routes can round-trip back to their original destination.
- Signup.jsx: same pattern for the auto-sign-in case (no email confirmation).
- Dashboard.jsx / Analytics.jsx: show a loader while isLoading is true
  instead of redirecting. Pass location.pathname as state.from so after
  login the user returns to where they were going.
Fix sign-in race that bounced users back to /login
- Sidebar now overlays content on hover instead of pushing grid layout
- Added Sun/Moon theme toggle in TopBar with localStorage persistence
- Fixed routing so authenticated users stay in app shell
- Set default dark theme in index.html to prevent flash
- Updated TopBar action buttons with proper navigation links
- Remove unused lucide-react icon imports (CheckCircle, Zap, ArrowRight, Play)
- Collapse copy-paste sub-app cards, float cards, how-it-works steps, and
  footer columns into local data arrays + map(), matching the existing
  <step.icon /> rendering pattern used elsewhere in the codebase

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
- Remove unused lucide-react icon imports (CheckCircle, Zap, ArrowRight, Play)
- Collapse copy-paste sub-app cards, float cards, how-it-works steps, and
  footer columns into local data arrays + map(), matching the existing
  <step.icon /> rendering pattern used elsewhere in the codebase

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ

Co-authored-by: Claude <[email protected]>
…n system

- Complete the dark-theme ink scale (ink-300/400/500/700/900) and add
  translucent app-color -bg tints so semantic colors flip in dark mode,
  matching the prototype token system
- Drive splash heading/stat/brand colors from --color-ink-900 (which now
  flips) and drop the hardcoded #F4F7FB dark-mode overrides
- Add dark-mode topbar background (rgba navy) instead of a translucent
  white bar; reference --color-border for the topbar divider
- Add the prototype's app-colored 3px accent bar to dashboard sub-app tiles
- Remove dead components orphaned by the landing-page rebuild
  (Hero, HowItWorks, AppCard/AppList)

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
…n system (#5)

* refactor: drive splash landing sections from data arrays

- Remove unused lucide-react icon imports (CheckCircle, Zap, ArrowRight, Play)
- Collapse copy-paste sub-app cards, float cards, how-it-works steps, and
  footer columns into local data arrays + map(), matching the existing
  <step.icon /> rendering pattern used elsewhere in the codebase

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ

* fix: align dark-theme tokens and dashboard tiles with prototype design system

- Complete the dark-theme ink scale (ink-300/400/500/700/900) and add
  translucent app-color -bg tints so semantic colors flip in dark mode,
  matching the prototype token system
- Drive splash heading/stat/brand colors from --color-ink-900 (which now
  flips) and drop the hardcoded #F4F7FB dark-mode overrides
- Add dark-mode topbar background (rgba navy) instead of a translucent
  white bar; reference --color-border for the topbar divider
- Add the prototype's app-colored 3px accent bar to dashboard sub-app tiles
- Remove dead components orphaned by the landing-page rebuild
  (Hero, HowItWorks, AppCard/AppList)

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ

---------

Co-authored-by: Claude <[email protected]>
Full re-audit of the design handoff (Design System + Prototype + Showcase)
against the live app; this applies the verified top gaps:

- Brand: replace the improvised lucide Hexagon on the splash nav/footer
  with the real Fixars gear+F mark assets already shipped in /public
- Iconography: enforce the design system's 1.75px outline stroke globally
  for lucide icons; drop now-inert per-icon strokeWidth overrides
- Page-head grammar: add the prototype's page-head/eyebrow/icon/tag/title/sub
  CSS and a shared PageHead component; adopt it on all four sub-app pages
  with the prototype's app glyphs, taglines, and missions
- Wallet: add the prototype's radial gradient blob to both wallet hero cards
- Numerals: tabular figures on the .mono utility per the number-dense
  design principles

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
…doff

Phase 1 (foundation) of the v2 super-app redesign:

- Retune brand to deeper indigo (#2F45E0): primary/blue ramp/ring, plus
  decorative glow literals, replacing the electric blue. Logo mark nudged
  to match via a calibrated CSS filter (hue-rotate + desaturate) in the
  sidebar, mobile header, and splash nav.
- Align semantic tokens to the v2 spec (success #16A34A, warning #D97706,
  danger bg) in both light and dark, with matching dark-mode tint alphas.
- Add the sepia ("dim") theme and density/vibe token blocks (compact/cozy/
  spacious, focused/expressive/playful).
- Add ThemeContext as the single source of truth for theme/density/vibe,
  applied as data-* attributes on <html> and persisted; default is
  light · cozy · expressive. TopBar now consumes it.
- Vendor the approved v2 design handoff under design-handoff/fixars-v2 as
  the reference for subsequent phases.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
- Add time-aware greeting eyebrow (page-tag) and refine the home head to
  the v2 grammar.
- Add a 4-stat strip (FCS, Fixars points, active stakes, verified skills)
  with inline SVG sparklines pinned bottom-right, driven by live data.
- Port the shared v2 grammar to phase2.css: .stats-strip/.stat-card/.spark,
  the reusable .tag status-pill family, and .page-eyebrow/.page-tag — the
  expressive-vibe gradient bar now rides the stat cards by default.
- Drop two dead payment imports from Dashboard.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Command palette (SearchContext + SearchOverlay):
- Add a global ⌘K / Ctrl+K listener in SearchProvider (works app-wide),
  removing the duplicate listener that only lived in the public Header.
- Add a "Go to" pages group so the palette navigates routes; the empty
  query now lists all destinations instead of a hint.
- Full keyboard support: ↑↓ moves a highlighted selection, Enter opens
  (navigate or open the item), Esc closes; results capped at 9 and
  grouped (Go to / Ideas / Campaigns / Projects / Talent) with a key-hint
  footer.
- Expose a clean open/close/toggle API (plus a setIsSearchOpen alias) and
  fix the app-shell search box, which previously called a missing setter.

Notifications:
- Wire the existing NotificationDropdown popover into the app TopBar bell
  (unread badge, mark-all-read, type glyphs) in place of the page link.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Adopt the v2 wallet model so staking spends a real, shared balance:

- Add WalletContext: a single ₦ balance + transaction ledger persisted to
  localStorage, with a validated `spend`/`deposit` primitive and a navy
  toast (`notify`). Wired into the provider tree; <WalletToast/> renders
  the bottom-center pill.
- Add StakeFlowModal (the v2 "key flow"): backs a campaign from the wallet
  with an IRR/return banner, amount input + quick-amount chips, wallet
  source showing the live balance, a live projected-return range, and
  validation against the balance. Confirm deducts the wallet, increments
  the campaign (funded/backers via makeStake), toasts, and closes.
- VestDen now opens StakeFlowModal instead of the card PaymentModal; the
  modal is mounted only while active so it starts from fresh state.
- Wire the shared balance through WalletPage (balance + live ledger ahead
  of seeded history) and the Home "Wallet" stat card.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Bring all four sub-apps to v2 fidelity with a shared kit:

- Add SubAppKit (StatRow, Toolbar with search + segmented filter, ListGrid,
  EmptyState) and port the v2 grammar to phase2.css: .toolbar/.segment,
  .list-grid/.list-card (+title/desc/meta/progress), .av/.team-av,
  .skill-chip, .btn-app-*, .btn-ghost, .empty.
- ConceptNexus: idea list-cards (status tag + score + validation bar +
  reviewers) with live search and All/Submitted/In Validation/Validated
  segment; AI recommendations retained.
- vestDen: campaign list-cards (funding tag, days left, funded progress,
  target return, backers) with All/Funding now/Closing soon/Funded/My
  Portfolio filters that drive both the list and the stat strip.
- SkillsCanvas: talent list-cards (gradient avatar, verified tag, skill
  chips, rate · rating, projects) with availability filters.
- CollaBoard: project list-cards (derived status tag, task counts, team
  avatars) with All/My Boards/Active/In review/Done; kanban detail view
  preserved.

Each page now uses the shared PageHead + 4-stat strip + toolbar + 2-col
list grid with live filtering and a Clear-filters empty state.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Reconcile the Settings pickers to the real v2 theming axes and make them
live: theme (light/dim·sepia/dark), density (compact/cozy/spacious), and
vibe (focused/expressive/playful) now read from and write to ThemeContext,
applied as data-* attributes on <html> and persisted. Drops the dead local
state and the unused-user lint error.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Phase 4 refinements:
- Currency: switch the remaining $/USD displays to ₦ — Analytics total
  staked, BookingModal and CollaBoard Skill-Summoner rates, and the
  CreateStake/ListSkills amount labels + validation messages.
- FCS: reconcile the Profile gauge to the v2 300–850 scale, driven by the
  user's stored fcs, with banded labels (Excellent/Very good/Good/Fair/
  Building) and a matching ring color; range now reads 300 — 850.
- Accessibility: add a global prefers-reduced-motion guard that disables
  entrance/looping animations and long transitions, per the handoff.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
- ui/Modal now renders as a bottom-sheet on mobile (full-width, anchored
  to the bottom edge, rounded top corners, slide-up) and a centered card
  on sm+, with the v2 slim 3px app-accent bar and scroll-contained body
  (max-h + overflow) so long forms fit. This brings SubmitIdea, CreateBoard,
  CreateStake, ListSkills and AddTask to v2 modal chrome.
- Remove PaymentModal — dead since vestDen moved to the wallet-based
  StakeFlowModal.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
# Conflicts:
#	src/index.css
#	src/pages/Home.jsx
Redesign Fixars to the v2 super-app design system
Comprehensive pre-launch audit of the production codebase against the
Fixars Comprehensive PRD and the four sub-app PRDs. Flags the P0 blockers
(mock backend/auth, simulated money + escrow, missing KYC, non-existent
DMs, regulatory/NDPR gaps), trust/brand inconsistencies (Western seed
names, naming/logo conflicts, currency leftovers, FCS-vs-reputation scale),
feature gaps vs the v2 vision, and a recommended phased launch path.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
…chines)

Visual-first model of how the four sub-apps connect, pre-AI: the innovation
journey, the event pub/sub webbing (publisher -> event -> subscribers), and
the per-entity state machines (Concept/Campaign/Milestone/Engagement) with
deterministic guards. Includes the rules-table preview that the engine layer
will implement. Diagrams authored in Mermaid so they render on GitHub.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
The runnable form of the ecosystem webbing diagrams — connects the four
sub-apps via state machines + an event bus + a rules table.

- events.js: event catalog + payload schemas + tunables (threshold, fee)
- guards.js: pure, deterministic predicates (KYC, score, verified)
- stateMachines.js: Concept/Campaign/Milestone/Engagement lifecycles (View 3)
- effects.js: the THEN actions + an in-memory reference store
- rules.js: the algorithm — WHEN event -> IF guard -> THEN effects (View 2)
- engine.js: dispatcher with idempotency, compensation/sagas, bounded cascades
- index.js: public entry point
- demo.mjs: runnable end-to-end journey (24 assertions, exits non-zero on fail)
- README.md: architecture + how it maps to the diagrams + where AI plugs in

Backend-agnostic: swap the in-memory store/effects for Supabase/Paystack/
escrow without changing event names, machines, or rule structure. All code
thoroughly commented. `node src/engine/demo.mjs` passes 24/24.

https://claude.ai/code/session_018zosF6Byo1ieTvvk2EEvAZ
Ecosystem engine + launch-readiness review + visual webbing
…data integrity, schema hardening

Blockers:
- Enable Supabase when env vars are set (flag was hardcoded false); lazy
  client creation, no more placeholder URL
- RequireAuth wrapper on all private routes; signed-in users redirected
  away from /login and /signup
- Fix talent creation (missing userId arg), persist updateTalentProfile,
  map DB rows to the camelCase shape everywhere
- Fix boards query for members-less users (invalid id.in.() filter) and
  check all insert errors
- BookingModal now actually creates a skill_request via createBookingRequest

Data unification:
- Single talent store (DataContext); useTalents/useTalent read from it;
  TalentProfile/useReviews moved to camelCase
- Persist comments (new post_comments table + trigger), follows (new
  table), activities, and notification deletes
- Pass vote comment/badge through to idea_votes; task completion detected
  by target column title instead of UUID comparison

Reliability:
- Reference-counted realtime channels; debounced reconciliation refetches
- AuthContext: safe localStorage parse, single profile fetch via
  INITIAL_SESSION, updateUser only sends defined columns
- WalletContext: ref-mirror balance so concurrent spends can't overdraft
- Collaboard: derived board selection (no setState in render/effects)

Schema hardening (supabase/schema.sql, idempotent):
- notifications insert restricted to self; profiles rows hidden from anon
- post_comments, follows, payments tables + RLS; comment_count trigger
- create_dm_conversation upserts on a deterministic dm_key (unique index)
  so concurrent DM creation can't duplicate conversations

Hygiene:
- Currency unified to Naira; payments.js no longer sends raw card data
- api.js checks res.ok, clamps limits, docs match schema
- Dead buttons/links wired: mobile bell, mobile create, post share,
  forgot-password, landing CTAs/footer; dead kanban kebab removed
- Analytics sparklines memoized (no random-per-render); lazy useState
  initializers for impure mock data
- Add vitest + 18 unit tests (utils + engine cascade/idempotency)
@macaddy2
macaddy2 marked this pull request as draft August 22, 2026 18:03
…l/screenshot policy

Server-side money rails (#2):
- schema RPCs: award_points/spend_points (amounts resolved server-side),
  make_stake (validates state + refuses over-funding), wallet_balance,
  wallet_spend; wallet_transactions ledger (derived balance, no client writes)
- PointsContext/WalletContext switch to RPCs; stake flow persists BEFORE
  wallet debit (failed stakes never charge the wallet)
- Paystack Edge Functions: create-payment (hosted checkout, PCI stays with
  Paystack), verify-payment (verify + credit wallet); payments.js rewritten
  around them; WalletPage Fund button runs checkout + verifies on return

Engine activation (#3):
- src/engine/runtime.js bridges app -> @/engine: concept lifecycle cascades
  (validated -> campaign draft -> points/feed/notify), funded campaigns ->
  room+escrow mirrors; fail-soft everywhere; DB stays source of truth
- wired into DataContext submitIdea/voteIdea/makeStake; 4 new runtime tests

Hardening (#4):
- Gemini behind gemini-proxy Edge Function (key out of the browser bundle)
- ui/modal.jsx rebuilt on Radix Dialog (focus trap/restore); star ratings
  get radiogroup semantics
- Kanban drag-and-drop between columns wired to moveTask
- CI workflow (.github/workflows/ci.yml): lint + test + build
- eslint: ignore design-handoff/functions, add eslint-plugin-react
  jsx-uses-vars (fixes false 'unused' for JSX components), lint is fully green

Crawl policy (#5): landing page only indexable - robots.txt allow /\$ +
AI-bot blocks, default noindex meta flipped to index,follow only on Home,
X-Robots-Tag header for every other route via serve.json

Screenshot deterrence (#6): ScreenShield blanks UI on blur/hide, PrintScreen
clipboard wipe, print CSS lockdown (no watermarks)

Also: derived wallet stats replace hardcoded numbers; ActivityHeatmap uses
seeded pseudo-random (render purity)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants