# Local development

Local dev for `frontend-workstation` currently runs against the workstation
graphql-gateway, with the `frontend_workstation_graphql_router` FF doing a
runtime rewrite to graphql-router when it is on (the FF is on in QA, so
the runtime path is the router; the env var still points at the gateway).
This document covers how to bring a local dev environment up against that
baseline, including how to impersonate a label and the quirks that still
exist while the migration off legacy grass-session auth lands.

There is no production-backed local-dev path. Local development always
targets QA.

## Prerequisites

-   VPN connection.
-   Auth0 env vars set correctly in `.env`. See
    [configuration](configuration.md). A wrong `AUTH0_CLIENT_ID` produces an
    opaque error screen on first load.

## Running the app

```bash
pnpm install
cp .env.shadow .env  # if you don't already have one
pnpm start           # serves on http://localhost:8080
```

The first load redirects you through Auth0 SSO. Sign in with your regular
`username@sonymusic-pde.com` account.

## Loading a label

Two paths to load the workstation as a specific label locally. Pick by
how often you'll touch the label.

### Preferred: grant yourself the label once, then switch

If a label is going to be in your rotation, grant your own user access to
it once via `pnpm grant-label-to-user` in
[theorchard/qa-automation-tools](https://github.com/theorchard/qa-automation-tools/blob/master/src/grant-label-to-user/README.md)
(that link is the tool's full howto). The CLI opens a Neo4j cypher PR
(`python-neo4j-cypher-scheduler`) and an art_relations SQL PR
(`python-rds-utils`), and the two-PR shape means the grant survives QA
database refreshes. When prompted, choose immediate apply if you need
access before the PRs merge.

This assumes your Sony SSO identity already exists in QA Auth0 + Neo4j; if
the CLI cannot find it, follow its error output before using this path.

After access is granted, locally:

1. Sign in normally through Auth0 SSO.
2. Open the account switcher at the top-right of the workstation header.
3. Pick the label you want.

No URL pasting, no impersonation banner.

### One-off: impersonate via OA

For labels you dont have access to yet, or one-off debugging on a label
you wont touch again, get the `?impersonate=alw:<vendContactId>` slug
from OA:

1. Go to OA (https://oa.qaorch.com), open the Vendor / label you want.
2. On the Label Contacts table, click the **Workstation Impersonate**
   link on the contact you want to log in as. The companion **Workstation
   Login** link next to it is the legacy grass-session path and no longer
   works against the new flow, dont use it.
3. OA opens `https://workstation.qaorch.com/?impersonate=alw:<vendContactId>`
   in a new tab. Copy the path + query (`/?impersonate=alw:<vendContactId>`)
   onto your local host so you end up at
   `http://localhost:8080/?impersonate=alw:<vendContactId>`.

The app will issue a JWT through Auth0, write the impersonated identity
into your session, and load the workstation as that contact. When it works
youll see a yellow banner at the top saying you are impersonating that
contact.

Impersonation survives a page refresh (and browser restart for the JWT
lifetime) because Auth0 stores the impersonated contact id in your
`user_metadata`, so silent token refresh re-applies it.

If you previously had a stale session, the `?impersonate=` URL now takes
precedence over any leftover grass session, so you can paste a new
impersonation URL at any time without clearing cookies first.

To exit impersonation without picking a new target, either click
**End Impersonation** in the yellow banner at the top of the app, or open:

```
http://localhost:8080/?impersonate=end
```

Either path takes you back to your own user's main LabelProfile. If your
user doesnt have a LabelProfile, you land on a signed-in shell with no
vendor context (effectively nowhere useful), so just start a new
`?impersonate=alw:<id>` instead.

**When switching between different labels in the same session, end the
current impersonation first** and then start the new one. Hopping directly
from one `?impersonate=alw:<id>` to another without ending in between can
leave bits of the previous impersonated session underneath, which surfaces
as confusing UI state or stale data.

## When an Impersonate link errors

OA still shows Impersonate links for some contacts whose data shape the
new flow cant handle. The link renders fine, the click errors. If that
happens, scroll the Label Contacts table on the same Vendor page and try
another contact, at least one usually works.

`theorchard/orchard#17800` adds a render-time filter so OA only shows
links we can actually impersonate. When that lands this section becomes
obsolete.

## Testing against different vendor states

Older versions of `.env.shadow` exposed `USER_FEATURES`, `USER_RESTRICTIONS`
and `USER_PERMISSIONS` as env-var overrides. Those vars are no longer wired
into the bundle (`frontend.config.js` ships `userFeatureFlags: {}` and
`userRestrictedFeatures: []` hardcoded). They have been removed from
`.env.shadow`. Don't reintroduce them.

The supported way to test the app under a specific set of products,
features, permissions or restrictions is to **impersonate a vendor that
already has that state on QA**, rather than hacking your own dev process.
That keeps the work reproducible: a reviewer pulling your PR (or hitting
the PRI) only needs to impersonate the same vendor to see exactly what you
saw, with no per-developer env divergence.

When you genuinely need a single feature flag flipped for a session and
dont want to wait for a Split change, two paths exist:

-   **Split vendor-targeting:** add the vendor id to the flag's allowlist in
    Split admin. Persists across reloads and is visible to reviewers.
-   **URL feature-flag override:** append
    `?enabled_feature_flag=<flag>&disabled_feature_flag=<flag>` to the app
    URL. Session-scoped, no admin write needed.

Prefer Split targeting when the test will be repeated; prefer the URL
override for one-off checks.
