fix(white-label): back-to-clients link points at the byra cockpit's home domain (#1973)

* fix(white-label): back-to-clients link points at the byra cockpit's home domain

A byra member working a company homed on another host (e.g. a pre-byra
company on canonical) got a relative /clients on the wrong host instead
of their white-label cockpit. resolveCockpitHref mirrors WL-14's home
rule: relative when the current host is the cockpit's home (brand domain,
or canonical for a brandless byra), else an absolute URL there. Cross-
host links render a plain <a> with a 'Hanteras via' hint; the hop lands
on the brand host's login (per-host sessions, WL-01) and WL-14 then
lands on /clients.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(white-label): show the cross-host cockpit hint as visible text

CodeRabbit: title-only hints never surface on touch devices, so the
expanded-sidebar and mobile external back-links now render the
'Hanteras via {domain}' line as small muted text under the label
(same pattern as the switcher's foreign entries). Also corrects the
comments claiming the no-company branch never renders the back-link:
it can, on its cockpit/settings surfaces, where the relative fallback
matches pre-change behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-08-27 13:44:23 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent a860c690ed
commit 99c94d467a
5 changed files with 240 additions and 25 deletions
+75
View File
@@ -4,6 +4,7 @@ import {
isCompanyHomedOnHost,
isCockpitLandingRole,
resolveLandingPath,
resolveCockpitHref,
type TeamBrandRef,
} from '../home-domain'
@@ -200,3 +201,77 @@ describe('resolveLandingPath', () => {
expect(resolveLandingPath({ hostBrandTeamId: 'byra-1', byraTeams: [] })).toBe('/')
})
})
describe('resolveCockpitHref', () => {
const brandByTeam = new Map([['byra-1', siffraBrand]])
const canonicalDomain = CANONICAL.canonicalDomain
it('brandless byrå on the canonical host stays relative', () => {
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam: new Map(),
hostBrandTeamId: null,
canonicalDomain,
}),
).toBe('/clients')
})
it('branded byrå on its own brand host stays relative', () => {
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam,
hostBrandTeamId: 'byra-1',
canonicalDomain,
}),
).toBe('/clients')
})
it('branded byrå on the canonical host points at the brand domain (problem 4)', () => {
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam,
hostBrandTeamId: null,
canonicalDomain,
}),
).toBe('https://app.siffra.se/clients')
})
it("branded byrå on someone ELSE's brand host points at its own brand domain", () => {
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam,
hostBrandTeamId: 'byra-2',
canonicalDomain,
}),
).toBe('https://app.siffra.se/clients')
})
it('brandless byrå on a foreign brand host points home to canonical (WL-14 symmetry)', () => {
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam: new Map(),
hostBrandTeamId: 'byra-2',
canonicalDomain,
}),
).toBe('https://app.gnubok.se/clients')
})
it('byrå with zero client companies still resolves via its own brand entry', () => {
// The layout adds the byrå team id to the resolveBrandsForTeams id list
// precisely so this map entry exists even when no membership company
// belongs to the byrå (pre-byrå companies have team_id null).
expect(
resolveCockpitHref({
byraTeamId: 'byra-1',
brandByTeam: new Map([['byra-1', siffraBrand]]),
hostBrandTeamId: null,
canonicalDomain,
}),
).toBe('https://app.siffra.se/clients')
})
})
+32
View File
@@ -129,3 +129,35 @@ export function resolveLandingPath(opts: {
// Canonical host: only a brandless byrå homes its cockpit here.
return opts.byraTeams.some((t) => !t.hasBrand) ? '/clients' : '/'
}
/**
* Href for the pinned "Tillbaka till klienter" link: the byrå cockpit lives
* on the byrå's home domain (its brand domain when the team has a brand, else
* the canonical domain, same rule as WL-14's resolveLandingPath). Relative
* '/clients' when the current host IS that home; an absolute URL otherwise,
* so a byrå member working a company homed on a different host (e.g. a
* pre-byrå company on canonical) is sent to their white-label cockpit.
*
* Navigation rule only, like everything in this file: the hop lands on the
* brand host's login when there is no session there (per-host sessions,
* WL-01), and WL-14 then lands the user on /clients.
*/
export function resolveCockpitHref(opts: {
/** teams.id of the user's byrå team. */
byraTeamId: string
brandByTeam: Map<string, TeamBrandRef>
/** teams.id of the brand serving the current host, null on canonical. */
hostBrandTeamId: string | null
canonicalDomain: string
}): string {
const brand = opts.brandByTeam.get(opts.byraTeamId)
if (brand) {
return opts.hostBrandTeamId === opts.byraTeamId
? '/clients'
: `https://${brand.domain}/clients`
}
// Brandless byrå: the cockpit is homed on the canonical domain.
return opts.hostBrandTeamId === null
? '/clients'
: `https://${opts.canonicalDomain}/clients`
}