feat(client): link a Job/Contract employer to an Organization in the UI (#375) #378

Merged
james merged 1 commit from 375-job-org-link-client into main 2026-06-29 17:00:08 +00:00
Owner

Completes #375 — the client UI on top of the merged backend (#376).

What's in it

  • @carol/api-client: useOrganizationRoles(id) over GET /api/organizations/{id}/roles; keys.organizations.roles(id). (No generated-file changes — the endpoint/DTO were already generated; check stays clean.)
  • Job/Contract form (experience.tsx): the shared JobForm gains an "Employer" toggle — free text (default) vs linked organisation. Linked mode picks one of the user's orgs (useOrganizations); the company is derived server-side from the org. organizationId threads through create/update for both jobs and contracts; edit preselects the linked org; the linked option is disabled with a hint when there are no tracked orgs.
  • Tap-through (CompanyName): a linked employer renders as an accent link to the Org detail (showing the live organizationName); free-text employers stay plain Text. Wired into both list rows + both detail headers.
  • Org detail (network/orgs/[id].tsx): a read-only "your roles here" card listing the org's linked jobs/contracts (Job/Contract badge + date range) via useOrganizationRoles.
  • i18n: experience.jobs.form.* + network.organizationDetail.roles.* (British "organisation" to match the existing copy — 28 existing vs 5).

Verification (local, re-run in the worktree)

  • @carol/api-client: typecheck ✓ · lint ✓ · test ✓ 44 · check ✓ (generated untouched)
  • @carol/client: typecheck ✓ · lint ✓ · test ✓ 172
  • i18n valid; I verified the new keys resolve relative to their namespace (the literal-key-leak trap) — experience.jobs.form.employerLabel → "Employer", network.organizationDetail.roles.title → "Your roles here", etc.
  • semgrep ✓ 0 findings on the changed source

⚠️ Needs maintainer verification (headless limit)

Client tests are node-env. Please eyeball in a running build:

  • The employer free-text↔linked toggle + org picker (disabled/empty-orgs hint; edit-mode preselect).
  • The company tap-through to /network/orgs/{id} from all four spots. One thing to confirm specifically: in the list rows, CompanyName is a Pressable nested inside the row's drill-down Pressable. RN's responder system should grant the inner link the tap (→ org) while taps elsewhere on the row open the job/contract — but nested Pressables on RN Web can be finicky, so confirm both behaviors. (If it misbehaves, the clean fallback is to make the company tap-through only in the detail headers and keep list-row companies plain.)
  • The read-only "your roles here" card (badges, date ranges, empty/error states).

Closes #375

🤖 Generated with Claude Code

Completes **#375** — the client UI on top of the merged backend (#376). ## What's in it - **`@carol/api-client`**: `useOrganizationRoles(id)` over `GET /api/organizations/{id}/roles`; `keys.organizations.roles(id)`. (No generated-file changes — the endpoint/DTO were already generated; `check` stays clean.) - **Job/Contract form** (`experience.tsx`): the shared `JobForm` gains an **"Employer" toggle** — free text (default) vs **linked organisation**. Linked mode picks one of the user's orgs (`useOrganizations`); the company is derived server-side from the org. `organizationId` threads through create/update for **both jobs and contracts**; edit **preselects** the linked org; the linked option is **disabled with a hint** when there are no tracked orgs. - **Tap-through** (`CompanyName`): a linked employer renders as an accent link to the Org detail (showing the live `organizationName`); free-text employers stay plain `Text`. Wired into both list rows + both detail headers. - **Org detail** (`network/orgs/[id].tsx`): a read-only **"your roles here"** card listing the org's linked jobs/contracts (Job/Contract badge + date range) via `useOrganizationRoles`. - **i18n**: `experience.jobs.form.*` + `network.organizationDetail.roles.*` (British "organisation" to match the existing copy — 28 existing vs 5). ## Verification (local, re-run in the worktree) - `@carol/api-client`: typecheck ✓ · lint ✓ · test ✓ 44 · `check` ✓ (generated untouched) - `@carol/client`: typecheck ✓ · lint ✓ · test ✓ 172 - i18n valid; I verified the new keys resolve **relative to their namespace** (the literal-key-leak trap) — `experience.jobs.form.employerLabel` → "Employer", `network.organizationDetail.roles.title` → "Your roles here", etc. - semgrep ✓ 0 findings on the changed source ## ⚠️ Needs maintainer verification (headless limit) Client tests are `node`-env. Please eyeball in a running build: - The employer **free-text↔linked toggle** + org picker (disabled/empty-orgs hint; edit-mode preselect). - The **company tap-through** to `/network/orgs/{id}` from all four spots. **One thing to confirm specifically:** in the **list rows**, `CompanyName` is a `Pressable` nested inside the row's drill-down `Pressable`. RN's responder system should grant the inner link the tap (→ org) while taps elsewhere on the row open the job/contract — but nested Pressables on RN Web can be finicky, so confirm both behaviors. (If it misbehaves, the clean fallback is to make the company tap-through only in the detail headers and keep list-row companies plain.) - The read-only **"your roles here"** card (badges, date ranges, empty/error states). Closes #375 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(client): link a Job/Contract employer to an Organization in the UI (#375)
Some checks failed
Commits / Conventional Commits (pull_request) Successful in 7s
PR / OpenAPI (pull_request) Successful in 2m33s
PR / Static analysis (pull_request) Successful in 2m33s
PR / Client (web export smoke) (pull_request) Successful in 2m48s
PR / pnpm audit (pull_request) Successful in 3m5s
PR / Lint (pull_request) Successful in 4m0s
PR / Package age policy (soft) (pull_request) Successful in 46s
PR / OSV-Scanner (pull_request) Successful in 1m24s
PR / Typecheck (pull_request) Successful in 4m6s
PR / Build (pull_request) Successful in 4m12s
PR / Test (postgres) (pull_request) Successful in 4m26s
PR / Test (sqlite) (pull_request) Successful in 4m37s
Secrets / gitleaks (pull_request) Successful in 43s
PR / Trivy (image) (pull_request) Successful in 2m24s
PR / E2E (Playwright) (pull_request) Failing after 6m56s
PR / Coverage (soft) (pull_request) Successful in 2m29s
c2ed86e95e
Completes #375 on top of the merged backend (#376).

- @carol/api-client: useOrganizationRoles(id) over GET
  /api/organizations/{id}/roles; keys.organizations.roles(id).
- experience.tsx: the shared JobForm gains an "Employer" toggle —
  free text (default) vs linked organisation. Linked mode picks one of
  the user's orgs (useOrganizations); the company is derived server-side
  from the org. organizationId threads through create/update for BOTH
  jobs and contracts; edit preselects the linked org; the linked option
  is disabled with a hint when the user has no tracked orgs.
- CompanyName: a linked employer renders as an accent link to the Org
  detail (shows the live organizationName); free-text employers stay
  plain. Wired into both list rows + both detail headers.
- network/orgs/[id].tsx: a read-only "your roles here" card listing the
  org's linked jobs/contracts (badge + date range) via useOrganizationRoles.
- i18n: experience.jobs.form.* + network.organizationDetail.roles.*
  (British "organisation" to match existing copy).

Closes #375

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

📊 Test coverage

Patch coverage: no testable lines changed.

Overall (app/, lib/, db/, excluding UI per ADR-0019):

Metric Value Soft target
Lines 79.5% ≥ 50%
Branches 71.5% ⚠️ ≥ 75%
Functions 80.6% informational

Soft thresholds per ADR-0019. Coverage is informational and does not block merge.

<!-- coverage-comment --> ## 📊 Test coverage **Patch coverage:** no testable lines changed. **Overall** (`app/`, `lib/`, `db/`, excluding UI per ADR-0019): | Metric | Value | Soft target | |---|---|---| | Lines | 79.5% ✅ | ≥ 50% | | Branches | 71.5% ⚠️ | ≥ 75% | | Functions | 80.6% | informational | Soft thresholds per [ADR-0019](docs/adr/0019-coverage-soft-targets.md). Coverage is informational and does not block merge.
james merged commit 94908291a3 into main 2026-06-29 17:00:08 +00:00
james deleted branch 375-job-org-link-client 2026-06-29 17:00:09 +00:00
Sign in to join this conversation.
No description provided.