fix(uat): batch — timeline overshoot, name-sync, reset-password, dashboard cleanup, queue/seed hygiene + alpha UAT findings doc
UAT findings landed across the last few Playwright + React Grab passes; single grouped commit so the index doesn't fragment into 30 one-liners. User & auth: - `user-settings`: name now updates the avatar + topbar menu after save (was reading stale session). - `me/password-reset`: 3 bugs (token validation, error response shape, redirect chain). - Admin user permission-overrides route honours the same envelope as the rest of the admin surface. Dashboard: - Removed obsolete `revenue-breakdown-chart` + `dashboard-widgets-card` (replaced by the customisable widget grid). - Strip `revenue_breakdown` from analytics route + use-analytics + service + integration test so nothing renders an empty card. - Activity log timeline overshoot fix (`interest-timeline` + `entity-activity-feed`). - Tightened tiles: active-deals, berth-heat-widget, pipeline-value, kpi-tile. - `dev-mode-banner`: derive dismissed state synchronously instead of via an effect (set-state-in-effect lint rule). Forms & lists (assorted polish): - client / company / yacht / interest / reminder forms — validation + empty-state copy + tab transitions. - companies/yachts list tweaks; berth recommender panel; qualification checklist; supplemental info request button. Infra & misc: - Queue workers (ai / email / notifications) — log shape + per-job timeout consistency. - Auth / brochures / users schema small adjustments; seeds reflect permissions matrix changes. - Scan shell + scanner manifest + AI admin page small fixes. - `next.config.transpilePackages` adds `echarts`/`zrender`/`echarts-for-react` (recommended config from echarts-for-react inside Next). Docs: - `docs/superpowers/audits/alpha-uat-master.md` — single rolling cross-cutting UAT findings doc (per CLAUDE.md convention). - `docs/BACKLOG.md`: dashboard stats cards (§I) + activity-log normalization (§J). - 2026-05-18 audit log updated with this batch. - `CLAUDE.md` — small manual UAT scaffold notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -313,7 +313,13 @@ export function makeFullPermissions(): RolePermissions {
|
||||
generate_eoi: true,
|
||||
export: true,
|
||||
},
|
||||
berths: { view: true, edit: true, import: true, manage_waiting_list: true },
|
||||
berths: {
|
||||
view: true,
|
||||
edit: true,
|
||||
import: true,
|
||||
manage_waiting_list: true,
|
||||
update_prices: true,
|
||||
},
|
||||
documents: {
|
||||
view: true,
|
||||
create: true,
|
||||
@@ -395,7 +401,13 @@ export function makeViewerPermissions(): RolePermissions {
|
||||
generate_eoi: false,
|
||||
export: false,
|
||||
},
|
||||
berths: { view: true, edit: false, import: false, manage_waiting_list: false },
|
||||
berths: {
|
||||
view: true,
|
||||
edit: false,
|
||||
import: false,
|
||||
manage_waiting_list: false,
|
||||
update_prices: false,
|
||||
},
|
||||
documents: {
|
||||
view: true,
|
||||
create: false,
|
||||
@@ -477,7 +489,13 @@ export function makeSalesAgentPermissions(): RolePermissions {
|
||||
generate_eoi: true,
|
||||
export: false,
|
||||
},
|
||||
berths: { view: true, edit: false, import: false, manage_waiting_list: false },
|
||||
berths: {
|
||||
view: true,
|
||||
edit: false,
|
||||
import: false,
|
||||
manage_waiting_list: false,
|
||||
update_prices: false,
|
||||
},
|
||||
documents: {
|
||||
view: true,
|
||||
create: true,
|
||||
@@ -559,7 +577,13 @@ export function makeSalesManagerPermissions(): RolePermissions {
|
||||
generate_eoi: true,
|
||||
export: true,
|
||||
},
|
||||
berths: { view: true, edit: true, import: false, manage_waiting_list: true },
|
||||
berths: {
|
||||
view: true,
|
||||
edit: true,
|
||||
import: false,
|
||||
manage_waiting_list: true,
|
||||
update_prices: true,
|
||||
},
|
||||
documents: {
|
||||
view: true,
|
||||
create: true,
|
||||
|
||||
@@ -8,12 +8,10 @@ import { eq, and } from 'drizzle-orm';
|
||||
|
||||
import { db } from '@/lib/db';
|
||||
import { interests, interestBerths } from '@/lib/db/schema/interests';
|
||||
import { invoices } from '@/lib/db/schema/financial';
|
||||
import { analyticsSnapshots } from '@/lib/db/schema/insights';
|
||||
import {
|
||||
computePipelineFunnel,
|
||||
computeOccupancyTimeline,
|
||||
computeRevenueBreakdown,
|
||||
computeLeadSourceAttribution,
|
||||
getPipelineFunnel,
|
||||
refreshSnapshotsForPort,
|
||||
@@ -108,33 +106,6 @@ describe('analytics service', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeRevenueBreakdown', () => {
|
||||
it('groups invoice totals by status and currency', async () => {
|
||||
const port = await makePort();
|
||||
const baseInvoice = {
|
||||
portId: port.id,
|
||||
clientName: 'Acme',
|
||||
billingEntityType: 'client' as const,
|
||||
billingEntityId: 'client-id',
|
||||
dueDate: '2026-12-31',
|
||||
currency: 'USD',
|
||||
subtotal: '0',
|
||||
createdBy: 'seed',
|
||||
};
|
||||
await db.insert(invoices).values([
|
||||
{ ...baseInvoice, invoiceNumber: 'INV-001', total: '1000', status: 'paid' },
|
||||
{ ...baseInvoice, invoiceNumber: 'INV-002', total: '500', status: 'paid' },
|
||||
{ ...baseInvoice, invoiceNumber: 'INV-003', total: '2000', status: 'sent' },
|
||||
]);
|
||||
|
||||
const result = await computeRevenueBreakdown(port.id, '30d');
|
||||
const paid = result.bars.find((b) => b.status === 'paid');
|
||||
const sent = result.bars.find((b) => b.status === 'sent');
|
||||
expect(paid?.amount).toBe(1500);
|
||||
expect(sent?.amount).toBe(2000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeLeadSourceAttribution', () => {
|
||||
it('counts interests grouped by source descending', async () => {
|
||||
const port = await makePort();
|
||||
|
||||
Reference in New Issue
Block a user