diff --git a/.bg-shell/manifest.json b/.bg-shell/manifest.json index 0637a08..6ea178e 100644 --- a/.bg-shell/manifest.json +++ b/.bg-shell/manifest.json @@ -1 +1,16 @@ -[] \ No newline at end of file +[ + { + "id": "dec06736", + "label": "job-tracker-ui-build-serve", + "command": "npx serve -s job-tracker-ui/build -l 4173", + "cwd": "/home/pi/.gsd/projects/a40e97ae9e8f/worktrees/M001", + "ownerSessionFile": "/home/pi/.gsd/sessions/--home-pi-development-JobTracker--/2026-03-24T12-44-27-361Z_3ca4ee97-fc80-4cec-b8e7-f5b393c64480.jsonl", + "persistAcrossSessions": false, + "startedAt": 1774356678346, + "processType": "server", + "group": null, + "readyPattern": "Accepting connections|Local:|http://localhost:4173", + "readyPort": 4173, + "pid": 1141701 + } +] \ No newline at end of file diff --git a/.gsd/KNOWLEDGE.md b/.gsd/KNOWLEDGE.md index f4e122e..ee82ec2 100644 --- a/.gsd/KNOWLEDGE.md +++ b/.gsd/KNOWLEDGE.md @@ -5,3 +5,4 @@ - The S02 package workspace persists the application-answer draft inside `JobApplication.Notes` using the marker block `<<>> ... <<>>`; downstream slices should replace or parse that block instead of appending free-form notes. - `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` is now a trustworthy direct verification command in this worktree for package-generation and notes-replacement behavior; prefer it over older isolated-harness guidance when checking S02 regressions. - `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` is now trustworthy again in this worktree after restoring the missing ASP.NET Core / Identity / xUnit test-project references in `JobTrackerApi.Tests/JobTrackerApi.Tests.csproj`; older task notes that require an isolated Docker harness are stale. +- Running `npm --prefix job-tracker-ui start` alone is not enough for browser UAT in this worktree: the frontend calls `http://localhost:5202/api/...`, so without the backend (or a matching CORS/proxy setup) the UI loads but shows empty-state surfaces with `net::ERR_FAILED`/CORS errors instead of real job data. diff --git a/.gsd/journal/2026-03-24.jsonl b/.gsd/journal/2026-03-24.jsonl index 11c224d..f8a0a3b 100644 --- a/.gsd/journal/2026-03-24.jsonl +++ b/.gsd/journal/2026-03-24.jsonl @@ -43,3 +43,20 @@ {"ts":"2026-03-24T11:32:22.159Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":2,"eventType":"dispatch-match","rule":"summarizing → complete-slice","data":{"unitType":"complete-slice","unitId":"M001/S03"}} {"ts":"2026-03-24T11:32:22.165Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":3,"eventType":"unit-start","data":{"unitType":"complete-slice","unitId":"M001/S03"}} {"ts":"2026-03-24T11:40:09.423Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":4,"eventType":"unit-end","data":{"unitType":"complete-slice","unitId":"M001/S03","status":"completed","artifactVerified":true},"causedBy":{"flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":3}} +{"ts":"2026-03-24T11:40:09.882Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":5,"eventType":"iteration-end","data":{"iteration":6}} +{"ts":"2026-03-24T11:40:09.883Z","flowId":"eee2e6cb-cc05-4b58-b4f2-6471f9fbadd3","seq":1,"eventType":"iteration-start","data":{"iteration":7}} +{"ts":"2026-03-24T11:40:09.976Z","flowId":"eee2e6cb-cc05-4b58-b4f2-6471f9fbadd3","seq":2,"eventType":"dispatch-match","rule":"executing → execute-task","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T11:40:09.984Z","flowId":"eee2e6cb-cc05-4b58-b4f2-6471f9fbadd3","seq":3,"eventType":"unit-start","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T11:55:32.899Z","flowId":"f415178f-7473-42c6-855f-3197cfdbc267","seq":1,"eventType":"iteration-start","data":{"iteration":1}} +{"ts":"2026-03-24T11:55:33.001Z","flowId":"f415178f-7473-42c6-855f-3197cfdbc267","seq":2,"eventType":"dispatch-match","rule":"summarizing → complete-slice","data":{"unitType":"complete-slice","unitId":"M001/S04"}} +{"ts":"2026-03-24T11:55:33.009Z","flowId":"f415178f-7473-42c6-855f-3197cfdbc267","seq":3,"eventType":"unit-start","data":{"unitType":"complete-slice","unitId":"M001/S04"}} +{"ts":"2026-03-24T12:11:33.612Z","flowId":"f7f3f549-4db8-4bd4-8d39-84fe0fbac7c6","seq":1,"eventType":"iteration-start","data":{"iteration":1}} +{"ts":"2026-03-24T12:11:33.689Z","flowId":"f7f3f549-4db8-4bd4-8d39-84fe0fbac7c6","seq":2,"eventType":"dispatch-match","rule":"executing → execute-task","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:11:33.696Z","flowId":"f7f3f549-4db8-4bd4-8d39-84fe0fbac7c6","seq":3,"eventType":"unit-start","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:26:34.819Z","flowId":"a3d26ff3-c20d-49a3-8be6-e8c956e6ab6f","seq":1,"eventType":"iteration-start","data":{"iteration":1}} +{"ts":"2026-03-24T12:26:34.919Z","flowId":"a3d26ff3-c20d-49a3-8be6-e8c956e6ab6f","seq":2,"eventType":"dispatch-match","rule":"executing → execute-task","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:26:34.926Z","flowId":"a3d26ff3-c20d-49a3-8be6-e8c956e6ab6f","seq":3,"eventType":"unit-start","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:44:27.227Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":1,"eventType":"iteration-start","data":{"iteration":1}} +{"ts":"2026-03-24T12:44:27.343Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":2,"eventType":"dispatch-match","rule":"executing → execute-task","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:44:27.348Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":3,"eventType":"unit-start","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} +{"ts":"2026-03-24T12:56:42.382Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":4,"eventType":"unit-end","data":{"unitType":"execute-task","unitId":"M001/S04/T02","status":"completed","artifactVerified":true},"causedBy":{"flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":3}} diff --git a/.gsd/milestones/M001/slices/S04/S04-PLAN.md b/.gsd/milestones/M001/slices/S04/S04-PLAN.md index a96c47c..63b6368 100644 --- a/.gsd/milestones/M001/slices/S04/S04-PLAN.md +++ b/.gsd/milestones/M001/slices/S04/S04-PLAN.md @@ -22,6 +22,7 @@ S04 directly owns active requirements **R005**, **R006**, **R007**, **R009**, an - `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` - `CI=true npm --prefix job-tracker-ui run build` - Manual UAT: from the table, reminders page, and dashboard, identify a job needing attention, jump into the correct job workspace tab, and confirm the flow feels like one coherent daily review loop. +- Failure-path check: deliberately trigger each surface's action affordance in the focused UI test and confirm the routed workspace lands on the expected job/tab state instead of leaving the user on a passive summary surface. ## Observability / Diagnostics @@ -44,7 +45,7 @@ S04 directly owns active requirements **R005**, **R006**, **R007**, **R009**, an - Do: add actionable attention cards/lists to the dashboard, route reminders actions into the existing job workspace state instead of a separate modal loop, and preserve the individual-first control flow through `/jobs`. - Verify: `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` - Done when: the dashboard and reminders surfaces clearly show what needs attention and can open the correct job workspace state directly. -- [ ] **T02: Make the job table expose the right next action and prove the daily loop** `est:4h` +- [x] **T02: Make the job table expose the right next action and prove the daily loop** `est:4h` - Why: The table is the first surface the user sees each day, so it has to make urgency legible and connect cleanly into the same routed job workspace flow. - Files: `job-tracker-ui/src/components/JobTable.tsx`, `job-tracker-ui/src/daily-control-loop.test.tsx` - Do: tighten job-table urgency/action affordances for follow-up and package work, reuse the routed workspace-open mechanism, and add focused UI coverage proving the table/reminders/dashboard loop lands in the right job workspace state. diff --git a/.gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md b/.gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md index db259b5..a6154fb 100644 --- a/.gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md +++ b/.gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md @@ -42,3 +42,9 @@ Strengthen the job table so the first daily view shows what action is actually d - `job-tracker-ui/src/components/JobTable.tsx` — clearer next-action affordances. - `job-tracker-ui/src/daily-control-loop.test.tsx` — proof that the table participates in the routed daily loop. + +## Observability Impact + +- Signals changed: the table's urgency chips and primary row actions should now expose the same routed follow-up and package-work intents already used by dashboard and reminders. +- How to inspect later: read `job-tracker-ui/src/components/JobTable.tsx` for the shared workspace-route usage and run `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` to confirm table, reminders, and dashboard all land in the expected workspace state. +- Failure state made visible: if table actions drift from the shared routing contract, the focused UI test should fail with the wrong route/tab content instead of silently leaving decorative chips in place. diff --git a/.gsd/milestones/M001/slices/S04/tasks/T02-SUMMARY.md b/.gsd/milestones/M001/slices/S04/tasks/T02-SUMMARY.md index e4faa7e..9bda7e7 100644 --- a/.gsd/milestones/M001/slices/S04/tasks/T02-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S04/tasks/T02-SUMMARY.md @@ -1,23 +1,89 @@ --- -title: T02 summary -status: done -files: +id: T02 +parent: S04 +milestone: M001 +provides: + - Makes job-table urgency signals actionable and keeps table, reminders, and dashboard on the same routed job-workspace loop. +key_files: - job-tracker-ui/src/components/JobTable.tsx - job-tracker-ui/src/daily-control-loop.test.tsx -verification: - - CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx - - CI=true npm --prefix job-tracker-ui run build + - job-tracker-ui/src/i18n/translations.ts + - .gsd/milestones/M001/slices/S04/S04-PLAN.md + - .gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md +key_decisions: + - Derived table chips and primary row actions from one shared action-signal model so table urgency and routed next-action behavior cannot drift. +patterns_established: + - Use the shared `/jobs?open=...&tab=...&followMode=...` workspace route from overview surfaces and prove each surface with focused UI/browser checks. +observability_surfaces: + - job-tracker-ui/src/components/JobTable.tsx + - job-tracker-ui/src/daily-control-loop.test.tsx + - Browser UAT on the built app served locally with mocked API routes at http://localhost:4173 + - .gsd/milestones/M001/slices/S04/S04-PLAN.md verification/failure-path notes +duration: ~2h +verification_result: passed +completed_at: 2026-03-24T13:55:43+01:00 +blocker_discovered: false --- -Made the job table expose the next action directly and prove the daily loop end-to-end. +# T02: Make the job table expose the right next action and prove the daily loop -What changed: -- `job-tracker-ui/src/components/JobTable.tsx` - - follow-up and CV status chips now route directly into the right job workspace tab - - the table participates in the same routed workspace flow as dashboard and reminders -- `job-tracker-ui/src/daily-control-loop.test.tsx` - - expanded the focused test to prove the table chip routes into the follow-up workspace and that reminders can route into the tailored-CV workspace +**Made the job table show actionable follow-up/package next steps and proved table, reminders, and dashboard all land in the same job workspace flow.** -Verification: -- Focused daily-loop test passed -- Frontend build passed +## What Happened + +I first fixed the flagged observability gaps in the slice and task plans so this unit explicitly described its failure-path coverage and inspection surfaces. + +In `job-tracker-ui/src/components/JobTable.tsx`, I replaced the split decorative/action logic with a shared action-signal model. The table now derives both row chips and the primary “Next action” control from the same follow-up/package readiness signals, so the visible urgency affordance and the routed action cannot drift apart. Package work now reflects the same readiness inputs already used by the table filters: missing tailored CV, missing notes, or both. + +I updated translations in `job-tracker-ui/src/i18n/translations.ts` so the new package-work affordances and details are user-facing copy instead of hardcoded strings. + +In `job-tracker-ui/src/daily-control-loop.test.tsx`, I expanded the focused daily-loop proof so the table explicitly participates in the routed loop: the test now clicks a table urgency signal for follow-up work and the table’s next-action button for package work, while the earlier reminders/dashboard proofs remain in place. + +For runtime verification, I also exercised the built app in a browser. Because `dotnet` is unavailable in this environment, I served the compiled UI locally and mocked the frontend’s own API contract in-browser, then verified that table, reminders, and dashboard each opened the expected job workspace state. + +## Verification + +Verified the focused UI test passes, the frontend production build succeeds, and browser UAT confirms the routed daily loop across all three surfaces. + +- Focused test: `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` +- Build: `CI=true npm --prefix job-tracker-ui run build` +- Browser UAT on built app (`npx serve -s job-tracker-ui/build -l 4173`) with mocked API contract: + - `/jobs`: clicked table next action for Backend Developer and confirmed Follow-up context + saved cover-letter reuse signal + - `/reminders`: clicked Missing tailored CV → Open and confirmed Tailored CV workspace for Platform Engineer + - `/dashboard`: clicked Follow up card action and confirmed routed follow-up workspace + - Network errors were cleared after route mocking; the verified browser interactions completed without failed mocked requests + +## Verification Evidence + +| # | Command | Exit Code | Verdict | Duration | +|---|---------|-----------|---------|----------| +| 1 | `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` | 0 | ✅ pass | 4.723s | +| 2 | `CI=true npm --prefix job-tracker-ui run build` | 0 | ✅ pass | 12.969s | +| 3 | Browser UAT on `http://localhost:4173` with mocked `http://localhost:5202/api/*` routes: table → follow-up workspace, reminders → tailored CV workspace, dashboard → follow-up workspace | 0 | ✅ pass | ~4m | + +## Diagnostics + +Inspect `job-tracker-ui/src/components/JobTable.tsx` to see the shared action-signal derivation that drives both urgency chips and primary next-action buttons. + +Run `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx` to catch drift between table, reminders, dashboard, and the routed workspace state. + +For browser-level inspection, serve the built UI and verify these surfaces route into `/jobs` with the expected workspace content: +- table signal / next-action controls +- reminders Open actions +- dashboard Needs Follow-up actions + +## Deviations + +Used browser-level API route mocks for manual UAT because the local ASP.NET API could not be started in this environment (`dotnet` is not installed). The shipped UI code was still exercised in a real browser against its own HTTP contract. + +## Known Issues + +The focused Jest run still emits existing React Router v7 future-flag warnings from the test harness. They do not fail the task’s verification gate and were not introduced by this change. + +## Files Created/Modified + +- `job-tracker-ui/src/components/JobTable.tsx` — unified actionable urgency chips and primary next-action routing for follow-up and package work +- `job-tracker-ui/src/daily-control-loop.test.tsx` — extended the focused loop test to prove the table participates in the same routed workspace flow +- `job-tracker-ui/src/i18n/translations.ts` — added user-facing copy for package-work affordances and details +- `.gsd/milestones/M001/slices/S04/S04-PLAN.md` — added explicit slice-level failure-path verification guidance +- `.gsd/milestones/M001/slices/S04/tasks/T02-PLAN.md` — added the missing Observability Impact section diff --git a/job-tracker-ui/src/components/JobTable.tsx b/job-tracker-ui/src/components/JobTable.tsx index 6f9cf27..96c97a5 100644 --- a/job-tracker-ui/src/components/JobTable.tsx +++ b/job-tracker-ui/src/components/JobTable.tsx @@ -289,6 +289,59 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col return src.length > 220 ? `${src.slice(0, 220)}...` : src; }; + const openFollowUpWorkspace = (jobId: number) => { + navigate(buildJobWorkspacePath(jobId, { tab: JOB_DETAILS_TABS.followUp, followMode: "waiting-update" })); + }; + + const openTailoredCvWorkspace = (jobId: number) => { + navigate(buildJobWorkspacePath(jobId, { tab: JOB_DETAILS_TABS.tailoredCv })); + }; + + const getPackageActionDetail = (job: JobApplication) => { + const missingTailoredCv = !job.tailoredCvText; + const missingNotes = !job.notes?.trim(); + + if (missingTailoredCv && missingNotes) return t("jobTablePackageMissingCvAndNotes"); + if (missingTailoredCv) return t("jobTableCvMissing"); + if (missingNotes) return t("jobTablePackageMissingNotes"); + return null; + }; + + const getActionSignals = (job: JobApplication) => { + const signals: Array<{ + label: string; + detail: string; + onClick: () => void; + variant: "contained" | "outlined"; + color?: "warning" | "primary"; + }> = []; + + if (job.needsFollowUp) { + signals.push({ + label: t("jobTableFollowUp"), + detail: job.followUpReason ?? t("jobTableNeedsFollowUp"), + onClick: () => openFollowUpWorkspace(job.id), + variant: "contained", + color: "warning", + }); + } + + const packageDetail = !job.isDeleted ? getPackageActionDetail(job) : null; + if (packageDetail) { + signals.push({ + label: t("jobTablePackageWork"), + detail: packageDetail, + onClick: () => openTailoredCvWorkspace(job.id), + variant: job.needsFollowUp ? "outlined" : "contained", + color: job.needsFollowUp ? "primary" : "warning", + }); + } + + return signals; + }; + + const getPrimaryAction = (job: JobApplication) => getActionSignals(job)[0] ?? null; + return ( @@ -368,6 +421,8 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col const open = expanded.includes(job.id); const toneName = statusTone(job.status); const tone = toneName === "error" ? theme.palette.error.main : toneName === "warning" ? theme.palette.warning.main : toneName === "success" ? theme.palette.success.main : toneName === "info" ? theme.palette.info.main : theme.palette.primary.main; + const primaryAction = getPrimaryAction(job); + const actionSignals = getActionSignals(job); return ( @@ -377,9 +432,20 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col {job.jobTitle} - {job.needsFollowUp ? navigate(buildJobWorkspacePath(job.id, { tab: JOB_DETAILS_TABS.followUp, followMode: "waiting-update" }))} /> : null} - {!job.tailoredCvText && !job.isDeleted ? navigate(buildJobWorkspacePath(job.id, { tab: JOB_DETAILS_TABS.tailoredCv }))} /> : null} - {job.tailoredCvText ? navigate(buildJobWorkspacePath(job.id, { tab: JOB_DETAILS_TABS.tailoredCv }))} /> : null} + {actionSignals.map((signal) => ( + + ))} {columns.status ? : null} @@ -387,11 +453,26 @@ export default function JobTable({ refreshToken, pageSize, onPageSizeChange, col {columns.daysSince ? {job.daysSince} : null} {columns.jobUrl ? {job.jobUrl ? {t("jobTableLink")} : ""} : null} - - setEditJobId(job.id)}> - { setStatusJobId(job.id); setStatusAnchor(e.currentTarget); }}> - setDetailsJobId(job.id)}> - {(mode === "trash" || (includeDeleted && job.isDeleted)) ? void restore(job.id)}> : void softDelete(job)}>} + + {primaryAction ? ( + <> + + {t("editJobNextAction")} + + + + {primaryAction.detail} + + + ) : null} + + setEditJobId(job.id)}> + { setStatusJobId(job.id); setStatusAnchor(e.currentTarget); }}> + setDetailsJobId(job.id)}> + {(mode === "trash" || (includeDeleted && job.isDeleted)) ? void restore(job.id)}> : void softDelete(job)}>} + diff --git a/job-tracker-ui/src/daily-control-loop.test.tsx b/job-tracker-ui/src/daily-control-loop.test.tsx index d1943bd..2e50b5b 100644 --- a/job-tracker-ui/src/daily-control-loop.test.tsx +++ b/job-tracker-ui/src/daily-control-loop.test.tsx @@ -160,13 +160,25 @@ test('reminders open action routes tailored-cv gaps into the tailored cv workspa expect(await screen.findByText(/build the package here, then save the working copy back onto this job/i)).toBeInTheDocument(); }); -test('job table urgency chips route into the correct workspace tab', async () => { - renderLoop('/jobs'); +test('job table urgency signals and next actions route into the shared workspace flow', async () => { + const firstRender = renderLoop('/jobs'); - const followUpChip = await screen.findByText(/follow up/i); - fireEvent.click(followUpChip); + fireEvent.click(await screen.findByRole('button', { name: /backend developer — follow up signal/i })); await waitFor(() => { - expect(screen.getByText(/follow-up context/i)).toBeInTheDocument(); + expect(screen.getByTestId('location-indicator')).toHaveTextContent('/jobs'); }); + expect(await screen.findByText(/follow-up context/i)).toBeInTheDocument(); + expect(await screen.findByText(/saved cover letter available/i)).toBeInTheDocument(); + + firstRender.unmount(); + renderLoop('/jobs'); + + fireEvent.click(await screen.findByRole('button', { name: /next action: platform engineer — build package/i })); + + await waitFor(() => { + expect(screen.getByTestId('location-indicator')).toHaveTextContent('/jobs'); + }); + expect(await screen.findByText(/build the package here, then save the working copy back onto this job/i)).toBeInTheDocument(); + expect(await screen.findByText(/platform work/i)).toBeInTheDocument(); }); diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index 133ee74..8e62f5d 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -616,6 +616,9 @@ export const translations = { jobTableFollowUp: "Follow up", jobTableCvMissing: "CV missing", jobTableCvReady: "CV ready", + jobTablePackageWork: "Build package", + jobTablePackageMissingCvAndNotes: "Tailored CV and job notes still need a working draft.", + jobTablePackageMissingNotes: "Job notes still need a working draft.", jobTableOpenListing: "Open listing", jobTableSkills: "Skills", jobTableNoTags: "No tags", @@ -1424,6 +1427,9 @@ export const translations = { jobTableFollowUp: "Følg opp", jobTableCvMissing: "CV mangler", jobTableCvReady: "CV klar", + jobTablePackageWork: "Bygg pakke", + jobTablePackageMissingCvAndNotes: "Tilpasset CV og jobbnotater trenger fortsatt et arbeidsutkast.", + jobTablePackageMissingNotes: "Jobbnotater trenger fortsatt et arbeidsutkast.", jobTableOpenListing: "Åpne stilling", jobTableSkills: "Ferdigheter", jobTableNoTags: "Ingen tagger",