feat: add support for indicating workplaceTypes (#296)
This commit is contained in:
@@ -87,6 +87,19 @@ describe("settingsRegistry helpers", () => {
|
||||
);
|
||||
expect(settingsRegistry.searchTerms.serialize(null)).toBeNull();
|
||||
});
|
||||
|
||||
it("parses valid workplace type arrays", () => {
|
||||
expect(
|
||||
settingsRegistry.workplaceTypes.parse('["remote","onsite"]'),
|
||||
).toEqual(["remote", "onsite"]);
|
||||
});
|
||||
|
||||
it("rejects invalid workplace type arrays", () => {
|
||||
expect(
|
||||
settingsRegistry.workplaceTypes.parse('["remote","satellite"]'),
|
||||
).toBeNull();
|
||||
expect(settingsRegistry.workplaceTypes.parse("[]")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Resume projects settings", () => {
|
||||
|
||||
@@ -90,6 +90,35 @@ function createEnumParser<const TValues extends readonly [string, ...string[]]>(
|
||||
};
|
||||
}
|
||||
|
||||
function createEnumArrayParser<
|
||||
const TValues extends readonly [string, ...string[]],
|
||||
>(values: TValues): (raw: string | undefined) => TValues[number][] | null {
|
||||
const allowedValues = new Set<string>(values);
|
||||
|
||||
return (raw: string | undefined): TValues[number][] | null => {
|
||||
if (!raw) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(parsed)) return null;
|
||||
|
||||
const out: TValues[number][] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const value of parsed) {
|
||||
if (typeof value !== "string" || !allowedValues.has(value)) {
|
||||
return null;
|
||||
}
|
||||
if (seen.has(value)) continue;
|
||||
seen.add(value);
|
||||
out.push(value as TValues[number]);
|
||||
}
|
||||
if (out.length === 0) return null;
|
||||
return out;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const parseChatStyleLanguageModeOrNull = createEnumParser(
|
||||
CHAT_STYLE_LANGUAGE_MODE_VALUES,
|
||||
);
|
||||
@@ -98,6 +127,9 @@ const parseChatStyleManualLanguageOrNull = createEnumParser(
|
||||
CHAT_STYLE_MANUAL_LANGUAGE_VALUES,
|
||||
);
|
||||
|
||||
const WORKPLACE_TYPE_VALUES = ["remote", "hybrid", "onsite"] as const;
|
||||
const parseWorkplaceTypesOrNull = createEnumArrayParser(WORKPLACE_TYPE_VALUES);
|
||||
|
||||
export const resumeProjectsSchema = z.object({
|
||||
maxProjects: z.number().int().min(0).max(100),
|
||||
lockedProjectIds: z.array(z.string().trim().min(1)).max(200),
|
||||
@@ -271,6 +303,17 @@ export const settingsRegistry = {
|
||||
parse: parseJsonArrayOrNull,
|
||||
serialize: serializeNullableJsonArray,
|
||||
},
|
||||
workplaceTypes: {
|
||||
kind: "typed" as const,
|
||||
schema: z.array(z.enum(WORKPLACE_TYPE_VALUES)).min(1).max(3),
|
||||
default: (): Array<(typeof WORKPLACE_TYPE_VALUES)[number]> => [
|
||||
"remote",
|
||||
"hybrid",
|
||||
"onsite",
|
||||
],
|
||||
parse: parseWorkplaceTypesOrNull,
|
||||
serialize: serializeNullableJsonArray,
|
||||
},
|
||||
blockedCompanyKeywords: {
|
||||
kind: "typed" as const,
|
||||
schema: z.array(z.string().trim().min(1).max(200)).max(200),
|
||||
|
||||
@@ -159,6 +159,11 @@ export const createAppSettings = (
|
||||
default: ["Software Engineer"],
|
||||
override: null,
|
||||
},
|
||||
workplaceTypes: {
|
||||
value: ["remote", "hybrid", "onsite"],
|
||||
default: ["remote", "hybrid", "onsite"],
|
||||
override: null,
|
||||
},
|
||||
blockedCompanyKeywords: {
|
||||
value: [],
|
||||
default: [],
|
||||
|
||||
@@ -155,6 +155,7 @@ export interface AppSettings {
|
||||
gradcrackerMaxJobsPerTerm: Resolved<number>;
|
||||
startupjobsMaxJobsPerTerm: Resolved<number>;
|
||||
searchTerms: Resolved<string[]>;
|
||||
workplaceTypes: Resolved<Array<"remote" | "hybrid" | "onsite">>;
|
||||
blockedCompanyKeywords: Resolved<string[]>;
|
||||
scoringInstructions: Resolved<string>;
|
||||
searchCities: Resolved<string>;
|
||||
|
||||
Reference in New Issue
Block a user