From 1053252208c826dd6eef2fa88459a86679a27054 Mon Sep 17 00:00:00 2001 From: Atharv Narkar Date: Sun, 26 Jul 2026 15:57:06 +0530 Subject: [PATCH 1/4] Feat: Add JD matching logic for required skills --- backend/analyzer/services.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/analyzer/services.py b/backend/analyzer/services.py index 4a998b3a..970846ef 100644 --- a/backend/analyzer/services.py +++ b/backend/analyzer/services.py @@ -64,7 +64,10 @@ def analyze_resume(file_path, target_role, file_name="resume.pdf",user_id=None,j matched = [] missing = [] - required = ROLE_SKILLS.get(target_role, []) + if job_description and job_description.strip(): + required = extract_skills(job_description) + else: + required = ROLE_SKILLS.get(target_role, []) for skill in required: if skill in detected: From 8f8ce7a14a07c084b8ec3f79bd19b92ab1fa2fb4 Mon Sep 17 00:00:00 2001 From: Atharv Narkar Date: Sun, 26 Jul 2026 16:17:36 +0530 Subject: [PATCH 2/4] Add quantify your achievements nudge (#376) --- backend/analyzer/services.py | 4 + backend/resume_analyzer/quantify_checker.py | 107 ++++++++++++++++++ .../resume_analyzer/test_quantify_checker.py | 52 +++++++++ frontend/package-lock.json | 13 +-- frontend/src/App.tsx | 17 ++- frontend/src/QuantifyNudges.css | 19 ++++ frontend/src/QuantifyNudges.tsx | 73 ++++++++++++ frontend/src/components/HowItWorks.tsx | 2 +- frontend/src/components/Navbar.tsx | 2 +- 9 files changed, 270 insertions(+), 19 deletions(-) create mode 100644 backend/resume_analyzer/quantify_checker.py create mode 100644 backend/resume_analyzer/test_quantify_checker.py create mode 100644 frontend/src/QuantifyNudges.css create mode 100644 frontend/src/QuantifyNudges.tsx diff --git a/backend/analyzer/services.py b/backend/analyzer/services.py index 970846ef..6aeff569 100644 --- a/backend/analyzer/services.py +++ b/backend/analyzer/services.py @@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from .models import ResumeAnalysis from .skill_matcher import extract_skills +from resume_analyzer.quantify_checker import flag_unquantified_bullets User = get_user_model() @@ -138,6 +139,8 @@ def analyze_resume(file_path, target_role, file_name="resume.pdf",user_id=None,j "suggestions": role_suggestions, } + quantify_nudges = flag_unquantified_bullets(raw_text.split('\n')) + return { "id": analysis_id, "score": score, @@ -145,6 +148,7 @@ def analyze_resume(file_path, target_role, file_name="resume.pdf",user_id=None,j "readability_label": readability_label, "skills_found": detected, "suggestions": suggestions, + "quantify_nudges": quantify_nudges, "matched_skills": matched, "missing_skills": missing, "target_role": target_role, diff --git a/backend/resume_analyzer/quantify_checker.py b/backend/resume_analyzer/quantify_checker.py new file mode 100644 index 00000000..0ab08748 --- /dev/null +++ b/backend/resume_analyzer/quantify_checker.py @@ -0,0 +1,107 @@ +import re +from typing import TypedDict + +NUMERIC_PATTERN = re.compile( + r""" + \d+ + | \d+[%xX] + | \$[\d,]+ + | [\d,]+\s*(k|m|b)\b + | \b(zero|one|two|three|four|five|six|seven|eight|nine|ten| + hundred|thousand|million|billion|dozen|several|multiple| + first|second|third|half|double|triple|quadruple)\b + | \b\d+\+ + | \b\d+[-]\d+ + """, + re.VERBOSE | re.IGNORECASE, +) + +EXEMPT_PATTERNS = [ + re.compile(r, re.IGNORECASE) + for r in [ + r"^(objective|summary|profile|about|overview)\s*[:\-]", + r"^(skills?|technologies|tools?|languages?|frameworks?)\s*[:\-]", + r"@", + r"\b(university|college|school|bachelor|master|phd|b\.?s\.?|m\.?s\.?|gpa)\b", + r"\b(january|february|march|april|may|june|july|august|september|october|november|december)\b", + r"^\s*references\b", + ] +] + +ACCOMPLISHMENT_VERBS = re.compile( + r"\b(achiev|improv|increas|decreas|reduc|optimiz|accelerat|deliver|" + r"develop|launch|lead|led|manag|built|build|creat|design|implement|automat|" + r"deploy|drove|generat|saved|cut|grew|scal|streamlin|transform|" + r"boost|enhanc|resolv|migrat|refactor|consolidat|negoti|complet|" + r"execut|oversaw|coordin|establish|spearhead|champion)\w*", + re.IGNORECASE, +) + +METRIC_PROMPTS = { + "improv": "e.g. 'improved load time by 40%'", + "increas": "e.g. 'increased revenue by $50k'", + "decreas": "e.g. 'decreased bug count by 60%'", + "reduc": "e.g. 'reduced deployment time from 2h to 15 min'", + "optimiz": "e.g. 'optimized query time from 3s to 200ms'", + "lead": "e.g. 'led a team of 5 engineers'", + "led": "e.g. 'led a team of 5 engineers'", + "manag": "e.g. 'managed 3 direct reports'", + "develop": "e.g. 'developed 3 REST APIs serving 10k daily users'", + "launch": "e.g. 'launched feature used by 5,000+ customers'", + "built": "e.g. 'built a pipeline processing 1M records/day'", + "automat": "e.g. 'automated 8 workflows, saving 20 hrs/week'", + "deploy": "e.g. 'deployed 12 microservices to AWS'", + "drove": "e.g. 'drove a 25% increase in conversion rate'", + "saved": "e.g. 'saved $30k/year in infrastructure costs'", + "grew": "e.g. 'grew user base from 500 to 5,000 in 6 months'", + "scal": "e.g. 'scaled system to handle 100k concurrent requests'", + "resolv": "e.g. 'resolved 95% of P1 incidents within SLA'", + "default": "Try adding a number, percentage, dollar amount, or time saved.", +} + +class QuantifyNudge(TypedDict): + line_index: int + original_text: str + suggestion: str + hint: str + +def is_exempt(line: str) -> bool: + stripped = line.strip().lstrip("-*▪▸ \t") + if len(stripped) < 15: + return True + for pat in EXEMPT_PATTERNS: + if pat.search(stripped): + return True + return False + +def _pick_hint(line: str) -> str: + for key, hint in METRIC_PROMPTS.items(): + if key == "default": + continue + if re.search(r"\b" + key, line, re.IGNORECASE): + return hint + return METRIC_PROMPTS["default"] + +def flag_unquantified_bullets(bullets: list[str]) -> list[QuantifyNudge]: + nudges: list[QuantifyNudge] = [] + for idx, raw in enumerate(bullets): + line = raw.strip() + if not line: + continue + if is_exempt(line): + continue + if not ACCOMPLISHMENT_VERBS.search(line): + continue + if NUMERIC_PATTERN.search(line): + continue + nudges.append(QuantifyNudge( + line_index=idx, + original_text=raw, + suggestion=( + "This bullet describes an accomplishment but lacks a " + "quantifiable detail. Adding a number, percentage, or " + "metric will make it significantly stronger." + ), + hint=_pick_hint(line), + )) + return nudges diff --git a/backend/resume_analyzer/test_quantify_checker.py b/backend/resume_analyzer/test_quantify_checker.py new file mode 100644 index 00000000..c19c02ef --- /dev/null +++ b/backend/resume_analyzer/test_quantify_checker.py @@ -0,0 +1,52 @@ +import pytest +from quantify_checker import flag_unquantified_bullets + +SHOULD_FLAG = [ + "Improved application performance significantly", + "Reduced server downtime and improved reliability", + "Led cross-functional teams to deliver product features", + "Developed RESTful APIs for the internal platform", + "Automated deployment pipeline using GitHub Actions", + "Managed the onboarding process for new developers", + "Optimized database queries for better response times", + "Built a real-time notification system for the app", +] + +SHOULD_NOT_FLAG = [ + "Improved application performance by 40%", + "Led a team of 8 engineers across 3 time zones", + "Summary: Results-driven software engineer with 5 years", + "Skills: Python, Django, React, PostgreSQL", + "Bachelor of Engineering, University of Mumbai, 2023", + "atharv@example.com | Mumbai, India", + "", +] + +class TestFlagUnquantifiedBullets: + def test_flags_accomplishment_without_number(self): + for bullet in SHOULD_FLAG: + nudges = flag_unquantified_bullets([bullet]) + assert len(nudges) == 1, f"Expected flag for: '{bullet}', got {len(nudges)}" + + def test_does_not_flag_already_quantified(self): + for bullet in SHOULD_NOT_FLAG: + nudges = flag_unquantified_bullets([bullet]) + assert len(nudges) == 0, f"Should not flag: '{bullet}'" + + def test_correct_indices(self): + bullets = [ + "Summary: Experienced engineer", + "Improved performance significantly", + "Increased revenue by 25%", + "Automated CI/CD pipeline", + ] + nudges = flag_unquantified_bullets(bullets) + assert len(nudges) == 2 + assert nudges[0]["line_index"] == 1 + assert nudges[1]["line_index"] == 3 + + def test_empty_list(self): + assert flag_unquantified_bullets([]) == [] + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2fec2a68..5244e1c5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,8 +8,8 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "@sentry/react": "^8.55.2", "@babel/parser": "^8.0.4", + "@sentry/react": "^8.38.0", "axios": "^1.13.6", "bootstrap": "^5.3.8", "d3-cloud": "^1.2.9", @@ -145,7 +145,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1496,7 +1495,6 @@ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -1508,7 +1506,6 @@ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -4590,7 +4587,6 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", - "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -5195,7 +5191,6 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -6513,7 +6508,6 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -6527,7 +6521,6 @@ "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "js-tokens": "^9.0.1" }, @@ -6630,7 +6623,6 @@ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "18 || 20 || >=22" } @@ -6641,7 +6633,6 @@ "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, @@ -7194,7 +7185,6 @@ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", @@ -7518,7 +7508,6 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index aa202e6d..b036061c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -46,6 +46,7 @@ import { FilePreview } from './components/FilePreview/FilePreview' import { ShareResult } from './components/ShareResult' import { SharedResultView } from './SharedResultView' import CookieConsentBanner from './components/CookieConsentBanner' +import QuantifyNudges, { type QuantifyNudge } from './QuantifyNudges' type Theme = 'light' | 'dark' interface UndoState { @@ -146,21 +147,24 @@ const SuggestionCard: React.FC = ({ text, index, backendUrl } } + const isQuantify = text.startsWith('QUANTIFY:') + const displayText = isQuantify ? text.replace('QUANTIFY:', '').trim() : text + return ( -
+
- 💡 + {isQuantify ? '📊' : '💡'} - Recommendation #{index + 1} + {isQuantify ? 'Quantify Achievement' : `Recommendation #${index + 1}`}

= ({ text, index, backendUrl lineHeight: '1.6', }} > - {text} + {displayText}

@@ -277,6 +281,7 @@ function App() { const [score, setScore] = useState(null) const [skills, setSkills] = useState([]) const [suggestions, setSuggestions] = useState([]) + const [quantifyNudges, setQuantifyNudges] = useState([]) const [readabilityLabel, setReadabilityLabel] = useState(null) const [undoState, setUndoState] = useState(null) @@ -607,6 +612,7 @@ function App() { setScore(res.data.score) setSkills(res.data.skills_found || []) setSuggestions(res.data.suggestions || []) + setQuantifyNudges(res.data.quantify_nudges || []) setMatchedSkills(res.data.matched_skills || []) setMissingSkills(res.data.missing_skills || []) setResumeText(res.data.resume_text || '') @@ -1655,6 +1661,7 @@ function App() {
)} +
diff --git a/frontend/src/QuantifyNudges.css b/frontend/src/QuantifyNudges.css new file mode 100644 index 00000000..851d8315 --- /dev/null +++ b/frontend/src/QuantifyNudges.css @@ -0,0 +1,19 @@ +.quantify-nudges { margin: 1.5rem 0; padding: 1.25rem 1.5rem; background: #1e2a3a; border: 1px solid #2d4060; border-left: 4px solid #f59e0b; border-radius: 10px; } +.quantify-nudges--empty { display: flex; align-items: center; gap: 0.6rem; color: #4ade80; font-size: 0.95rem; } +.quantify-nudges__header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; } +.quantify-nudges__title { font-size: 1.05rem; font-weight: 700; color: #f59e0b; margin: 0; } +.quantify-nudges__count { font-size: 0.78rem; font-weight: 600; background: #f59e0b22; color: #f59e0b; border: 1px solid #f59e0b55; border-radius: 999px; padding: 2px 10px; } +.quantify-nudges__desc { font-size: 0.85rem; color: #94a3b8; margin: 0 0 1rem 0; line-height: 1.5; } +.nudge-card { background: #16202e; border: 1px solid #2d4060; border-radius: 8px; margin-bottom: 0.75rem; overflow: hidden; } +.nudge-card:hover { border-color: #f59e0b66; } +.nudge-header { display: flex; justify-content: space-between; align-items: center; padding: 0.65rem 1rem; cursor: pointer; background: #1a2840; } +.nudge-badge { display: flex; align-items: center; gap: 0.4rem; } +.nudge-label { font-size: 0.82rem; font-weight: 600; color: #cbd5e1; } +.nudge-original { padding: 0.6rem 1rem; border-top: 1px solid #2d4060; } +.nudge-quote { margin: 0.3rem 0 0 0; padding-left: 0.8rem; border-left: 3px solid #f59e0b66; font-size: 0.88rem; color: #e2e8f0; font-style: italic; } +.nudge-details { padding: 0.6rem 1rem 0.9rem; border-top: 1px dashed #2d4060; background: #111b27; } +.nudge-suggestion { font-size: 0.85rem; color: #94a3b8; margin: 0 0 0.6rem 0; } +.nudge-hint-box { background: #f59e0b11; border: 1px solid #f59e0b33; border-radius: 6px; padding: 0.5rem 0.75rem; } +.nudge-hint-text { font-size: 0.83rem; color: #fbbf24; margin: 0.25rem 0 0 0; font-style: italic; } +.nudge-tag { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; color: #64748b; background: #1e2a3a; border: 1px solid #2d4060; border-radius: 4px; padding: 1px 6px; display: inline-block; } +.nudge-tag--hint { color: #f59e0b; background: #f59e0b11; border-color: #f59e0b44; } diff --git a/frontend/src/QuantifyNudges.tsx b/frontend/src/QuantifyNudges.tsx new file mode 100644 index 00000000..bd80468b --- /dev/null +++ b/frontend/src/QuantifyNudges.tsx @@ -0,0 +1,73 @@ +import React, { useState } from "react"; +import "./QuantifyNudges.css"; + +export interface QuantifyNudge { + line_index: number; + original_text: string; + suggestion: string; + hint: string; +} + +interface Props { + nudges: QuantifyNudge[]; +} + +const NudgeCard: React.FC<{ nudge: QuantifyNudge; index: number }> = ({ nudge, index }) => { + const [expanded, setExpanded] = useState(false); + return ( +
+
setExpanded(p => !p)} + role="button" tabIndex={0} onKeyDown={(e) => e.key === "Enter" && setExpanded(p => !p)}> +
+ 📊 + Bullet #{index + 1} needs a metric +
+ {expanded ? "▲" : "▼"} +
+
+ Original +
{nudge.original_text}
+
+ {expanded && ( +
+

💡 {nudge.suggestion}

+
+ Example fix +

{nudge.hint}

+
+
+ )} +
+ ); +}; + +const QuantifyNudges: React.FC = ({ nudges }) => { + if (!nudges || nudges.length === 0) { + return ( +
+ +

All achievement bullets are already quantified. Great work!

+
+ ); + } + return ( +
+
+

📊 Quantify Your Achievements

+ + {nudges.length} bullet{nudges.length !== 1 ? "s" : ""} need a metric + +
+

+ Numbers make bullets stronger. Each item below is missing a quantifiable detail. +

+
+ {nudges.map((nudge, i) => ( + + ))} +
+
+ ); +}; + +export default QuantifyNudges; diff --git a/frontend/src/components/HowItWorks.tsx b/frontend/src/components/HowItWorks.tsx index 75d15ff8..fde27369 100644 --- a/frontend/src/components/HowItWorks.tsx +++ b/frontend/src/components/HowItWorks.tsx @@ -4,7 +4,7 @@ import { UploadCloud, Cpu, CheckCircle } from 'lucide-react' const steps = [ { - icon: , + icon: , color: '#eeeff5', title: '1. Upload', description: 'Upload your resume in PDF format.', diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index aac214c3..a4c72d91 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -77,7 +77,7 @@ export const Navbar: React.FC = ({ }} > Home - + setMobileOpen(false)}> Analyze Resume From 26957cc3c422ecd11e6e4773ebfabb39aff9ddb7 Mon Sep 17 00:00:00 2001 From: Atharv Narkar Date: Tue, 28 Jul 2026 12:46:35 +0530 Subject: [PATCH 3/4] fix(frontend): add missing recharts dependency and fix typescript type --- frontend/package-lock.json | 397 +++++++++++++++++- frontend/package.json | 5 +- frontend/src/components/ScoreHistoryChart.tsx | 2 +- 3 files changed, 398 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a5b782db..0678340f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,7 +18,8 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "react-icons": "^5.7.0", - "react-router-dom": "^7.11.0" + "react-router-dom": "^7.11.0", + "recharts": "^3.10.1" }, "devDependencies": { "@eslint/js": "^9.39.4", @@ -1603,6 +1604,32 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", @@ -2309,6 +2336,18 @@ "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -2707,6 +2746,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, "node_modules/@types/d3-cloud": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.9.tgz", @@ -2717,6 +2762,63 @@ "@types/d3": "^3" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -2765,7 +2867,7 @@ "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2788,6 +2890,12 @@ "license": "MIT", "optional": true }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", @@ -3622,6 +3730,15 @@ "node": ">= 16" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3743,9 +3860,21 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-cloud": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.9.tgz", @@ -3755,12 +3884,121 @@ "d3-dispatch": "^1.0.3" } }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-dispatch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", "license": "BSD-3-Clause" }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -3799,6 +4037,12 @@ "dev": true, "license": "MIT" }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -3977,6 +4221,17 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", + "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types" + ] + }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -4333,6 +4588,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -4857,6 +5118,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "11.1.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz", + "integrity": "sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4894,6 +5165,15 @@ "node": ">=8" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/iobuffer": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", @@ -6108,6 +6388,29 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-redux": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", + "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "7.18.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", @@ -6146,6 +6449,36 @@ "react-dom": ">=18" } }, + "node_modules/recharts": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -6160,6 +6493,21 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -6167,6 +6515,12 @@ "license": "MIT", "optional": true }, + "node_modules/reselect": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6666,6 +7020,12 @@ "utrie": "^1.0.2" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -6893,6 +7253,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", @@ -6903,6 +7272,28 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/vite": { "version": "8.1.5", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 59080552..331bb875 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,8 +13,8 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { - "@sentry/react": "^8.38.0", "@babel/parser": "^8.0.4", + "@sentry/react": "^8.38.0", "axios": "^1.13.6", "bootstrap": "^5.3.8", "d3-cloud": "^1.2.9", @@ -23,7 +23,8 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "react-icons": "^5.7.0", - "react-router-dom": "^7.11.0" + "react-router-dom": "^7.11.0", + "recharts": "^3.10.1" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/frontend/src/components/ScoreHistoryChart.tsx b/frontend/src/components/ScoreHistoryChart.tsx index 1176609d..93922e55 100644 --- a/frontend/src/components/ScoreHistoryChart.tsx +++ b/frontend/src/components/ScoreHistoryChart.tsx @@ -82,7 +82,7 @@ export const ScoreHistoryChart: React.FC = ({ entries }) - `${val}`} /> + `${val}`} /> } /> Date: Tue, 28 Jul 2026 13:09:38 +0530 Subject: [PATCH 4/4] Remove score-history chart changes (moved to separate branch) --- frontend/package-lock.json | 397 +----------------- frontend/package.json | 5 +- frontend/src/HistorySidebar.tsx | 2 - frontend/src/components/ScoreHistoryChart.tsx | 99 ----- 4 files changed, 5 insertions(+), 498 deletions(-) delete mode 100644 frontend/src/components/ScoreHistoryChart.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0678340f..a5b782db 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,8 +18,7 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "react-icons": "^5.7.0", - "react-router-dom": "^7.11.0", - "recharts": "^3.10.1" + "react-router-dom": "^7.11.0" }, "devDependencies": { "@eslint/js": "^9.39.4", @@ -1604,32 +1603,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@reduxjs/toolkit": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", - "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@standard-schema/utils": "^0.3.0", - "immer": "^11.0.0", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", @@ -2336,18 +2309,6 @@ "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -2746,12 +2707,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, "node_modules/@types/d3-cloud": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/@types/d3-cloud/-/d3-cloud-1.2.9.tgz", @@ -2762,63 +2717,6 @@ "@types/d3": "^3" } }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -2867,7 +2765,7 @@ "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2890,12 +2788,6 @@ "license": "MIT", "optional": true }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", - "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.65.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", @@ -3730,15 +3622,6 @@ "node": ">= 16" } }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3860,21 +3743,9 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/d3-cloud": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.9.tgz", @@ -3884,121 +3755,12 @@ "d3-dispatch": "^1.0.3" } }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/d3-dispatch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==", "license": "BSD-3-Clause" }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -4037,12 +3799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -4221,17 +3977,6 @@ "node": ">= 0.4" } }, - "node_modules/es-toolkit": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", - "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks", - "tests/types" - ] - }, "node_modules/esbuild": { "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", @@ -4588,12 +4333,6 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -5118,16 +4857,6 @@ "node": ">= 4" } }, - "node_modules/immer": { - "version": "11.1.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz", - "integrity": "sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -5165,15 +4894,6 @@ "node": ">=8" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/iobuffer": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", @@ -6388,29 +6108,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "node_modules/react-redux": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", - "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", - "license": "MIT", - "dependencies": { - "@types/use-sync-external-store": "^0.0.6", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "@types/react": "^18.2.25 || ^19", - "react": "^18.0 || ^19", - "redux": "^5.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, "node_modules/react-router": { "version": "7.18.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", @@ -6449,36 +6146,6 @@ "react-dom": ">=18" } }, - "node_modules/recharts": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", - "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", - "license": "MIT", - "workspaces": [ - "www" - ], - "dependencies": { - "@reduxjs/toolkit": "^1.9.0 || 2.x.x", - "clsx": "^2.1.1", - "decimal.js-light": "^2.5.1", - "es-toolkit": "^1.39.3", - "eventemitter3": "^5.0.1", - "immer": "^11.1.8", - "react-redux": "8.x.x || 9.x.x", - "reselect": "5.2.0", - "tiny-invariant": "^1.3.3", - "use-sync-external-store": "^1.2.2", - "victory-vendor": "^37.0.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -6493,21 +6160,6 @@ "node": ">=8" } }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", - "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" - } - }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -6515,12 +6167,6 @@ "license": "MIT", "optional": true }, - "node_modules/reselect": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", - "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", - "license": "MIT" - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -7020,12 +6666,6 @@ "utrie": "^1.0.2" } }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -7253,15 +6893,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", @@ -7272,28 +6903,6 @@ "base64-arraybuffer": "^1.0.2" } }, - "node_modules/victory-vendor": { - "version": "37.3.6", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", - "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, "node_modules/vite": { "version": "8.1.5", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 331bb875..59080552 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,8 +13,8 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { - "@babel/parser": "^8.0.4", "@sentry/react": "^8.38.0", + "@babel/parser": "^8.0.4", "axios": "^1.13.6", "bootstrap": "^5.3.8", "d3-cloud": "^1.2.9", @@ -23,8 +23,7 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "react-icons": "^5.7.0", - "react-router-dom": "^7.11.0", - "recharts": "^3.10.1" + "react-router-dom": "^7.11.0" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/frontend/src/HistorySidebar.tsx b/frontend/src/HistorySidebar.tsx index bfbd9836..2ca495c5 100644 --- a/frontend/src/HistorySidebar.tsx +++ b/frontend/src/HistorySidebar.tsx @@ -1,7 +1,6 @@ import React, { useState, useEffect } from 'react' import { X, ClipboardList, BookOpen, Trash2, GitCompare } from 'lucide-react' import type { AnalysisEntry } from './hooks/useAnalysisHistory' -import { ScoreHistoryChart } from './components/ScoreHistoryChart' const PAGE_SIZE = 10 interface HistorySidebarProps { @@ -147,7 +146,6 @@ export const HistorySidebar: React.FC = ({

No notifications or past analyses yet.

) : ( <> -
    {entries.slice(0, visibleCount).map((entry) => { const isNew = entry.timestamp > lastViewedTimestamp diff --git a/frontend/src/components/ScoreHistoryChart.tsx b/frontend/src/components/ScoreHistoryChart.tsx deleted file mode 100644 index 93922e55..00000000 --- a/frontend/src/components/ScoreHistoryChart.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useMemo } from 'react' -import { - ResponsiveContainer, - LineChart, - Line, - XAxis, - YAxis, - CartesianGrid, - Tooltip, -} from 'recharts' -import type { AnalysisEntry } from '../hooks/useAnalysisHistory' - -interface ScoreHistoryChartProps { - entries: AnalysisEntry[] -} - -interface CustomTooltipProps { - active?: boolean - payload?: any[] -} - -const CustomTooltip = ({ active, payload }: CustomTooltipProps) => { - if (active && payload && payload.length) { - const entry = payload[0].payload - return ( -
    -
    {entry.dateLabel}
    -
    - Score: {entry.score}% -
    -
    - Role: {entry.targetRole || 'N/A'} -
    -
    - ) - } - return null -} - -export const ScoreHistoryChart: React.FC = ({ entries }) => { - const data = useMemo(() => { - // Sort entries by timestamp (oldest first) - const sorted = [...entries].sort((a, b) => a.timestamp - b.timestamp) - - // Format timestamp for display - return sorted.map((entry) => { - const date = new Date(entry.timestamp) - return { - ...entry, - dateLabel: date.toLocaleDateString(undefined, { - month: 'short', - day: 'numeric', - }), - fullDate: date.toLocaleString(), - } - }) - }, [entries]) - - if (entries.length < 2) { - return null // Graceful empty/single-entry state - } - - return ( -
    -

    - ATS Score History -

    - - - - - `${val}`} /> - } /> - - - -
    - ) -}