Users entering the full endpoint URL (e.g. https://openrouter.ai/api/v1/chat/completions)
caused a 404 because the code appended /chat/completions again. Now strips any trailing
/chat/completions before re-appending, and adds a hint in the UI.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new admin-only feature that allows the platform owner to benchmark
the production AI model against up to 2 alternate models (any OpenAI-compatible
API) using real tenant data, without impacting users.
Backend:
- Shared AI caller utility (ai-caller.ts) for OpenAI-compatible endpoints
- Shadow AI module with service, controller, and 3 entities
- 6 admin API endpoints for model config CRUD, run trigger, and history
- Auto-creates shadow_ai_models, shadow_runs, shadow_run_results tables
- Exposes health-scores and investment-planning prompt builders for reuse
Frontend:
- New admin page at /admin/shadow-ai with 3 tabs:
- Model Configuration (production + 2 alternate slots)
- Run Comparison (tenant select, feature select, side-by-side results)
- History (filterable run log with detail drill-down)
- Full side-by-side output display with diff highlighting
- Sidebar navigation link for AI Benchmarking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a dedicated super admin page for managing idea submissions across
all tenants. Includes status summary cards, filterable/searchable table,
detail modal with status updates, and private admin notes for internal
tracking (sprint refs, thoughts, follow-ups). Notes are not visible to
tenant users.
- Database: admin_note column on shared.ideas (019 migration)
- Backend: PUT /admin/ideas/:id/note endpoint
- Frontend: AdminIdeasPage with table, filters, detail modal
- Sidebar: "Idea Submissions" nav link in admin sections
- Routing: /admin/ideas route under SuperAdminRoute guard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds idea submission capability gated by a per-tenant feature flag.
Super admins can enable/disable ideation for specific tenants via the
admin tenant detail drawer. Users see a lightbulb icon in the header
when enabled, opening a modal to submit ideas (title + description).
Ideas are stored in shared schema for cross-tenant backlog querying.
- Database: shared.ideas table (018-ideas.sql migration)
- Backend: Ideas NestJS module (entity, service, controller)
- Admin API: GET /admin/ideas, PUT /admin/ideas/:id/status,
PUT /admin/organizations/:id/settings
- Frontend: IdeaModal component, lightbulb ActionIcon in header
- Admin UI: Feature Toggles card with ideation Switch in drawer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remaining life years is documentation-only reference info. The board's
planned project date is the authoritative timeline for urgency assessment.
Updates data gathering, prompt construction, and system instructions to
base all urgency on target_year/target_month instead of remaining_life_years.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move useMemo hook above early returns to satisfy React Rules of Hooks,
fixing blank screen when navigating to scenario detail. Also re-fetch
scenario after projection updates so auto-renew renewal records appear
automatically without requiring manual navigation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lock InvestmentTimeline and ProjectionChart to shared X axis range
- Auto-create renewal scenario_investments records when auto_renew is true
- Add fund transfer mechanism between asset accounts with journal entries
- Add Capital Planning Report (5-year forecast grouped by category)
- Add Upcoming Investment Activities dashboard card (maturities + planned purchases)
- Bump version to 2026.3.24
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Onboarding wizard: add Reserve Account step between Operating and Assessments,
redirect to Budget Planning on completion
- Dashboard: health score pending state shows clickable links to set up missing items
- Projects/Vendors: rich empty-state wizard screens with real-world examples and CTAs
- Investment Planning: auto-refresh AI recommendations when empty or stale (>30 days)
- Hide Invoices and Payments menus (see PARKING-LOT.md for re-enablement)
- Send welcome email via Resend when new members are added to a tenant
- Enforce 5-member limit for Starter/Standard/Professional plans (Enterprise unlimited)
- Cash flow forecast: only mark months as "Actual" when journal entries exist,
fixing the issue where months without data showed as actuals
- Bump version to 2026.3.19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix "Manage Billing" button error for trial orgs without Stripe customer;
add fallback to retrieve customer from subscription, show helpful message
for trial users, and surface real error messages in the UI
- Add "Balance As-Of Date" field to onboarding wizard so opening balance
journal entries use the correct statement date instead of today
- Add "Total Unit Count" field to onboarding wizard assessment group step
so cash flow projections work immediately
- Remove broken budget upload step from onboarding wizard (was using legacy
budgets endpoint); replace with guidance to use Budget Planning page
- Replace bare "No budget plan lines" text with rich onboarding-style card
featuring download template and upload CSV action buttons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Interactive CLI for managing test organizations, users, and tenant schemas.
Supports list, delete-org, delete-user, purge-all, and reseed commands
with dry-run mode and safety guards for platform owner protection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add monthly/annual billing toggle with 25% annual discount on pricing page
- Implement 14-day no-card free trial (server-side Stripe subscription creation)
- Enable upgrade/downgrade via Stripe Customer Portal
- Add admin-initiated ACH/invoice billing for enterprise customers
- Add billing card to Settings page with plan info and Manage Billing button
- Handle past_due status with read-only grace period access
- Add trial ending and trial expired email templates
- Add DB migration for billing_interval and collection_method columns
- Update ONBOARDING-AND-AUTH.md documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the 4 summary cards from the Cash Flow page as they don't
properly represent the story over time. Increase gradient opacity
on stacked area charts (cash flow and investment scenarios) from
0.3-0.4/0-0.05 to 0.6/0.15 for better visual shading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the stubbed email service with Resend API integration.
Emails are sent with branded HTML templates including activation,
welcome, payment failed, member invite, and password reset flows.
- Install resend@6.9.4 in backend
- Rewrite EmailService with Resend SDK + graceful fallback to
stub mode when API key is not configured
- Add branded HTML email template with CTA buttons, preheader
text, and fallback URL for all email types
- Add reply-to support (sales@hoaledgeriq.com in production)
- Track send status (sent/failed) in shared.email_log metadata
- Add RESEND_API_KEY, RESEND_FROM_ADDRESS, RESEND_REPLY_TO env
vars to both docker-compose.yml and docker-compose.prod.yml
- Add sendPasswordResetEmail() method for future use
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
APP_URL was never passed to the backend container, causing Stripe
checkout success_url to redirect to http://localhost instead of the
production domain. The prod overlay also completely replaced the base
environment block, dropping all Stripe, SSO, WebAuthn, and invite
token variables.
- Add APP_URL to base docker-compose.yml (default: http://localhost)
- Add all missing vars to docker-compose.prod.yml with production
defaults (app.hoaledgeriq.com)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enterprise plan no longer displays a fixed price. Instead it shows
"Request Quote" and the CTA opens the interest form on hoaledgeriq.com
in a new tab to capture leads for custom quotes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- L2: Add server_tokens off to nginx configs to hide version
- M1: Add X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
Permissions-Policy headers to all nginx routes
- L3: Add global NoCacheInterceptor (Cache-Control: no-store) on all
API responses to prevent caching of sensitive financial data
- C1: Disable open registration by default (ALLOW_OPEN_REGISTRATION env)
- H3: Add logout endpoint with correct HTTP 200 status code
- M2: Implement full password reset flow (forgot-password, reset-password,
change-password) with hashed tokens, 15-min expiry, single-use
- Reduce JWT access token expiry from 24h to 1h
- Add EmailService stub (logs to shared.email_log)
- Add DB migration 016 for password_reset_tokens table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove redundant Settings link from sidebar (accessible via user menu)
- Move Transactions section below Board Reference for better grouping
- Promote Investment Scenarios to its own top-level sidebar item
- Add Compact View preference with tighter spacing theme
- Wire compact theme into MantineProvider with dynamic switching
- Enable Compact View toggle in both Preferences and Settings pages
- Install missing @simplewebauthn/browser package (lock file update)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers Stripe billing flow, provisioning pipeline, activation magic links,
onboarding checklist, refresh tokens, MFA, SSO, passkeys, env var reference,
manual intervention checklist, and full API endpoint reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the Planning section. Move Projects and Capital Planning (as
sub-item) into Board Planning. Move Investment Planning with Investment
Scenarios as sub-item into Board Planning. Move Vendors into new Board
Reference section. Board Planning order: Budget Planning, Projects >
Capital Planning, Assessment Scenarios, Investment Planning > Investment
Scenarios, Compare Scenarios. Sidebar now supports parent items with
their own route plus nested children.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the Planning section. Move Projects and Capital Planning (as
sub-item) into Board Planning. Move Investment Planning with Investment
Scenarios as sub-item into Board Planning. Move Vendors into new Board
Reference section. Board Planning order: Budget Planning, Projects >
Capital Planning, Assessment Scenarios, Investment Planning > Investment
Scenarios, Compare Scenarios. Sidebar now supports parent items with
their own route plus nested children.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Refresh Recommendations now shows inline processing banner with
animated progress bar while keeping existing results visible (dimmed).
Auto-scrolls to AI section and shows titled notification on completion.
- Investment recommendations now auto-calculate purchase and maturity
dates from a configurable start date (defaults to today) in the
"Add to Plan" modal, so scenarios build projections immediately.
- Projection engine computes per-investment and total interest earned,
ROI percentage, and total principal invested. Summary cards on the
Investment Scenario detail page display these metrics prominently.
- Replaced dropdown action menu with inline Edit/Execute/Remove
icon buttons matching the assessment scenarios pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When adding a multi-stage investment strategy (e.g. CD ladder) from AI
recommendations to a board planning scenario, all component investments
are now created as separate rows instead of collapsing into a single
investment. The AI prompt now instructs inclusion of a components array,
the backend loops through components to create individual scenario
investments, and the frontend passes and displays component details.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reorder sidebar: Assessment Scenarios now directly under Budget Planning
- Simplify special assessment form: remove Total Amount, keep Per Unit only
- Replace Duration field from free-text installments to dropdown (one-time/quarterly/6mo/annual)
- Update Change column display to show total per-unit with duration label
- Fix Reserve Coverage to use planned capital project costs instead of budget expenses
- Include capital_projects table in projection engine alongside projects table
- Replace actions dropdown menu with inline Edit/Remove icon buttons
- Remove Refresh Projection button (projection auto-refreshes on changes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use CSS drop-shadow filter on the logo img in dark mode to create a
subtle white outline that helps the transparent-background logo stand
out against the dark header and login page backgrounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The budgetData was stored in a separate useState and updated inside
queryFn. When switching years, React Query served cached data with
isLoading=false but the local state still held the previous year's
data, causing the "no budget" empty state to flash intermittently.
Fix: Use query data directly as source of truth. Local state (editData)
is only used when actively editing. Added a small spinner indicator
when refetching in the background.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add new logo.png (2090x512) with transparent background
- Update AppLayout and LoginPage imports from .svg to .png
- Old SVG had opaque background that clashed with dark theme
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix compound inflation: use Math.pow(1 + rate/100, yearsGap) instead of
flat rate so multi-year gaps (e.g., 2026→2029) compound annually
- Budget Planner: add CSV import flow + Download Template button; show proper
empty state when no base budget exists with Create/Import options
- Budget Manager: remove CSV import, Download Template, and Save buttons;
redirect users to Budget Planner when no budget exists for selected year
- Fix getAvailableYears to return null latestBudgetYear when no budgets exist
and include current year in year selector for fresh tenants
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The accounts table uses 'name' not 'account_name'. Alias it correctly
in the getPlan JOIN query.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>