Fix/multiple company (#203)

* Refactor onboarding and dashboard logic; add silent team creation for users

- Removed unnecessary useCompany context in DashboardContent and SettingsSidebar components.
- Simplified onboarding setup logic to allow direct access to the dashboard for users without companies.
- Introduced WelcomeOnboarding component to handle user onboarding steps.
- Added migration to create silent teams for all users at signup, backfilling existing users without teams, and cleaning up incomplete companies.

* fix: update greeting logic and improve email handling in TIC extension

* Redirect to onboarding for users without companies and update onboarding flow

* Build issue fix

* Enhance onboarding experience by adding existing companies check
This commit is contained in:
Mattsson
2026-04-09 11:54:12 +02:00
committed by GitHub
parent dec9a37d2d
commit bf5a8d9195
24 changed files with 1101 additions and 1173 deletions
+18
View File
@@ -1,3 +1,21 @@
/**
* A person's role/position in a Swedish company, from BankID enrichment.
* Defined in core so onboarding components can import it without
* violating the CI constraint (no core → @/extensions/ imports).
*/
export interface EnrichmentCompanyRole {
companyId: number
companyRegistrationNumber: string
legalName: string
legalEntityType: string
positionTypes: string[]
positionDescriptions: string[]
positionStart: string
positionEnd: string | null
companyStatus: string
signatureDescription?: string
}
/**
* Generic company lookup result — provider-agnostic.
* Defined in core so onboarding components can import it without
+7 -66
View File
@@ -58,23 +58,8 @@ export async function updateSession(request: NextRequest) {
pathname.startsWith('/sandbox') ||
pathname.startsWith('/invite')
) {
// If user is logged in and trying to access auth pages, redirect to dashboard or onboarding
// If user is logged in and trying to access auth pages, redirect to dashboard
if (user) {
const companyId = await resolveCompanyForMiddleware(supabase, user.id, request)
if (!companyId) {
return NextResponse.redirect(new URL('/onboarding', request.url))
}
const { data: settings } = await supabase
.from('company_settings')
.select('onboarding_complete')
.eq('company_id', companyId)
.single()
if (!settings?.onboarding_complete) {
return NextResponse.redirect(new URL('/onboarding', request.url))
}
return NextResponse.redirect(new URL('/', request.url))
}
return supabaseResponse
@@ -102,8 +87,9 @@ export async function updateSession(request: NextRequest) {
}
// MFA required but user has no factor enrolled yet → force enrollment
// (skip during onboarding — let them finish setup first)
if (!pathname.startsWith('/onboarding')) {
// Skip for users with no companies (still setting up)
const companyIdForMfa = await resolveCompanyForMiddleware(supabase, user.id, request)
if (companyIdForMfa) {
const { data: factors } = await supabase.auth.mfa.listFactors()
const hasVerifiedFactor = factors?.totp?.some(f => f.status === 'verified')
@@ -116,27 +102,8 @@ export async function updateSession(request: NextRequest) {
// Company context resolution
const companyId = await resolveCompanyForMiddleware(supabase, user.id, request)
// No companies at all
// No companies — only allow /onboarding, redirect everything else there
if (!companyId) {
// Check if user is in a team (consultant without companies yet).
// Team members should reach the dashboard (which shows an empty state)
// rather than being forced through company onboarding.
const { data: teamMember } = await supabase
.from('team_members')
.select('id')
.eq('user_id', user.id)
.limit(1)
.maybeSingle()
if (teamMember) {
// Consultant with a team but no companies — allow dashboard access
if (pathname.startsWith('/onboarding')) {
return NextResponse.redirect(new URL('/', request.url))
}
return supabaseResponse
}
// No team, no companies → redirect to onboarding
if (pathname.startsWith('/onboarding')) {
return supabaseResponse
}
@@ -152,37 +119,11 @@ export async function updateSession(request: NextRequest) {
maxAge: 60 * 60 * 24 * 365,
})
// Select-company page — allow access (for switching companies)
if (pathname.startsWith('/select-company') || pathname.startsWith('/companies/new')) {
// Allow access to onboarding (for adding new companies), select-company, and companies/new
if (pathname.startsWith('/select-company') || pathname.startsWith('/companies/new') || pathname.startsWith('/onboarding')) {
return supabaseResponse
}
// Onboarding route - only accessible if not complete
if (pathname.startsWith('/onboarding')) {
const { data: settings } = await supabase
.from('company_settings')
.select('onboarding_complete')
.eq('company_id', companyId)
.single()
if (settings?.onboarding_complete) {
return NextResponse.redirect(new URL('/', request.url))
}
return supabaseResponse
}
// Dashboard routes - require completed onboarding for active company
const { data: settings } = await supabase
.from('company_settings')
.select('onboarding_complete')
.eq('company_id', companyId)
.single()
if (!settings?.onboarding_complete) {
return NextResponse.redirect(new URL('/onboarding', request.url))
}
return supabaseResponse
}