Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions __tests__/lib/career-guides.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { loadCareerGuides } from "@/lib/career-guides";

describe("career-guides", () => {
describe("loadCareerGuides", () => {
const guides = loadCareerGuides();

it("yields two distinct 6333 entries, one per branch", () => {
const entries = guides.filter((g) => g.code === "6333");
expect(entries).toHaveLength(2);

const navy = entries.find((g) => g.branch === "Navy");
expect(navy?.title).toBe("Aviation Maintenance Officer");
expect(navy?.slug).toBe("6333");
expect(navy?.rank).toBe("Officer");

const marine = entries.find((g) => g.branch === "Marine Corps");
expect(marine?.title).toBe("Aviation Electronics Technician");
expect(marine?.slug).toBe("marine_corps:6333");
expect(marine?.rank).toBe("Enlisted");
});

it("resolves branch-prefixed pathway data before the bare code", () => {
const navy = guides.find((g) => g.code === "6333" && g.branch === "Navy");
const marine = guides.find((g) => g.code === "6333" && g.branch === "Marine Corps");

// Navy officer entry keeps the management-flavored bare "6333" pathways
expect(navy?.civilian).toBe("Aircraft Maintenance Manager");
// Marine technician entry resolves its own "marine_corps:6333" pathways
expect(marine?.civilian).toBe("Avionics Technician");
});

it("gives every guide a unique slug", () => {
const slugs = guides.map((g) => g.slug);
expect(new Set(slugs).size).toBe(slugs.length);
});
});
});
2 changes: 1 addition & 1 deletion src/containers/career-guides/grid-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const GuideCard = ({ g }: { g: GuideEntry }) => {

return (
<Link
href={`/career-guides/${g.code.toLowerCase()}`}
href={`/career-guides/${g.slug}`}
className={clsx(
"tw-group tw-relative tw-flex tw-flex-col tw-gap-5 tw-border-t tw-border-l tw-border-cream/10 tw-bg-secondary tw-p-6 tw-transition-colors tw-duration-150",
"hover:tw-border-accent hover:tw-bg-[#003559]"
Expand Down
2 changes: 2 additions & 0 deletions src/containers/career-guides/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export type Family =

export interface GuideEntry {
code: string;
/** Full training-pipeline key (lowercased); unique even when codes collide across branches */
slug: string;
title: string;
branch: Branch;
rank: Rank;
Expand Down
61 changes: 61 additions & 0 deletions src/data/career-pathways-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -34662,6 +34662,67 @@
"dataSource": "curated"
}
],
"marine_corps:6333": [
{
"role": "Avionics Technician",
"matchLevel": "High match",
"avgSalary": 75000,
"demand": "High demand",
"skillsToClose": [
"FAA repair station experience",
"Civilian avionics installation standards"
],
"dataSource": "curated"
},
{
"role": "Junior Software Engineer",
"matchLevel": "Good match",
"avgSalary": 85000,
"demand": "High demand",
"skillsToClose": [
"JavaScript/TypeScript or Python fundamentals",
"Version control with Git",
"Web application frameworks"
],
"dataSource": "curated"
},
{
"role": "Software Developer in Test",
"matchLevel": "Good match",
"avgSalary": 90000,
"demand": "Growing demand",
"skillsToClose": [
"Test automation frameworks (Selenium, Playwright)",
"Scripting in Python or JavaScript",
"CI/CD pipeline fundamentals"
],
"dataSource": "curated"
},
{
"role": "Systems Software Developer",
"matchLevel": "Moderate match",
"avgSalary": 95000,
"demand": "Growing demand",
"skillsToClose": [
"C/C++ or Rust programming",
"Embedded systems development",
"Real-time operating systems"
],
"dataSource": "curated"
},
{
"role": "DevOps Engineer",
"matchLevel": "Moderate match",
"avgSalary": 105000,
"demand": "Very high demand",
"skillsToClose": [
"Linux administration",
"Cloud platforms (AWS, Azure)",
"Infrastructure as code (Terraform)"
],
"dataSource": "curated"
}
],
"6335": [
{
"role": "Aircraft Maintenance Manager",
Expand Down
23 changes: 23 additions & 0 deletions src/data/training-pipeline.json
Original file line number Diff line number Diff line change
Expand Up @@ -12424,6 +12424,29 @@
"civilian_certs": [],
"ace_credits": "Up to 3 semester hours in management"
},
"marine_corps:6333": {
"branch": "Marine Corps",
"title": "Aviation Electronics Technician",
"program": "Aviation Electronics Technician Course, Center for Naval Aviation Technical Training (CNATT), NAS Pensacola, FL",
"hours": 960,
"weeks": 24,
"topics": [
"Basic Electricity and Electronics Theory",
"Aircraft Electrical Systems",
"Avionics Troubleshooting and Fault Isolation",
"Wiring Schematics and MIL-SPEC Standards",
"Electronic Test Equipment Operation (Multimeters, Oscilloscopes, Avionics Test Sets)",
"Aircraft Communication and Navigation Systems Maintenance",
"Technical Documentation and Procedures",
"Safety Procedures for Avionics Maintenance"
],
"civilian_certs": [
"FCC General Radiotelephone Operator License (partial)",
"CompTIA A+ (partial)",
"ETA-I Certified Electronics Technician (CET) Associate (partial)"
],
"ace_credits": "Up to 15 semester hours recommended in avionics technology or electronics engineering technology."
},
"6335": {
"branch": "Navy",
"title": "Aviation Maintenance Management Officer",
Expand Down
5 changes: 3 additions & 2 deletions src/lib/career-guides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ export const loadCareerGuides = (): GuideEntry[] => {
const family = detectFamily(entry.title);
const certs = entry.civilian_certs ?? [];

// Pathway lookup: try raw code first, then full key
const matches = pathways[code] ?? pathways[stripBranchPrefix(key)] ?? [];
// Pathway lookup: exact full key first (branch-prefixed entries), then bare code
const matches = pathways[key] ?? pathways[code] ?? pathways[stripBranchPrefix(key)] ?? [];
const top = matches[0];
const civilian = top?.role ?? entry.title;
const salaries = matches.map((m) => m.avgSalary).filter((s) => typeof s === "number");
Expand All @@ -142,6 +142,7 @@ export const loadCareerGuides = (): GuideEntry[] => {

guides.push({
code,
slug: key.toLowerCase(),
title: entry.title,
branch,
rank,
Expand Down
7 changes: 6 additions & 1 deletion src/pages/career-guides/[mos].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
const training = trainingMap[mosCode];
if (!training) return { notFound: true };

// Branch-prefixed keys (e.g. "marine_corps:6333") disambiguate code collisions
// across branches; display only the bare code.
const colonIdx = mosCode.indexOf(":");
const displayCode = colonIdx === -1 ? mosCode : mosCode.slice(colonIdx + 1);

const techBundle = techPathwaysMap[mosCode];
const techPathway: TechPathway | null = techBundle
? {
Expand All @@ -171,7 +176,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
: null;

const detail = buildDetail({
code: mosCode,
code: displayCode,
training,
certs: certsMap[mosCode] || {
direct_qualifies: [],
Expand Down
Loading