diff --git a/core/onboarding-review.test.ts b/core/onboarding-review.test.ts index 952e3ce..8d63acd 100644 --- a/core/onboarding-review.test.ts +++ b/core/onboarding-review.test.ts @@ -66,6 +66,7 @@ function resume(): ExtractedResume { startDate: "2021-05-01", endDate: "2021-08-01", description: "", + site: null, }, ]; return data; @@ -164,6 +165,9 @@ describe("keptOnboardingHistory", () => { org: "State", title: "AS", field: "Math", + place: "Philadelphia, PA", + lat: 39.95, + lng: -75.16, }; expect(isBlankOnboardingCheckItem(blankOnboardingCheckItem("Job", -1))).toBe(true); expect(isBlankOnboardingCheckItem(job)).toBe(false); @@ -186,6 +190,13 @@ describe("keptOnboardingHistory", () => { institution: "State", degree: "AS", field: "Math", + location: "Philadelphia, PA", + }); + expect(kept.education[0]?.site).toEqual({ + label: "Philadelphia, PA", + address: "Philadelphia, PA", + latitude: 39.95, + longitude: -75.16, }); expect(kept.skills).toEqual(["PLC"]); }); diff --git a/core/onboarding-review.ts b/core/onboarding-review.ts index 5e395b9..240bdb0 100644 --- a/core/onboarding-review.ts +++ b/core/onboarding-review.ts @@ -67,14 +67,14 @@ export function onboardingCheckItems(data: ExtractedResume): OnboardingCheckItem org: school.institution || "Untitled school", title: school.degree, field: school.field, - place: school.location || ONBOARDING_NO_PLACE, + place: school.site?.label || school.location || ONBOARDING_NO_PLACE, startDate: school.startDate, endDate: school.endDate, isCurrent: false, description: school.description, achievements: [], - lat: null, - lng: null, + lat: school.site?.latitude ?? null, + lng: school.site?.longitude ?? null, }), ); return [...jobs, ...schools]; @@ -223,6 +223,10 @@ function keptSchool( const base = item.sourceIndex >= 0 ? extracted.education[item.sourceIndex] : undefined; if (item.sourceIndex >= 0 && !base) return null; const place = item.place === ONBOARDING_NO_PLACE ? (base?.location ?? "") : item.place; + const site = + item.lat != null && item.lng != null + ? { label: place, address: place, latitude: item.lat, longitude: item.lng } + : (base?.site ?? null); return { institution: "", degree: "", @@ -231,6 +235,7 @@ function keptSchool( startDate: "", endDate: "", description: "", + site: null, ...base, institution: item.org.trim() || "Untitled school", degree: item.title.trim(), @@ -239,6 +244,7 @@ function keptSchool( startDate: item.startDate, endDate: item.endDate, description: item.description.trim(), + site, }; } diff --git a/core/resume-extract.ts b/core/resume-extract.ts index d856d6c..feb9d09 100644 --- a/core/resume-extract.ts +++ b/core/resume-extract.ts @@ -83,6 +83,7 @@ export type ExtractedSchool = { startDate: string; endDate: string; description: string; + site: ExtractedSite | null; }; export type ExtractedResume = { @@ -378,5 +379,6 @@ function asSchool(value: unknown): ExtractedSchool | null { startDate: asText(row.startDate), endDate: asText(row.endDate), description: asText(row.description), + site: parseSite(row.site), }; } diff --git a/server/history.test.ts b/server/history.test.ts index dc3ec31..9b467d3 100644 --- a/server/history.test.ts +++ b/server/history.test.ts @@ -101,6 +101,47 @@ describe("GET /api/history", () => { } }); + it("pins a validated school site onto the Work Map", async () => { + const ctx = setup(); + try { + await ctx.app.request("/api/history", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + experience: [], + education: [ + { + institution: "City College", + degree: "AAS", + location: "Philadelphia, PA", + site: { + label: "City College", + address: "Philadelphia, Pennsylvania, United States", + latitude: 39.9526, + longitude: -75.1652, + }, + }, + ], + skills: [], + }), + }); + const map = await ctx.app.request("/api/work-map"); + const body = (await map.json()) as { + roles: Array<{ + kind: string; + locations: Array<{ label: string; latitude: number }>; + }>; + }; + expect(body.roles[0]).toMatchObject({ kind: "school" }); + expect(body.roles[0]?.locations[0]).toMatchObject({ + label: "City College", + latitude: 39.9526, + }); + } finally { + ctx.close(); + } + }); + it("pins a validated job site onto the Work Map", async () => { const ctx = setup(); try { diff --git a/server/history.ts b/server/history.ts index e2a9167..524ad63 100644 --- a/server/history.ts +++ b/server/history.ts @@ -89,6 +89,9 @@ export async function saveExtractedResume( school.field, now, ); + if (school.site) { + persistExtractedSite(db, occupantId, schoolId, school.site); + } roles.push({ id: schoolId, kind: "school", diff --git a/web/src/Onboarding.tsx b/web/src/Onboarding.tsx index 2f2b119..232b67a 100644 --- a/web/src/Onboarding.tsx +++ b/web/src/Onboarding.tsx @@ -89,6 +89,7 @@ export function Onboarding({ const abortRef = useRef(null); const scanAbort = useRef(null); const fileInputRef = useRef(null); + const previewDialogRef = useRef(null); useEffect(() => { if (!photo) return; @@ -97,6 +98,16 @@ export function Onboarding({ return () => URL.revokeObjectURL(url); }, [photo]); + useEffect(() => { + const node = previewDialogRef.current; + if (!node) return; + if (previewOpen) { + if (!node.open) node.showModal(); + return; + } + if (node.open) node.close(); + }, [previewOpen, file]); + useEffect(() => { let cancel = false; void (async () => { @@ -644,9 +655,9 @@ export function Onboarding({ @@ -664,7 +675,31 @@ export function Onboarding({ /> )} - {file && previewOpen ? : null} + {file ? ( + setPreviewOpen(false)} + > +
+

Resume

+ +
+

+ {file.name} +

+
+ {previewOpen ? : null} +
+
+ ) : null} {file ? ( {summary ?

{summary}

: null}
    - {items.map((item) => ( + {items.map((item, index) => (
  • -
    +
    +
  • ))}
@@ -102,7 +111,15 @@ export function CheckOverview({ - @@ -217,6 +234,7 @@ function ReviewSlide({ setStreetOpen(false); }, [index, item.org]); const placed = item.lat != null && item.lng != null; + const prompt = placePrompt(item.kind); const last = index + 1 >= total; return ( <> @@ -286,44 +304,33 @@ function ReviewSlide({ Current role ) : null} - {item.kind === "Job" ? ( - <> -
-
- {streetOpen || placed ? ( - { - onChange({ ...item, place, lat, lng }); - setStreetOpen(false); - }} - /> - ) : null} - - ) : ( - - )} +
+
+ {streetOpen ? ( + { + onChange({ ...item, place, lat, lng }); + setStreetOpen(false); + }} + /> + ) : null}