mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-24 00:43:29 +10:00
3e16586d7a
* feat: add job listings feature with JSearch API integration, resume tailoring, and per-user rate limiting * feat(jobs): add search filters UI, filter helper functions with tests, and job_search_quota DB migration * feat(jobs): add pagination with 30 results per page and prev/next navigation * refactor(job-detail): Adjust sheet width and scroll area height * feat(ai): Add resume tailoring feature and prompt * refactor(ai): Revise tailoring prompts and schema for full skill rewrite * feat(ai): Add reference tailoring and output sanitization * feat(testing): Add Vitest testing framework * fix: address PR review - atomic rate limiting, calendar-month quota, skill sync warning, gitignore routeTree.gen.ts * feat(jobs): Add location filter to job listings * feat(job-listings): Add DOCX document generation * feat(job-listings): Enable search by location and on Enter key * feat(job-listings): Split location filter into city, state, and country * feat(jobs): Implement job search adapter and JSearch * Update 'locale/' directory * feat(resume): Simplify filename generation and add tests * fix(JSearch): reduce JSearch API usage to 1 request per search to prevent quota exhaustion * fix(JSearch): Displayed quota amounts on Job Search functionality and settings fixed to pull from RapidAPI/JSearch response * fix(internal rate limit): Removed internal rate limit and .env.example addition, cloud based implementation handles. * style(job-filters): Adjust layout of switch filters * fix(typecheck): Fixed typecheck issues introduced to sync with origin * feat(jobs): Enhance tailor dialog with apply link and tags * feat(locale files): updated locale files with the latest build * feat(jobs): Add job search provider and integrate testing functionality - Introduced `createJobSearchProvider` function to instantiate a JSearchProvider. - Enhanced job search provider with methods for searching jobs, retrieving job details, and testing connection. - Updated `vite.config.ts` to include new testing configurations and plugins. - Added new dependencies in `package.json` for testing and document generation. - Removed obsolete `vitest.config.ts` file. - Improved job search provider tests for better coverage and reliability. * refactor: Update job search routes and remove obsolete test configurations - Removed the test configuration from `vite.config.ts`. - Updated localization files to reflect changes in job search routes, renaming references from `jobs` to `job-search` across multiple languages. - Adjusted autofix workflow to run formatting without the `--fix` flag for better control over code style adjustments. * chore: Update dependencies and improve animation performance - Added `jsdom` as a new dependency in `package.json`. - Updated `vite-plus` and `vitest` to the latest versions for better compatibility. - Enhanced animation components with `willChange` styles to optimize rendering performance. - Adjusted various UI components to improve responsiveness and visual effects. - Removed obsolete job details functionality from the job search provider and related tests. * chore(locales): Update localization files for job search improvements - Modified job search related strings to remove references to "this month" for a more concise format. - Updated file references in localization entries to reflect changes in the job search component structure. - Added new strings for API usage, quota remaining, and job fetching error messages across multiple languages. - Removed obsolete "Monthly Usage" string from localization files. * chore(dependencies): Update @typescript/native-preview to version 7.0.0-dev.20260319.1 --------- Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
155 lines
5.6 KiB
TypeScript
155 lines
5.6 KiB
TypeScript
import z from "zod";
|
|
|
|
// --- JSearch API Request Types ---
|
|
|
|
export const searchParamsSchema = z.object({
|
|
query: z.string().min(1),
|
|
page: z.number().int().positive().optional(),
|
|
num_pages: z.number().int().positive().max(10).optional(),
|
|
date_posted: z.enum(["all", "today", "3days", "week", "month"]).optional(),
|
|
country: z
|
|
.string()
|
|
.length(2)
|
|
.regex(/^[A-Z]{2}$/)
|
|
.optional(),
|
|
remote_jobs_only: z.boolean().optional(),
|
|
employment_types: z.string().optional(),
|
|
job_requirements: z.string().optional(),
|
|
radius: z.number().positive().optional(),
|
|
exclude_job_publishers: z.string().optional(),
|
|
categories: z.string().optional(),
|
|
});
|
|
|
|
export type SearchParams = z.infer<typeof searchParamsSchema>;
|
|
|
|
// --- JSearch API Response Types ---
|
|
|
|
export const jobRequiredExperienceSchema = z.object({
|
|
no_experience_required: z.boolean().catch(false),
|
|
required_experience_in_months: z.number().nullable().catch(null),
|
|
experience_mentioned: z.boolean().catch(false),
|
|
experience_preferred: z.boolean().catch(false),
|
|
});
|
|
|
|
export const jobRequiredEducationSchema = z.object({
|
|
postgraduate_degree: z.boolean().catch(false),
|
|
professional_certification: z.boolean().catch(false),
|
|
high_school: z.boolean().catch(false),
|
|
associates_degree: z.boolean().catch(false),
|
|
bachelors_degree: z.boolean().catch(false),
|
|
degree_mentioned: z.boolean().catch(false),
|
|
degree_preferred: z.boolean().catch(false),
|
|
professional_certification_mentioned: z.boolean().catch(false),
|
|
});
|
|
|
|
export const applyOptionSchema = z.object({
|
|
publisher: z.string().catch(""),
|
|
apply_link: z.string().catch(""),
|
|
is_direct: z.boolean().catch(false),
|
|
});
|
|
|
|
export const jobResultSchema = z.object({
|
|
job_id: z.string(),
|
|
job_title: z.string(),
|
|
employer_name: z.string(),
|
|
employer_logo: z.string().nullable().catch(null),
|
|
employer_website: z.string().nullable().catch(null),
|
|
employer_company_type: z.string().nullable().catch(null),
|
|
employer_linkedin: z.string().nullable().catch(null),
|
|
job_publisher: z.string().catch(""),
|
|
job_employment_type: z.string().catch(""),
|
|
job_apply_link: z.string().catch(""),
|
|
job_apply_is_direct: z.boolean().catch(false),
|
|
job_apply_quality_score: z.number().nullable().catch(null),
|
|
job_description: z.string().catch(""),
|
|
job_is_remote: z.boolean().catch(false),
|
|
job_city: z.string().catch(""),
|
|
job_state: z.string().catch(""),
|
|
job_country: z.string().catch(""),
|
|
job_latitude: z.number().nullable().catch(null),
|
|
job_longitude: z.number().nullable().catch(null),
|
|
job_posted_at_timestamp: z.number().nullable().catch(null),
|
|
job_posted_at_datetime_utc: z.string().catch(""),
|
|
job_offer_expiration_datetime_utc: z.string().nullable().catch(null),
|
|
job_offer_expiration_timestamp: z.number().nullable().catch(null),
|
|
job_min_salary: z.number().nullable().catch(null),
|
|
job_max_salary: z.number().nullable().catch(null),
|
|
job_salary_currency: z.string().nullable().catch(null),
|
|
job_salary_period: z.string().nullable().catch(null),
|
|
job_benefits: z.array(z.string()).nullable().catch(null),
|
|
job_google_link: z.string().nullable().catch(null),
|
|
job_required_experience: jobRequiredExperienceSchema.catch({
|
|
no_experience_required: false,
|
|
required_experience_in_months: null,
|
|
experience_mentioned: false,
|
|
experience_preferred: false,
|
|
}),
|
|
job_required_skills: z.array(z.string()).nullable().catch(null),
|
|
job_required_education: jobRequiredEducationSchema.catch({
|
|
postgraduate_degree: false,
|
|
professional_certification: false,
|
|
high_school: false,
|
|
associates_degree: false,
|
|
bachelors_degree: false,
|
|
degree_mentioned: false,
|
|
degree_preferred: false,
|
|
professional_certification_mentioned: false,
|
|
}),
|
|
job_experience_in_place_of_education: z.boolean().nullable().catch(null),
|
|
job_highlights: z.record(z.string(), z.array(z.string())).nullable().catch(null),
|
|
job_posting_language: z.string().nullable().catch(null),
|
|
job_onet_soc: z.string().nullable().catch(null),
|
|
job_onet_job_zone: z.string().nullable().catch(null),
|
|
job_occupational_categories: z.array(z.string()).nullable().catch(null),
|
|
job_naics_code: z.string().nullable().catch(null),
|
|
job_naics_name: z.string().nullable().catch(null),
|
|
apply_options: z.array(applyOptionSchema).catch([]),
|
|
});
|
|
|
|
export type JobResult = z.infer<typeof jobResultSchema>;
|
|
|
|
export const searchResponseSchema = z.object({
|
|
status: z.string(),
|
|
request_id: z.string(),
|
|
parameters: z.record(z.string(), z.string()).catch({}),
|
|
data: z.array(jobResultSchema).catch([]),
|
|
});
|
|
|
|
export type SearchResponse = z.infer<typeof searchResponseSchema>;
|
|
|
|
export const jobDetailsResponseSchema = z.object({
|
|
status: z.string(),
|
|
request_id: z.string(),
|
|
data: z.array(jobResultSchema).catch([]),
|
|
});
|
|
|
|
export type JobDetailsResponse = z.infer<typeof jobDetailsResponseSchema>;
|
|
|
|
// --- Client-side Filter Types ---
|
|
|
|
export const postFilterOptionsSchema = z
|
|
.object({
|
|
minSalary: z.number().nonnegative().optional(),
|
|
maxSalary: z.number().nonnegative().optional(),
|
|
includeKeywords: z.array(z.string()).optional(),
|
|
excludeKeywords: z.array(z.string()).optional(),
|
|
excludeCompanies: z.array(z.string()).optional(),
|
|
directApplyOnly: z.boolean().optional(),
|
|
})
|
|
.refine((value) => value.minSalary == null || value.maxSalary == null || value.minSalary <= value.maxSalary, {
|
|
message: "minSalary must be less than or equal to maxSalary",
|
|
path: ["minSalary"],
|
|
});
|
|
|
|
export type PostFilterOptions = z.infer<typeof postFilterOptionsSchema>;
|
|
|
|
// --- Rate Limiting Types ---
|
|
|
|
export const rapidApiQuotaSchema = z.object({
|
|
limit: z.number(),
|
|
remaining: z.number(),
|
|
used: z.number(),
|
|
});
|
|
|
|
export type RapidApiQuota = z.infer<typeof rapidApiQuotaSchema>;
|