From 7caf1e625ef2563e68de1a467b524dd4b21ace96 Mon Sep 17 00:00:00 2001 From: Akshay Date: Mon, 27 Oct 2025 12:39:02 +0530 Subject: [PATCH] refactor(resume): Optimize resume components and error handling - Remove unused imports and simplify component logic - Improve error boundary with explicit error code handling - Enhance error logging and toast error messages - Replace hardcoded values with semantic HTML entities - Remove unnecessary type assertions and unused variables - Simplify error handling in resume-related components - Improve type safety and reduce potential runtime errors - Clean up unused constants and improve code readability --- components/resume/ConfettiEffect.tsx | 1 - components/resume/ResumeErrorBoundary.tsx | 4 ++-- components/resume/ResumeList.tsx | 10 ++++---- components/resume/ResumePreview.tsx | 1 - components/resume/SaveAsDialog.tsx | 2 +- components/resume/SaveErrorBanner.tsx | 2 +- components/resume/StyleCustomizer.tsx | 24 +++++++++---------- components/resume/SuggestionPanel.tsx | 22 ----------------- components/resume/sections/AwardsSection.tsx | 2 +- .../resume/sections/CertificationsSection.tsx | 2 +- .../resume/sections/EducationSection.tsx | 2 +- .../resume/sections/ExperienceSection.tsx | 2 +- .../resume/sections/ProjectsSection.tsx | 2 +- components/resume/sections/SkillsSection.tsx | 19 ++++++++------- .../resume/templates/CreativeTemplate.tsx | 10 ++++---- lib/services/resume-import.ts | 1 + lib/services/resume-scoring.ts | 7 ------ lib/utils/debounce.ts | 3 +++ 18 files changed, 46 insertions(+), 70 deletions(-) diff --git a/components/resume/ConfettiEffect.tsx b/components/resume/ConfettiEffect.tsx index 97901e72..2c488366 100644 --- a/components/resume/ConfettiEffect.tsx +++ b/components/resume/ConfettiEffect.tsx @@ -1,7 +1,6 @@ 'use client'; import { useEffect, useState } from 'react'; -import { cn } from '@/lib/utils'; interface ConfettiPiece { id: number; diff --git a/components/resume/ResumeErrorBoundary.tsx b/components/resume/ResumeErrorBoundary.tsx index 04c16551..5256c399 100644 --- a/components/resume/ResumeErrorBoundary.tsx +++ b/components/resume/ResumeErrorBoundary.tsx @@ -4,7 +4,7 @@ import React, { Component, ReactNode } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { AlertTriangle, RefreshCw } from 'lucide-react'; -import { ResumeError, ErrorLogger, getErrorMessage } from '@/lib/errors/resume-errors'; +import { ResumeError, ResumeErrorCode, ErrorLogger, getErrorMessage } from '@/lib/errors/resume-errors'; interface Props { children: ReactNode; @@ -45,7 +45,7 @@ export class ResumeErrorBoundary extends Component { }); } else { ErrorLogger.log( - new ResumeError(error.message, 'UNKNOWN_ERROR' as any, { + new ResumeError(error.message, ResumeErrorCode.UNKNOWN_ERROR, { originalError: error, componentStack: errorInfo.componentStack, }) diff --git a/components/resume/ResumeList.tsx b/components/resume/ResumeList.tsx index afb92f41..90b12000 100644 --- a/components/resume/ResumeList.tsx +++ b/components/resume/ResumeList.tsx @@ -80,7 +80,7 @@ export function ResumeList({ onResumeSelect, onCreateNew }: ResumeListProps) { toast.success('Resume loaded', { description: `Loaded "${resume.title}"`, }); - } catch (error) { + } catch { toast.error('Failed to load resume', { description: 'Please try again.', }); @@ -94,7 +94,7 @@ export function ResumeList({ onResumeSelect, onCreateNew }: ResumeListProps) { toast.success('Resume duplicated', { description: `Created "${duplicated.title}"`, }); - } catch (error) { + } catch { toast.error('Failed to duplicate resume', { description: 'Please try again.', }); @@ -119,7 +119,7 @@ export function ResumeList({ onResumeSelect, onCreateNew }: ResumeListProps) { }); setDeleteDialogOpen(false); setResumeToDelete(null); - } catch (error) { + } catch { toast.error('Failed to delete resume', { description: 'Please try again.', }); @@ -136,7 +136,7 @@ export function ResumeList({ onResumeSelect, onCreateNew }: ResumeListProps) { toast.success('Resume created', { description: `Created "${newResume.title}"`, }); - } catch (error) { + } catch { toast.error('Failed to create resume', { description: 'Please try again.', }); @@ -277,7 +277,7 @@ export function ResumeList({ onResumeSelect, onCreateNew }: ResumeListProps) { Delete Resume - Are you sure you want to delete "{resumeToDelete?.title}"? This action cannot be undone. + Are you sure you want to delete “{resumeToDelete?.title}”? This action cannot be undone. diff --git a/components/resume/ResumePreview.tsx b/components/resume/ResumePreview.tsx index dcafc3b7..7ae245ec 100644 --- a/components/resume/ResumePreview.tsx +++ b/components/resume/ResumePreview.tsx @@ -9,7 +9,6 @@ import { useState, useEffect, useRef } from 'react'; // Constants for page calculations const PAGE_HEIGHT_INCHES = 11; -const PAGE_WIDTH_INCHES = 8.5; const PIXELS_PER_INCH = 96; // Standard screen DPI const PAGE_HEIGHT_PX = PAGE_HEIGHT_INCHES * PIXELS_PER_INCH; diff --git a/components/resume/SaveAsDialog.tsx b/components/resume/SaveAsDialog.tsx index 6e96eded..1f7e9b0f 100644 --- a/components/resume/SaveAsDialog.tsx +++ b/components/resume/SaveAsDialog.tsx @@ -60,7 +60,7 @@ export function SaveAsDialog({ open, onOpenChange }: SaveAsDialogProps) { }); onOpenChange(false); - } catch (error) { + } catch { toast.error('Failed to save as', { description: 'Please try again.', }); diff --git a/components/resume/SaveErrorBanner.tsx b/components/resume/SaveErrorBanner.tsx index ab355c37..52ea4211 100644 --- a/components/resume/SaveErrorBanner.tsx +++ b/components/resume/SaveErrorBanner.tsx @@ -37,7 +37,7 @@ export function SaveErrorBanner({ onRetry, onDismiss }: SaveErrorBannerProps) { } else { try { await saveResume(); - } catch (err) { + } catch { // Error is handled by context } } diff --git a/components/resume/StyleCustomizer.tsx b/components/resume/StyleCustomizer.tsx index 8d230996..777acd08 100644 --- a/components/resume/StyleCustomizer.tsx +++ b/components/resume/StyleCustomizer.tsx @@ -314,7 +314,7 @@ export function StyleCustomizer() {
- {styling.margin_top}" + {styling.margin_top}”
- 0.5" - 1.5" + 0.5” + 1.5”
@@ -335,7 +335,7 @@ export function StyleCustomizer() {
- {styling.margin_bottom}" + {styling.margin_bottom}”
- 0.5" - 1.5" + 0.5” + 1.5”
@@ -356,7 +356,7 @@ export function StyleCustomizer() {
- {styling.margin_left}" + {styling.margin_left}”
- 0.5" - 1.5" + 0.5” + 1.5”
@@ -377,7 +377,7 @@ export function StyleCustomizer() {
- {styling.margin_right}" + {styling.margin_right}”
- 0.5" - 1.5" + 0.5” + 1.5”
diff --git a/components/resume/SuggestionPanel.tsx b/components/resume/SuggestionPanel.tsx index d815e140..0d9230b1 100644 --- a/components/resume/SuggestionPanel.tsx +++ b/components/resume/SuggestionPanel.tsx @@ -64,28 +64,6 @@ export function SuggestionPanel({ className }: SuggestionPanelProps) { return 'Needs Work'; }; - const getSeverityIcon = (severity: ResumeSuggestion['severity']) => { - switch (severity) { - case 'critical': - return ; - case 'warning': - return ; - case 'info': - return ; - } - }; - - const getSeverityBadgeVariant = (severity: ResumeSuggestion['severity']) => { - switch (severity) { - case 'critical': - return 'destructive'; - case 'warning': - return 'default'; - case 'info': - return 'secondary'; - } - }; - // Group suggestions by severity const criticalSuggestions = suggestions.filter((s) => s.severity === 'critical'); const warningSuggestions = suggestions.filter((s) => s.severity === 'warning'); diff --git a/components/resume/sections/AwardsSection.tsx b/components/resume/sections/AwardsSection.tsx index 93c0e4f4..331c7b81 100644 --- a/components/resume/sections/AwardsSection.tsx +++ b/components/resume/sections/AwardsSection.tsx @@ -87,7 +87,7 @@ export function AwardsSection({ content, onChange }: AwardsSectionProps) {

No awards yet

-

Click "Add Award" to get started

+

Click “Add Award” to get started

) : ( content.map((award, index) => ( diff --git a/components/resume/sections/CertificationsSection.tsx b/components/resume/sections/CertificationsSection.tsx index 0f289249..e208473f 100644 --- a/components/resume/sections/CertificationsSection.tsx +++ b/components/resume/sections/CertificationsSection.tsx @@ -88,7 +88,7 @@ export function CertificationsSection({ content, onChange }: CertificationsSecti

No certifications yet

-

Click "Add Certification" to get started

+

Click “Add Certification” to get started

) : ( content.map((certification, index) => ( diff --git a/components/resume/sections/EducationSection.tsx b/components/resume/sections/EducationSection.tsx index 5a20a990..15b464b8 100644 --- a/components/resume/sections/EducationSection.tsx +++ b/components/resume/sections/EducationSection.tsx @@ -147,7 +147,7 @@ export function EducationSection({ content, onChange }: EducationSectionProps) {

No education entries yet

-

Click "Add Education" to get started

+

Click “Add Education” to get started

) : ( content.map((education, index) => ( diff --git a/components/resume/sections/ExperienceSection.tsx b/components/resume/sections/ExperienceSection.tsx index 4fba6049..eee672cd 100644 --- a/components/resume/sections/ExperienceSection.tsx +++ b/components/resume/sections/ExperienceSection.tsx @@ -125,7 +125,7 @@ export function ExperienceSection({ content, onChange }: ExperienceSectionProps)

No work experience entries yet

-

Click "Add Experience" to get started

+

Click “Add Experience” to get started

) : ( content.map((experience, index) => ( diff --git a/components/resume/sections/ProjectsSection.tsx b/components/resume/sections/ProjectsSection.tsx index 613c7e83..e21babda 100644 --- a/components/resume/sections/ProjectsSection.tsx +++ b/components/resume/sections/ProjectsSection.tsx @@ -132,7 +132,7 @@ export function ProjectsSection({ content, onChange }: ProjectsSectionProps) {

No projects yet

-

Click "Add Project" to get started

+

Click “Add Project” to get started

) : ( content.map((project, index) => ( diff --git a/components/resume/sections/SkillsSection.tsx b/components/resume/sections/SkillsSection.tsx index 064681b8..52a05e58 100644 --- a/components/resume/sections/SkillsSection.tsx +++ b/components/resume/sections/SkillsSection.tsx @@ -2,7 +2,6 @@ import { Skill } from '@/types/resume'; import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; @@ -50,7 +49,7 @@ function SortableSkillCategory({ onRemove: (id: string) => void; }) { const [inputValue, setInputValue] = useState(''); - const [isExpanded, setIsExpanded] = useState(true); + const [isExpanded] = useState(true); const { attributes, @@ -222,7 +221,8 @@ export function SkillsSection({ content, onChange }: SkillsSectionProps) { }; const newSkills = [...skillsWithIds, newSkill]; setSkillsWithIds(newSkills); - onChange(newSkills.map(({ id, ...skill }) => skill)); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChange(newSkills.map(({ id: _id, ...skill }) => skill)); }; const handleUpdateCategory = (id: string, updates: Partial) => { @@ -230,13 +230,15 @@ export function SkillsSection({ content, onChange }: SkillsSectionProps) { skill.id === id ? { ...skill, ...updates } : skill ); setSkillsWithIds(newSkills); - onChange(newSkills.map(({ id, ...skill }) => skill)); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChange(newSkills.map(({ id: _id, ...skill }) => skill)); }; const handleRemoveCategory = (id: string) => { const newSkills = skillsWithIds.filter((skill) => skill.id !== id); setSkillsWithIds(newSkills); - onChange(newSkills.map(({ id, ...skill }) => skill)); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChange(newSkills.map(({ id: _id, ...skill }) => skill)); }; const handleDragEnd = (event: DragEndEvent) => { @@ -250,7 +252,8 @@ export function SkillsSection({ content, onChange }: SkillsSectionProps) { const newSkills = arrayMove(skillsWithIds, oldIndex, newIndex); setSkillsWithIds(newSkills); - onChange(newSkills.map(({ id, ...skill }) => skill)); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onChange(newSkills.map(({ id: _id, ...skill }) => skill)); } }; @@ -280,7 +283,7 @@ export function SkillsSection({ content, onChange }: SkillsSectionProps) {

No skill categories yet

- Click "Add Category" to organize your skills + Click “Add Category” to organize your skills

) : ( @@ -326,7 +329,7 @@ export function SkillsSection({ content, onChange }: SkillsSectionProps) { ) : null; })()} - +

💡 Tip: Drag categories to reorder them. Organize skills by type diff --git a/components/resume/templates/CreativeTemplate.tsx b/components/resume/templates/CreativeTemplate.tsx index 4dc140ff..96cd2917 100644 --- a/components/resume/templates/CreativeTemplate.tsx +++ b/components/resume/templates/CreativeTemplate.tsx @@ -126,7 +126,7 @@ export function CreativeTemplate({ resume }: CreativeTemplateProps) { if (!items || items.length === 0) return null; return (

- {items.map((item, index) => ( + {items.map((item) => (
- {items.map((item, index) => ( + {items.map((item) => (
- {items.map((item, index) => ( + {items.map((item) => (
- {items.map((item, index) => ( + {items.map((item) => (
- {items.map((item, index) => ( + {items.map((item) => (
{ - const requiredFields = ['name', 'description', 'technologies']; const missingFields: string[] = []; if (!entry.name || entry.name.trim().length === 0) missingFields.push('name'); @@ -583,11 +582,9 @@ export class ResumeScoringService { } let totalSkills = 0; - let totalCategories = 0; visibleSections.forEach((section) => { const skills = section.content as Skill[]; - totalCategories += skills.length; skills.forEach((skill) => { if (skill.items && skill.items.length > 0) { @@ -637,8 +634,6 @@ export class ResumeScoringService { suggestions: ResumeSuggestion[]; } { const maxScore = this.WEIGHTS.certifications; - const issues: string[] = []; - const suggestions: ResumeSuggestion[] = []; const visibleSections = sections.filter((s) => s.visible); @@ -696,8 +691,6 @@ export class ResumeScoringService { suggestions: ResumeSuggestion[]; } { const maxScore = this.WEIGHTS.awards; - const issues: string[] = []; - const suggestions: ResumeSuggestion[] = []; const visibleSections = sections.filter((s) => s.visible); diff --git a/lib/utils/debounce.ts b/lib/utils/debounce.ts index 83af9c77..55f30f28 100644 --- a/lib/utils/debounce.ts +++ b/lib/utils/debounce.ts @@ -4,6 +4,7 @@ * since the last time it was invoked */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function debounce any>( func: T, wait: number @@ -25,6 +26,7 @@ export function debounce any>( * Debounce with promise support * Returns a promise that resolves when the debounced function executes */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function debounceAsync Promise>( func: T, wait: number @@ -60,6 +62,7 @@ export function debounceAsync Promise>( * Throttle utility for performance optimization * Ensures function is called at most once per specified time period */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function throttle any>( func: T, limit: number