-
Notifications
You must be signed in to change notification settings - Fork 0
LET: 103 | feat: logic implement inline editing for resumes directly from the #201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
LET: 103 | feat: logic implement inline editing for resumes directly from the #201
Conversation
…view (#155) * feat: admin protected route to render a resume for screenshotting job * fix: lint errors
…ll editor fixes (#146) * feat: integrate SkillsEditor into ResumeEditor component * feat: add preferred skills recommendation message in SkillsEditor * lint: fix lint issue * chore: bun sync reinstall packages in bun.lock * Create bun.lock * lint: fix lint issue in Resume editor and tabs * fix(skill): improve handling of skill categories and enhance API integration for existing skills * fix(skill): enhance custom skill handling and improve API integration for skill details retrieval * lint: fix linter issues * chore: remove debugging colors from ui * refactor(SkillsEditor): clean up imports by removing unused components * fix: review code rabbit comments and optimise code quality for skill editor * refactor(SkillAutocomplete): update skill type to GlobalSkill for improved type safety * feat: integrate SkillsEditor into ResumeEditor component * feat: add preferred skills recommendation message in SkillsEditor * lint: fix lint issue * chore: bun sync reinstall packages in bun.lock * Create bun.lock * feat: integrate SkillsEditor into ResumeEditor component * feat: add preferred skills recommendation message in SkillsEditor * lint: fix lint issue * chore: bun sync reinstall packages in bun.lock * Create bun.lock * chore: update Next.js version to 15.2.4 in bun.lock * fix: update ProficiencySlider logic to prevent out-of-bounds access * refactor: clean up SkillsEditor by removing unused imports and state variables * refactor: simplify skill parsing logic in fetchGlobalSkills function * feat: integrate SkillsEditor into ResumeEditor component * feat(editor): use expandable tabs from 21st.dev * lint: fix lint issue in Resume editor and tabs * fix(skill): improve handling of skill categories and enhance API integration for existing skills * fix(skill): enhance custom skill handling and improve API integration for skill details retrieval * lint: fix linter issues * chore: remove debugging colors from ui * refactor(SkillsEditor): clean up imports by removing unused components * fix: review code rabbit comments and optimise code quality for skill editor * refactor(SkillAutocomplete): update skill type to GlobalSkill for improved type safety * fix: resolve merge conflicts * fix: remove unnecessary resume prop from ResumeEditor * fix: Refactor ResumeEditor to use conditional rendering * feat(skill-editor): build autocomplete for the skill categories * lint: fix lint issue * feat(skill-editor): add smooth animation for the proficiency slider * fix(ProficiencySlider): enhance cleanup logic to close audio context on unmount
…g and the dashboard (#154) * feat: go-to-dashboard button on onboarding last step * fix: view-transition not working when navigating to dashboard from onboarding * feat: welcome animation on dashboard with mask reveal * fix: add group class on button to enable animation on arrow * chore: implement CodeRabbit suggestions * feat: auto-redirect to onboarding and leave-off continuation * fix: remove deprecated afterSignOutUrl signature * fix: users not being redirected to app after sign up * chore: implement CodeRabbit suggestions * fix: build failing due to enum type expectation from OnboardingSteps * fix: onboarding hooks type issues
* fix: onboarding forms doesn't reset properly, current indicator for onboarding forms and location aware country pre-selection * feat: education & experience list item expand on hover on onboarding * fix: list of education and experience isn't scrollable * fix: sanitize HTML content in education & experience list * fix: remove smooth scrolling from education & experience list * fix: update education component container height for responsive sizing
* feat: add certifications tab and editor to ResumeEditor component * refactor: integrate DatePicker for issue date input, improve date formatting for API compatibility, and enhance validation in CertificationMutation schema. * refactor: update CertificationEditor to handle optional issuing organization and issue date, improve API compatibility in actions, and enhance validation in CertificationMutation schema. * style: minor styling improvements --------- Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
…esign (#162) * feat(editor): Add feature flag for new resume editor tabs design * fix: animations of individual resume editors * fix: restore design flare on traditional tabs --------- Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
* feat(ProjectEditor): Integrate ProjectEditor into ResumeEditor component * refactor(ProjectEditor): streamline project mutation handling and enhance error messaging * refactor(ProjectEditor): enhance skills section by grouping skills by category and improving display * fix(ResumeEditor): correct indentation for Projects tab entry * refactor(ProjectEditor): update skill category handling to allow null values and improve URL formatting logic * refactor(ProjectEditor): enhance skill management by integrating resume skills and improving skill input handling * fix(ProjectEditor): correct indentation for skill addition logic in ProjectEditor component * style(ProjectEditor): design improvements and animations --------- Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
* feat(resume): Add Skills section support to default resume theme * lint(resume): fix lint issue
… Clerk's SDK (#166) * feat: identify users in Sentry from Next.js service through Clerk's SDK * fix: improve type safety in user ID handling in sentry configs * chore: remove duplicate redundant logic
… Clerks's SDK (#169) * feat: identify users in Posthog from NextJS service through Clerks's SDK * fix: implement CodeRabbit suggestion
* feat: loading indicator skeleton for resume views and resume editors * feat: improve resume page animation with smooth transition * fix: lint errors * feat: improve resume editor skeleton by including tabs & intelligent editor header skeleton usage * fix: deterministic randomization to avoid hydration error * fix: implement CodeRabbit suggestion
…tructure of backend response (#171)
* feat(resume): Add Projects section support to default resume theme * refactor(resume): Remove accomplishments handling from ProjectController and ProjectsSection * fix(ProjectsSection): Update project links separator to use aria-hidden span for improved accessibility * feat(ProjectsSection): Add project separator styling for improved layout and accessibility
* feat(resume): Add Certification section support to default resume theme * refactor(CertificationController): Extract month names to a constant for improved readability in date formatting
* feat: add comprehensive test utilities and helpers for vitest unit testing * test: fix date timezone handling and window mocking in lib/utils tests * test: cover ShadCN button component with unit test * test: set up test execution scripts and watch mode * fix: type errors in test configurations * test: create additional component tests for complex components * fix: type errors and import path * feat: add tests for API utilities and business logic * fix: change relative path imports to import aliases * test: unit tests for resume system components * test: unit tests for education editor components * fix: type errors in ResumeViewer.test.tsx * test: unit tests for ExperienceEditor component * test: unit tests for PersonalDetailsEditor component * test: unit tests for SkillEditor component * test: unit tests for ThemeFactory component * test: unit tests for DefaultTheme component * test: unit tests for PersonalInfoSection component * test: unit tests for education & experience resume sections * fix: lint errors * fix: type issues in test files * test: unit tests for onboarding components * test: unit tests for BaseResume component * fix: file location for test file of BaseResume component * test: add tests for skill server actions * test: add tests for experience server actions * fix: streamline server actions for resume * test: unit tests for resume server actions * fix: lint errors in test of resume server actions * test: add unit tests for onboarding server actions * test: unit tests for query & mutation hooks of skill & experience * fix: lint errors in unit test file * test: unit tests for education query & mutation hooks * test: unit tests for resume query & mutation hooks * test: unit tests for user-info query & mutation hooks * fix: lint errors in unit test files * fix: import paths to alias based from relative * test: unit tests for ErrorView component * test: unit tests for TextAnimations component * test: unit tests for WebsiteNavBar component * fix: lint errors in TextAnimations unit test file * test: unit tests for AppSidebar component * test: unit tests for AppLayoutContainer component * test: unit tests for RichTextEditor component * test: unit tests for Toolbar component * chore: fix merge conflicts
…pected and real data of resume object (#175) * fix: resume parsing fails due to mismatch parser configuration * fix: middleware lint issues
* feat: Add auto-focus hooks to resume editor components * feat(resume): Integrate ResumeHighlightContext for enhanced item highlighting across editors and sections * refactor(resume): Remove console logs from ResumeHighlightContext and editors for cleaner code * lint: fix linter issues * feat(resume): Enhance ResumeHighlightContext with timeout management for highlighting and scrolling * feat(resume): Update generateSelector to handle missing item IDs and add certification case * feat(resume): Enhance useAutoFocus and useAutoFocusField hooks to support scoped element focusing and increase default delay * feat(resume): Implement cleanup for timeouts in ResumeHighlightContext to prevent memory leaks on component unmount * feat(resume): Add inline scrolling behavior to ResumeHighlightContext for improved element visibility * fix(resume): Clean up formatting in useAutoFocus and useAutoFocusField hooks for improved readability * fix(resume): Add scroll threshold check in ResumeHighlightContext to prevent unnecessary scrolling for already visible elements * fix(resume): Adjust scroll threshold to 100 pixels and implement breathing room for improved element visibility in ResumeHighlightContext * fix(resume): Refactor scrolling logic in ResumeHighlightContext to improve element visibility and ensure optimal scroll position within scrollable containers * feat(resume): Introduce clearHighlight function in ResumeHighlightContext and update editors to utilize it for improved highlight management * feat(resume): Add TitleDimWrapper component to manage title dimming effect on highlight and refactor title components for cleaner implementation * feat(resume): Integrate useAutoFocusField hook in CertificationEditor and ProjectEditor to auto-focus the first field when the form is opened * feat: transition for highlighting in resume preview * feat: include certification section in the overall improvement --------- Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
…re validation mismatch (#177) * fix: resume structure mismatch & preview route error * fix: type errors in unit test * fix: update description of the status field in job
… structured content (#179) * feat: initial implementation of uploaded resume parsing and processing * fix: improve model response time * fix: sanitize old resume tailoring flow & implement CodeRabbit suggestion * chore: improve generic schema to return description more deterministically for resume * chore: implement CodeRabbit suggestion * chore: improve null handling of job data
…nd end dates for experiences and education (#181)
…data automatically during onboarding (#185) * fix: project type mismatch with backend's signature * feat: build and polish UI for uploading existing resume during onboarding * feat: logic for uploading and parsing resume files during onboarding * feat: api mutation for replacing a resume details by id * feat: wire api mutation for parsing resume and updating base resume * feat: optimize resume parsing, default_theme styling fix * chore: replace console statements with toast notifications. * chore: fix lint errors * fix: list not scrollable for education, experience, certification and skills * feat: animation and overall UX improvement on resume parsing feature * fix: key error * chore: implement CodeRabbit suggestions * Fix education query options import and usage * fix: implement CodeRabbit suggestion * fix: streamline prompt by removing redundant skill_id generation
…n the dashboard (#187) * feat: data fetching integration for getting all resumes * chore: remove redundant console.logs * feat: display resume cards in dashboard * chore: implement CodeRabbit suggestions
…az (#191) * feat: create privacy policy and terms of use pages * chore: remove decorative image from accessibility tree * fix: spacing issue and CodeRabbit suggestion * fix: implement shared layout for legal pages & optimize decorative image size * fix: lint errors
…he resume, as the system supports date up to current date-time (#189)
…#192) * feat: implement search functionality in the client-side app using Algolia * feat: add @mantine/hooks dependency and update dashboard resume grid for improved filtering and search functionality * refactor: replace DashboardResumeGrid with ResumeCard in AppHome and update search container to use ResumeSearch component * feat: enhance DashboardSearchInput with focus state and update ResumeCard to highlight search query in job details * refactor: update component exports to use consistent function declarations and improve code readability * fix: fix linter issues * fix: replace deprecated instantSearch package with newer version * refactor: clean up env.ts formatting, update DashboardSearchContainer and DashboardSearchInput styles, and correct import paths for ResumeSearch component * fix: uncomment redirect logic for users who have completed onboarding to ensure proper navigation to the main app * fix: add aria-label for accessibility in DashboardSearchInput and prevent rendering of ResumeSearch component when userId is not provided * fix: scrollbar on thumbnails, relative path import & non-optional algolia env variables * feat: dashboard new layout design improvement * style: padding unification in search input and resume-grid * fix: temporary disable sentry component annotation * feat: enhance Algolia search results handling with caching and smart scrolling * feat: conditionally render DashboardSearchContainer based on user resumes and highlight job location in ResumeCard * refactor: improve type safety in ResumeSearch component and update grid selector in DashboardSearchContainer * refactor: optimize caching and scrolling behavior in ResumeSearch component for improved user experience * refactor: update Algolia search results handling to use 'items' instead of deprecated 'hits' for improved compatibility * refactor: enhance error handling in Algolia search results by enabling catchError option in useInstantSearch --------- Co-authored-by: Sourabh Rathour <rathoursourabh5@gmail.com> Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
* feat: Enhance NewResumeInput component with URL validation and resume tailoring functionality * fix: improve resume processing UI * lint: fix linter issues * feat: Replace AiLoading with ResumeAiLoading component in processing UI * refactor: Remove unused loading messages and optimize loading component styles in resume processing UI * lint: fix linter issues * feat: Update resume editors to utilize resumeId from URL parameters for certification, education, experience, and project * feat: Update resume editors and queries to include resumeId for improved data handling * chore: fix lint issues * refactor: Simplify processing UI logic and enhance loading state handling in ResumeAiLoading component * feat: Integrate resumeId into skill and project mutations and queries for consistent data handling across editors * refactor: Remove console logging from experience mutation functions for cleaner code * Update React Compiler to latest RC version * chore: fix lint issues * Pass resumeId explicitly to query hooks * refactor: Update query options to use default resumeId for improved flexibility in data fetching * fix: lint errors * attempt: remove project normalization from getProjectsFromDB action (double-check) * feat: replace AILoading with updated animation video * refactor: update query options and mutation parameters for education and experience components * fix: pass resumeId to useCurrentResumeSkills for accurate data fetching * fix: pass resumeId to useSkillCategories for accurate data fetching * fix: update skill data access in SkillsEditor for correct mutation handling * fix: normalize thumbnail url to account for lack of protocol in URL --------- Co-authored-by: Subhajit Kundu <subha60kundu@gmail.com>
…onality in EducationSection
…andles in ReorderableSections and enhance SaveIndicator with tooltip functionality in EducationSection
…ection functionality
… with improved error handling and type safety
…ancellation and improve code organization
… types for improved type safety
…ave functionality and improved user interaction
…with debounced save functionality and improved user interaction
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedFailed to post review comments. WalkthroughAdds extensive testing infrastructure and suites, new onboarding flows (resume parsing/upload, flow control, welcome), dashboard search components, resume editor/view revamp with controllers and new editors (Skills, Certifications, Projects) with per-resume data, new providers (Sentry/PostHog identity), admin resume preview, legal pages, UI animations/styles, env flag, and removes legacy JD parsing/summarization. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as NewResumeInput
participant API as Tailor Resume API
participant Router as Next Router
User->>UI: Enter job URL/description + submit
UI->>API: mutate({ target })
API-->>UI: { id }
UI->>Router: push /app/craft/resumes/{id}
sequenceDiagram
autonumber
actor User
participant Page as ParseResume (Onboarding)
participant API as /api/resume/parse
participant SVC as ReplaceResume(base)
participant Router as Next Router
User->>Page: Drop/upload file
Page->>API: POST form-data (file, format)
API-->>Page: { parsed resume payload }
Page->>SVC: mutate({ resumeId:'base', data })
SVC-->>Page: success
Page->>Router: replace /app/onboarding?step=resume
sequenceDiagram
autonumber
participant Clerk as Clerk (auth)
participant Sentry as SentryUserProvider
participant PH as PosthogProvider
Clerk-->>Sentry: userId, user (loaded)
Sentry->>Sentry: setUser/clearUser + tags
Clerk-->>PH: userId, user (loaded)
PH->>PH: posthog.identify / group / reset (prod only)
sequenceDiagram
autonumber
participant AdminPage as /admin/resumes/[id]
participant Env as ENV (API_URL, ADMIN_KEY)
participant Backend as Admin API
AdminPage->>AdminPage: validate token=SELF_SECRET_KEY
AdminPage->>Backend: GET /admin/resumes/{id} (no-store)
Backend-->>AdminPage: Resume | 404
AdminPage-->>AdminPage: notFound() on 404/error
AdminPage->>UI: Render ResumeViewer (no anim) + Provider
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes Possibly related issues
Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 81
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
app/(website)/changes/page.heading.tsx (1)
21-24: Add rel="noopener noreferrer" to external link opened with target="_blank".Prevents reverse tabnabbing and isolates the opener window.
Apply this diff:
- Join our <a href={discordHandle} - target="_blank" + Join our <a href={discordHandle} + target="_blank" rel="noopener noreferrer" className="font-semibold text-flame-500 focus-visible:underline hover:underline" >Discord</a> to stay connected to more frequent updates and help us build the community.env.example (1)
25-26: Document and default new design feature flag in .env.example
Parsing via strict=== 'true'in ResumeEditor.tsx is already correct; no helper needed.Add a docs section and default the flag to false:
+## UI flags +# Toggle the new Resume Editor tabs design (client-side). Accepts "true" | "false". -NEXT_PUBLIC_RESUME_EDITOR_TABS_NEW_DESIGN_ENABLED=true +NEXT_PUBLIC_RESUME_EDITOR_TABS_NEW_DESIGN_ENABLED=falsecomponents/notifications/NotificationFeed.tsx (1)
60-61: Enforce safe URL protocols in sanitizeHtml
lib/utils.ts: sanitizeHtml currently uses DOMPurify with ALLOWED_TAGS and ALLOWED_ATTR but doesn’t restrict URI schemes, sohref="javascript:…"still passes. Add a protocol whitelist (e.g.ALLOWED_URI_REGEXP: /^(?:https?:|mailto:|tel:)/i) or a hook to strip unsafe protocols, and add tests to coverjavascript:links.components/resume/controllers/ExperienceController.tsx (1)
96-99: Populate dates.current.Keeps data shape consistent with the interface.
- const dates = { - hasDates: Boolean(dateRange), - formatted: dateRange - } + const dates = { + hasDates: Boolean(dateRange), + formatted: dateRange, + current: Boolean(experience.current) + }components/resume/editors/shared/CountrySelect.tsx (1)
60-71: Guard Next/Image src; avoid rendering with empty/unknown flag.countries.find(...) may return undefined; passing '' to Image src will throw. Also avoid repeated finds.
Apply this diff:
- <SelectValue placeholder={placeholder}> - {field.value && ( - <span className="flex items-center"> - <Image - src={countries.find(c => c.code === field.value)?.flag || ''} - width={64} - height={64} - alt={`The flag of ${countries.find(c => c.code === field.value)?.name}`} - className="mr-2 w-6" - /> - {countries.find(c => c.code === field.value)?.name || field.value} - </span> - )} - </SelectValue> + <SelectValue placeholder={placeholder}> + {field.value && (() => { + const selected = countries.find(c => c.code === field.value) + return ( + <span className="flex items-center"> + {selected ? ( + <> + <Image + src={selected.flag} + width={64} + height={64} + alt={`The flag of ${selected.name}`} + className="mr-2 w-6" + /> + {selected.name} + </> + ) : ( + field.value + )} + </span> + ) + })()} + </SelectValue>components/resume/editors/ExperienceEditor.tsx (1)
154-158: Remove incorrect label-to-code conversion for employment_type
Incomponents/resume/editors/ExperienceEditor.tsxthe edit handler assumesexperience.employment_typeis a label and looks up its code (type.label === experience.employment_type), but the model already stores the code (e.g.'flt'), so it always falls back to'flt'. Remove that lookup and reset the form with the code directly:- // Find the employment type code that matches the label in the data - const employmentTypeCode = employmentTypes.find( - type => type.label === experience.employment_type - )?.value || 'flt' form.reset({ ...experience, - employment_type: employmentTypeCode, + employment_type: experience.employment_type, country: experience.country.code, started_from_month: experience.started_from_month?.toString() || null, /* … */ })components/resume/editors/EducationEditor.tsx (1)
188-195: Fix: possible crash when country is undefined in edit modeAccessing
education.country.codewill throw ifcountryis null/undefined. Use optional chaining with a safe fallback.- country: education.country.code, + country: education.country?.code ?? '',components/resume/editors/SkillsEditor.tsx (1)
239-257: Potential crash when skill.alias is undefined
skill.alias.some(...)assumesaliasexists. Guard it to avoid runtime errors with incomplete data.- .filter(skill => (skill.name.toLowerCase().includes(searchQuery.toLowerCase()) || - skill.category?.toLowerCase().includes(searchQuery.toLowerCase()) || - skill.alias.some(alias => alias.name.toLowerCase().includes(searchQuery.toLowerCase()))) && + .filter(skill => (skill.name.toLowerCase().includes(searchQuery.toLowerCase()) || + skill.category?.toLowerCase().includes(searchQuery.toLowerCase()) || + (Array.isArray(skill.alias) && skill.alias.some(a => a.name.toLowerCase().includes(searchQuery.toLowerCase())))) && !resumeSkills.some(rs => rs.skill.id === skill.id))
| if (pattern instanceof RegExp) { | ||
| matches = pattern.test(url) | ||
| } else { | ||
| matches = url.includes(pattern) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Reset RegExp lastIndex to avoid flaky matches with /g or /y
.test() on global/sticky regex mutates lastIndex and may intermittently fail on repeated calls.
Apply:
- if (pattern instanceof RegExp) {
- matches = pattern.test(url)
+ if (pattern instanceof RegExp) {
+ pattern.lastIndex = 0
+ matches = pattern.test(url)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (pattern instanceof RegExp) { | |
| matches = pattern.test(url) | |
| } else { | |
| matches = url.includes(pattern) | |
| } | |
| if (pattern instanceof RegExp) { | |
| pattern.lastIndex = 0 | |
| matches = pattern.test(url) | |
| } else { | |
| matches = url.includes(pattern) | |
| } |
🤖 Prompt for AI Agents
In __tests__/helpers/api-mocks.ts around lines 55 to 59, the use of
pattern.test(url) on a RegExp can be flaky because .test() mutates lastIndex for
global (g) or sticky (y) regexes; before calling test(), reset pattern.lastIndex
= 0 (or clone the RegExp without those flags) so repeated calls behave
deterministically, then proceed to call pattern.test(url).
| const setupFetchMock = (): void => { | ||
| global.fetch = vi.fn().mockImplementation(async (url: string, init?: RequestInit) => { | ||
| const mockConfig = fetchMockManager.getMockResponse(url, init) | ||
|
|
||
| if (!mockConfig) { | ||
| throw new Error(`No mock configured for URL: ${url}`) | ||
| } | ||
|
|
||
| // Simulate network delay if specified | ||
| if (mockConfig.delay) { | ||
| await new Promise(resolve => setTimeout(resolve, mockConfig.delay)) | ||
| } | ||
|
|
||
| const { | ||
| data = null, | ||
| status = 200, | ||
| statusText = 'OK', | ||
| headers = {} | ||
| } = mockConfig | ||
|
|
||
| const response = { | ||
| ok: status >= 200 && status < 300, | ||
| status, | ||
| statusText, | ||
| headers: new Headers(headers), | ||
| url, | ||
| redirected: false, | ||
| type: 'basic' as ResponseType, | ||
| body: null, | ||
| bodyUsed: false, | ||
| clone: vi.fn(), | ||
| json: vi.fn().mockResolvedValue(data), | ||
| text: vi.fn().mockResolvedValue(JSON.stringify(data)), | ||
| arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)), | ||
| blob: vi.fn().mockResolvedValue(new Blob()), | ||
| formData: vi.fn().mockResolvedValue(new FormData()), | ||
| bytes: vi.fn().mockResolvedValue(new Uint8Array()) | ||
| } | ||
|
|
||
| return response as Response | ||
| }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Request and URL inputs in fetch mock to avoid runtime errors
Current mock assumes the first arg is a string. Real fetch callers often pass Request or URL, which will break matching and verification.
Apply:
-const setupFetchMock = (): void => {
- global.fetch = vi.fn().mockImplementation(async (url: string, init?: RequestInit) => {
- const mockConfig = fetchMockManager.getMockResponse(url, init)
+const setupFetchMock = (): void => {
+ global.fetch = vi.fn().mockImplementation(async (input: RequestInfo | URL, init?: RequestInit) => {
+ const url =
+ typeof input === 'string'
+ ? input
+ : input instanceof URL
+ ? input.toString()
+ : (input as Request).url
+
+ const mockConfig = fetchMockManager.getMockResponse(url, init)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const setupFetchMock = (): void => { | |
| global.fetch = vi.fn().mockImplementation(async (url: string, init?: RequestInit) => { | |
| const mockConfig = fetchMockManager.getMockResponse(url, init) | |
| if (!mockConfig) { | |
| throw new Error(`No mock configured for URL: ${url}`) | |
| } | |
| // Simulate network delay if specified | |
| if (mockConfig.delay) { | |
| await new Promise(resolve => setTimeout(resolve, mockConfig.delay)) | |
| } | |
| const { | |
| data = null, | |
| status = 200, | |
| statusText = 'OK', | |
| headers = {} | |
| } = mockConfig | |
| const response = { | |
| ok: status >= 200 && status < 300, | |
| status, | |
| statusText, | |
| headers: new Headers(headers), | |
| url, | |
| redirected: false, | |
| type: 'basic' as ResponseType, | |
| body: null, | |
| bodyUsed: false, | |
| clone: vi.fn(), | |
| json: vi.fn().mockResolvedValue(data), | |
| text: vi.fn().mockResolvedValue(JSON.stringify(data)), | |
| arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)), | |
| blob: vi.fn().mockResolvedValue(new Blob()), | |
| formData: vi.fn().mockResolvedValue(new FormData()), | |
| bytes: vi.fn().mockResolvedValue(new Uint8Array()) | |
| } | |
| return response as Response | |
| }) | |
| } | |
| const setupFetchMock = (): void => { | |
| global.fetch = vi.fn().mockImplementation(async (input: RequestInfo | URL, init?: RequestInit) => { | |
| const url = | |
| typeof input === 'string' | |
| ? input | |
| : input instanceof URL | |
| ? input.toString() | |
| : (input as Request).url | |
| const mockConfig = fetchMockManager.getMockResponse(url, init) | |
| if (!mockConfig) { | |
| throw new Error(`No mock configured for URL: ${url}`) | |
| } | |
| // Simulate network delay if specified | |
| if (mockConfig.delay) { | |
| await new Promise(resolve => setTimeout(resolve, mockConfig.delay)) | |
| } | |
| const { | |
| data = null, | |
| status = 200, | |
| statusText = 'OK', | |
| headers = {} | |
| } = mockConfig | |
| const response = { | |
| ok: status >= 200 && status < 300, | |
| status, | |
| statusText, | |
| headers: new Headers(headers), | |
| url, | |
| redirected: false, | |
| type: 'basic' as ResponseType, | |
| body: null, | |
| bodyUsed: false, | |
| clone: vi.fn(), | |
| json: vi.fn().mockResolvedValue(data), | |
| text: vi.fn().mockResolvedValue(JSON.stringify(data)), | |
| arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)), | |
| blob: vi.fn().mockResolvedValue(new Blob()), | |
| formData: vi.fn().mockResolvedValue(new FormData()), | |
| bytes: vi.fn().mockResolvedValue(new Uint8Array()) | |
| } | |
| return response as Response | |
| }) | |
| } |
🤖 Prompt for AI Agents
In __tests__/helpers/api-mocks.ts around lines 75 to 116, the fetch mock assumes
the first argument is a string which breaks when callers pass a Request or URL;
update the mock implementation to normalize the inputs by detecting if the first
arg is a Request, URL, or string, extracting the URL string (use request.url or
url.toString()), and when a Request is passed also extract its init-like values
(method, headers, body) to pass into fetchMockManager.getMockResponse; then use
the normalized url string for error messages and matching so getMockResponse
receives a consistent URL string and the thrown error includes the normalized
URL.
| if (url) { | ||
| const calls = fetchMock.mock.calls.filter(call => { | ||
| const callUrl = call[0] as string | ||
| if (url instanceof RegExp) { | ||
| return url.test(callUrl) | ||
| } | ||
| return callUrl.includes(url) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify helper must support Request/URL inputs
verifyFetchCalled casts to string and misses Request/URL cases, causing false negatives.
Apply:
- const callUrl = call[0] as string
+ const arg = call[0]
+ const callUrl =
+ typeof arg === 'string'
+ ? arg
+ : arg instanceof URL
+ ? arg.toString()
+ : (arg as Request).url📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (url) { | |
| const calls = fetchMock.mock.calls.filter(call => { | |
| const callUrl = call[0] as string | |
| if (url instanceof RegExp) { | |
| return url.test(callUrl) | |
| } | |
| return callUrl.includes(url) | |
| }) | |
| if (url) { | |
| const calls = fetchMock.mock.calls.filter(call => { | |
| const arg = call[0] | |
| const callUrl = | |
| typeof arg === 'string' | |
| ? arg | |
| : arg instanceof URL | |
| ? arg.toString() | |
| : (arg as Request).url | |
| if (url instanceof RegExp) { | |
| return url.test(callUrl) | |
| } | |
| return callUrl.includes(url) | |
| }) |
🤖 Prompt for AI Agents
In __tests__/helpers/api-mocks.ts around lines 231 to 238, verifyFetchCalled
wrongly assumes fetchMock call[0] is a string and therefore misses cases where
the request argument is a Request or URL; update the filter to detect if call[0]
is a Request (use (call[0] as Request).url), a URL (use (call[0] as URL).href or
toString()), or already a string, and normalize to a string before testing
against the provided url (handling url being a RegExp, URL, Request, or string)
so Request/URL inputs are correctly matched.
| // Job-related mock data | ||
| export const createMockJob = (overrides?: Partial<Job>): Job => ({ | ||
| title: 'Software Engineer', | ||
| company_name: 'Test Company', | ||
| description: 'Test job description', | ||
| requirements: ['React', 'TypeScript'], | ||
| location: 'Remote', | ||
| job_url: 'https://example.com/job', | ||
| currency: 'USD', | ||
| salary_min: 80000, | ||
| salary_max: 120000, | ||
| responsibilities: ['Build web applications'], | ||
| benefits: ['Health insurance', 'Remote work'], | ||
| ...overrides | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure createMockJob matches the latest Job type.
If Job adds/renames fields, this factory will silently drift.
🏁 Script executed:
#!/bin/bash
# Show JobSchema shape and usages
rg -nP 'JobSchema|type\s+Job\s*=' --type=ts -C3Length of output: 2961
Sync createMockJob with the current Job schema. Update the mock factory so its default properties exactly match z.infer<typeof JobSchema>—add any newly required fields and remove or rename any that no longer exist.
🤖 Prompt for AI Agents
In __tests__/helpers/mock-factories.ts around lines 133 to 147, the
createMockJob factory no longer matches the current Job schema inferred from
JobSchema; update the factory so its default properties exactly mirror
z.infer<typeof JobSchema> by (1) comparing the JobSchema fields and types and
adding any newly required fields (with reasonable defaults), (2) removing or
renaming properties that no longer exist (e.g., company_name → companyName,
salary_min → salaryMin, etc.) to match schema keys, (3) ensuring optional fields
are present or omitted according to the schema and type-correct (arrays vs
strings vs numbers), and (4) keeping the final ...overrides spread so tests can
override defaults; adjust imports if necessary to reference JobSchema/Job type
used in the tests.
| import {describe, expect, it} from 'vitest' | ||
| import {render, screen} from '@testing-library/react' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure jest-dom matchers are registered globally
These tests rely on jest-dom matchers (toBeInTheDocument, toBeVisible, toHaveTextContent). Please verify global setup; otherwise they’ll fail with “expect(...).toBeInTheDocument is not a function.”
Run to verify configuration:
If missing, add:
// vitest.setup.ts
import '@testing-library/jest-dom/vitest'// vitest.config.ts
import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
},
})🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for jest-dom setup and jsdom environment..."
rg -n "jest-dom/(vitest|extend-expect)" -S || echo "NOT FOUND: jest-dom extension import"
fd -a "vitest.setup.*" || echo "No vitest.setup.* file found"
cfgs=$(fd -a "vitest.config.*" || true)
if [ -n "$cfgs" ]; then
rg -n "setupFiles\s*:\s*\[.*vitest\.setup" -S $cfgs || echo "setupFiles not pointing to vitest.setup.*"
rg -n "environment\s*:\s*['\"]jsdom['\"]" -S $cfgs || echo "jsdom environment not set in vitest config"
else
echo "No vitest config file found"
fiLength of output: 698
Add and configure Vitest setup for jest-dom matchers
Tests rely on jest-dom matchers but no setup file is registered and setupFiles isn’t configured in vitest.config.ts. Add a vitest.setup.ts file containing
import '@testing-library/jest-dom/vitest'and update vitest.config.ts under test to include:
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],so that jest-dom matchers are loaded globally.
🤖 Prompt for AI Agents
In __tests__/react-setup.test.tsx around lines 1 to 2, tests use jest-dom
matchers but there is no global setup file registered; create a new
vitest.setup.ts at project root that imports '@testing-library/jest-dom/vitest',
and update vitest.config.ts test options to set environment to 'jsdom' and add
setupFiles pointing to ['./vitest.setup.ts'] so the jest-dom matchers are loaded
for all tests.
| // Process links | ||
| const links = { | ||
| hasLinks: Boolean(project.github_url || project.live_url), | ||
| github: project.github_url || undefined, | ||
| live: project.live_url || undefined | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sanitize/validate external URLs to prevent javascript:/data: XSS.
github_url/live_url can be user-controlled; passing them directly into anchors risks XSS. Whitelist protocols and normalize before exposing.
Apply this diff:
- const links = {
- hasLinks: Boolean(project.github_url || project.live_url),
- github: project.github_url || undefined,
- live: project.live_url || undefined
- }
+ const safeGithub = ensureSafeUrl(project.github_url)
+ const safeLive = ensureSafeUrl(project.live_url)
+ const links = {
+ hasLinks: Boolean(safeGithub || safeLive),
+ github: safeGithub,
+ live: safeLive
+ }Add this helper in the module (top-level), or centralize in a shared util:
function ensureSafeUrl(url?: string | null): string | undefined {
if (!url) return undefined
const trimmed = url.trim()
// allow only https/http/mailto; drop everything else (e.g., javascript:, data:)
return /^(https?:\/\/|mailto:)/i.test(trimmed) ? trimmed : undefined
}🤖 Prompt for AI Agents
In components/resume/controllers/ProjectController.tsx around lines 80 to 85,
the github_url/live_url are used directly which can allow javascript:/data: XSS;
add a top-level helper (or import a shared util) like ensureSafeUrl(url) that
trims the string and only returns it if it matches a whitelist of protocols
(http:, https:, mailto:) otherwise returns undefined, then replace direct uses
of project.github_url and project.live_url with
ensureSafeUrl(project.github_url) and ensureSafeUrl(project.live_url) when
building the links object so only normalized, safe URLs are exposed to anchors.
| @@ -0,0 +1,554 @@ | |||
| import React from 'react' | |||
| import {fireEvent, render, screen, waitFor} from '@testing-library/react' | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use the project’s render helper to include providers.
SkillsEditor likely depends on shared providers (e.g., QueryClient). Use the custom render to avoid hidden coupling issues.
-import {fireEvent, render, screen, waitFor} from '@testing-library/react'
+import {fireEvent, render, screen, waitFor} from '@/__tests__/helpers/test-utils'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import {fireEvent, render, screen, waitFor} from '@testing-library/react' | |
| import {fireEvent, render, screen, waitFor} from '@/__tests__/helpers/test-utils' |
🤖 Prompt for AI Agents
In components/resume/editors/__tests__/SkillsEditor.test.tsx around line 2, the
test imports render directly from @testing-library/react but SkillsEditor relies
on shared providers (e.g., QueryClient); update the test to use the project's
custom render helper that wraps components with the required providers (replace
the import of render from @testing-library/react with the project's render
helper import, remove the direct render import, and call that custom render in
tests), and ensure any waitFor/fireEvent/screen imports remain from
@testing-library/react so the component is rendered with the proper provider
context.
| // Get user's country from IP | ||
| useEffect(() => { | ||
| fetch('https://ipapi.co/json/') | ||
| .then(res => res.json()) | ||
| .then(data => { | ||
| if (data.country_code_iso3) { | ||
| setUserCountry(data.country_code_iso3) | ||
| } | ||
| }) | ||
| .catch(() => { | ||
| // Fallback to India if detection fails | ||
| setUserCountry('IND') | ||
| }) | ||
| }, []) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid client IP calls + remove hardcoded 'IND' fallback.
- Third-party IP lookups on mount have privacy/compliance risks and can be blocked/slow.
- Hardcoding India contradicts prior guidance to avoid hardcoded IDs (per repository learnings).
Apply this change to only fetch when the field is empty, add abort, and drop the default-to-IND:
- // Get user's country from IP
- useEffect(() => {
- fetch('https://ipapi.co/json/')
- .then(res => res.json())
- .then(data => {
- if (data.country_code_iso3) {
- setUserCountry(data.country_code_iso3)
- }
- })
- .catch(() => {
- // Fallback to India if detection fails
- setUserCountry('IND')
- })
- }, [])
+ // Prefill user's country via IP (best-effort) only if empty
+ useEffect(() => {
+ if (form.getValues(name)) return
+ const controller = new AbortController()
+ fetch('https://ipapi.co/json/', {signal: controller.signal})
+ .then(res => res.json())
+ .then(data => {
+ if (data?.country_code_iso3) setUserCountry(data.country_code_iso3)
+ })
+ .catch(() => {
+ // silent: no default country
+ })
+ return () => controller.abort()
+ }, [form, name])If IP prefill must remain, gate it behind a feature flag and update the privacy notice.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Get user's country from IP | |
| useEffect(() => { | |
| fetch('https://ipapi.co/json/') | |
| .then(res => res.json()) | |
| .then(data => { | |
| if (data.country_code_iso3) { | |
| setUserCountry(data.country_code_iso3) | |
| } | |
| }) | |
| .catch(() => { | |
| // Fallback to India if detection fails | |
| setUserCountry('IND') | |
| }) | |
| }, []) | |
| // Prefill user's country via IP (best-effort) only if empty | |
| useEffect(() => { | |
| if (form.getValues(name)) return | |
| const controller = new AbortController() | |
| fetch('https://ipapi.co/json/', { signal: controller.signal }) | |
| .then(res => res.json()) | |
| .then(data => { | |
| if (data?.country_code_iso3) setUserCountry(data.country_code_iso3) | |
| }) | |
| .catch(() => { | |
| // silent: no default country | |
| }) | |
| return () => controller.abort() | |
| }, [form, name]) |
🤖 Prompt for AI Agents
In components/resume/editors/shared/CountrySelect.tsx around lines 27 to 41, the
current useEffect always calls a third‑party IP lookup on mount and falls back
to hardcoded 'IND'; change it to (1) only perform the fetch if the country field
is currently empty, (2) use an AbortController and cancel the request in the
cleanup to avoid leaks, (3) remove the hardcoded fallback (do not set 'IND' on
error), and (4) if IP prefill is required, gate the fetch behind a feature flag
and ensure the privacy notice is updated accordingly; implement these changes so
the effect is conditional, abortable, and no longer writes a default country on
failure.
| showAnimation?: boolean | ||
| } | ||
|
|
||
| const ResumeViewer = ({resume, resumeRef, className}: ResumeViewerProps) => { | ||
| const ResumeViewer = ({resume, resumeRef, className, showAnimation = true}: ResumeViewerProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Honor reduced-motion when defaulting showAnimation
Defaulting to true ignores user OS preference. Derive the effective flag to auto-disable animations when reduced-motion is set, while still allowing explicit overrides.
-const ResumeViewer = ({resume, resumeRef, className, showAnimation = true}: ResumeViewerProps) => {
+const ResumeViewer = (props: ResumeViewerProps) => {
+ const {resume, resumeRef, className, showAnimation} = props
+ const prefersReduced =
+ typeof window !== 'undefined' &&
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches
+ const enableAnimation = (showAnimation ?? true) && !prefersReducedThen replace usages of showAnimation below with enableAnimation.
🤖 Prompt for AI Agents
In components/resume/ResumeViewer.tsx around lines 13 to 16, the prop default
showAnimation=true ignores the user's reduced-motion OS preference; update the
component to compute an enableAnimation boolean that respects reduced-motion
(e.g., use window.matchMedia('(prefers-reduced-motion: reduce)') or a React hook
to detect it) so that enableAnimation = showAnimation when showAnimation is
explicitly provided, otherwise false when reduced-motion is true and true when
not. Replace the defaulted prop logic (remove showAnimation default) and use
enableAnimation for all downstream animation checks and rendering decisions,
ensuring explicit prop overrides still work.
| **We are not currently accepting external contributions to this project.** | ||
|
|
||
| Letraz is currently being developed by our internal team, and we are not seeking outside contributors at this time. | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add security disclosure and issues/feedback channels.
Even if PRs aren’t accepted, you should tell external users how to responsibly report security vulnerabilities and where to file bugs/feedback. This avoids public disclosure in PRs and reduces triage noise.
Apply:
Letraz is currently being developed by our internal team, and we are not seeking outside contributors at this time.
+
+## Reporting Security Vulnerabilities
+If you discover a security issue, please do not open a public issue. Instead, email us at [hello@letraz.app](mailto:hello@letraz.app) with details and a way to reproduce. We will acknowledge receipt within 3 business days.
+
+## Feedback and Bug Reports
+While we’re not accepting external code contributions, we welcome feedback and bug reports. Please use the repository’s Issues tab to open a ticket with steps to reproduce and expected behavior.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **We are not currently accepting external contributions to this project.** | |
| Letraz is currently being developed by our internal team, and we are not seeking outside contributors at this time. | |
| **We are not currently accepting external contributions to this project.** | |
| Letraz is currently being developed by our internal team, and we are not seeking outside contributors at this time. | |
| ## Reporting Security Vulnerabilities | |
| If you discover a security issue, please do not open a public issue. Instead, email us at [hello@letraz.app](mailto:hello@letraz.app) with details and a way to reproduce. We will acknowledge receipt within 3 business days. | |
| ## Feedback and Bug Reports | |
| While we’re not accepting external code contributions, we welcome feedback and bug reports. Please use the repository’s Issues tab to open a ticket with steps to reproduce and expected behavior. |
🤖 Prompt for AI Agents
In CONTRIBUTING.md around lines 5 to 8, the file currently only states that
external contributions are not accepted; add a short Security Policy and
Issue/Feedback section that tells external users how to responsibly report
security vulnerabilities (eg. an email like security@yourdomain or a link to a
private vulnerability reporting form) and where to file bugs/feedback (eg. a
link to the project’s issue tracker or a dedicated feedback email), include
guidance to avoid posting vulnerabilities publicly (do not open PRs for security
issues), specify expected response/triage timeframe, and provide contact/PGP key
info if needed.
Issue:
logic: Implement inline editing for resumes directly from the resume preview
Description:
Implement inline editing functionality within the
ResumeViewcomponent to allow users to edit resume fields directly from the preview.Changes Made:
ResumeViewcomponent and its sub-components.Closing Note:
This PR enhances user experience by enabling direct inline editing of resume fields from the preview, improving efficiency and reducing context switching for users.
Summary by CodeRabbit