feat(uat-b1): ship Wave A-E of Bucket 1 audit findings
Wave A (Interest+EOI form quick wins): - Auto-select yacht after inline-create from interest form - EOI generate dialog: "View EOI" action toast - Interest form berth picker: formatBerthRange compact label - Remove "Generate EOI" button from Documents tab (clean removal) - Interest auto-assign: only sales_agent/sales_manager auto-claim ownership on create (explicit role check via user_port_roles join) - LinkedBerthRowItem dims: drop "D" suffix + "L × W" format - ExternalEoiUploadDialog: prefillSignatories prop threaded from active EOI signers - EOI signature progress on Overview milestone card footer Wave B (a11y + i18n sweeps): - aria-live on supplemental-info error state - text-[10px] -> text-xs in client-pipeline-summary - Currency formatter: locale default removed (Intl uses runtime) - en-US/en-GB hardcoded toLocaleString swept across 13 components Wave C (Primary berth always in EOI bundle): - Service guard strengthened on update path - Migration 0083 backfills historical primary rows Wave D (Onboarding super_admin discoverability): - /api/v1/admin/onboarding/status endpoint + shared service - Topbar OnboardingBanner (super_admin, session-dismissible) - OnboardingTile dashboard widget (rail group, self-hides at 100%) - Celebration toast + invalidate of shared status on last tick Wave E (Branded post-completion email idempotency): - Verified handleDocumentCompleted already owns the email fan-out - Added regression test for the polling path + idempotency Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import { SocketProvider } from '@/providers/socket-provider';
|
||||
import { PortProvider } from '@/providers/port-provider';
|
||||
import { PermissionsProvider } from '@/providers/permissions-provider';
|
||||
import { AppShell } from '@/components/layout/app-shell';
|
||||
import { OnboardingBanner } from '@/components/admin/onboarding-banner';
|
||||
import { DevModeBanner } from '@/components/shared/dev-mode-banner';
|
||||
import { RealtimeToasts } from '@/components/shared/realtime-toasts';
|
||||
import { WebVitalsReporter } from '@/components/shared/web-vitals-reporter';
|
||||
@@ -84,6 +85,7 @@ export default async function DashboardLayout({ children }: { children: React.Re
|
||||
rerouted. Production hides itself (env.ts forbids the
|
||||
flag in prod) so the banner is dev/staging-only. */}
|
||||
<DevModeBanner />
|
||||
<OnboardingBanner />
|
||||
{/* #26: AppShell mounts ONE responsive tree (desktop OR
|
||||
* mobile) per render - never both - so pages don't pay the
|
||||
* double-state, double-fetch, double-Tabs-provider tax. */}
|
||||
|
||||
24
src/app/api/v1/admin/onboarding/status/route.ts
Normal file
24
src/app/api/v1/admin/onboarding/status/route.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { resolveOnboardingStatus } from '@/lib/services/onboarding.service';
|
||||
|
||||
/**
|
||||
* GET /api/v1/admin/onboarding/status — returns the resolved checklist
|
||||
* state for the caller's port. Drives the topbar discoverability banner
|
||||
* + dashboard onboarding tile + the admin checklist page summary.
|
||||
*
|
||||
* Permission-gated on `admin.manage_settings` to mirror the rest of the
|
||||
* admin surface; non-admin users never see the banner / tile anyway.
|
||||
*/
|
||||
export const GET = withAuth(
|
||||
withPermission('admin', 'manage_settings', async (_req, ctx) => {
|
||||
try {
|
||||
const status = await resolveOnboardingStatus(ctx.portId);
|
||||
return NextResponse.json({ data: status });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
@@ -151,7 +151,7 @@ export default function SupplementalInfoPage({ params }: PageProps) {
|
||||
if (error) {
|
||||
return (
|
||||
<BrandedAuthShell>
|
||||
<div className="text-center space-y-2 py-6">
|
||||
<div role="alert" aria-live="assertive" className="text-center space-y-2 py-6">
|
||||
<p className="text-sm text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
</BrandedAuthShell>
|
||||
|
||||
Reference in New Issue
Block a user