From f73e03839413ef01a45ef111459583a545fb19ad Mon Sep 17 00:00:00 2001 From: Ram-blip Date: Sat, 28 Feb 2026 19:20:01 +0530 Subject: [PATCH] Fix: prevent duplicate PR entries in PR Grading Dashboard (frontend) --- .../PRGradingDashboard/AddPRModal.jsx | 18 ++++++++++++- .../PRGradingDashboard/AddPRModal.module.css | 27 ++++++++++--------- .../PRGradingDashboard/GradingTable.jsx | 1 + 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/components/PRGradingDashboard/AddPRModal.jsx b/src/components/PRGradingDashboard/AddPRModal.jsx index 646f2279d5..e639e75774 100644 --- a/src/components/PRGradingDashboard/AddPRModal.jsx +++ b/src/components/PRGradingDashboard/AddPRModal.jsx @@ -5,12 +5,23 @@ import styles from './AddPRModal.module.css'; const PR_NUMBER_REGEX = /^\d+(?:\s*\+\s*\d+)*$/; const GRADE_OPTIONS = ['Unsatisfactory', 'Okay', 'Exceptional', 'No Correct Image']; -function AddPRModal({ reviewer, onAdd, onCancel }) { +const normalizePrNumbers = prNumbers => (prNumbers || '').replace(/\s+/g, ''); + +function AddPRModal({ reviewer, existingPRs, onAdd, onCancel }) { const [prNumber, setPrNumber] = useState(''); const [prNumberError, setPrNumberError] = useState(''); const [selectedGrade, setSelectedGrade] = useState(''); const [step, setStep] = useState(1); // 1: PR Number input, 2: Grade selection + const safeExistingPRs = Array.isArray(existingPRs) ? existingPRs : []; + + const isDuplicatePR = value => { + const normalizedNew = normalizePrNumbers(value); + return safeExistingPRs.some( + pr => pr?.prNumbers && normalizePrNumbers(pr.prNumbers) === normalizedNew, + ); + }; + const validatePRNumber = value => { const trimmedValue = value.trim(); if (!trimmedValue) { @@ -24,6 +35,11 @@ function AddPRModal({ reviewer, onAdd, onCancel }) { setPrNumberError('Invalid format. Use format like "1234" or "1234 + 5678"'); return false; } + // Check for duplicate PR number for this reviewer + if (isDuplicatePR(trimmedValue)) { + setPrNumberError(`PR number "${trimmedValue}" already exists for this reviewer`); + return false; + } setPrNumberError(''); return true; }; diff --git a/src/components/PRGradingDashboard/AddPRModal.module.css b/src/components/PRGradingDashboard/AddPRModal.module.css index e461fe8f11..67be78c943 100644 --- a/src/components/PRGradingDashboard/AddPRModal.module.css +++ b/src/components/PRGradingDashboard/AddPRModal.module.css @@ -3,7 +3,7 @@ border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1rem; - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -2px rgb(0 0 0 / 5%); } .modalHeader { @@ -36,7 +36,7 @@ .closeButton:focus { outline: none; - box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.5); + box-shadow: 0 0 0 2px rgb(107 114 128 / 50%); } .closeIcon { @@ -68,7 +68,7 @@ .input:focus { outline: none; border-color: #2563eb; - box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.5); + box-shadow: 0 0 0 2px rgb(37 99 235 / 50%); } .inputError { @@ -77,7 +77,7 @@ .inputError:focus { border-color: #ef4444; - box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.5); + box-shadow: 0 0 0 2px rgb(239 68 68 / 50%); } .errorMessage { @@ -114,7 +114,7 @@ border: 1px solid #d1d5db; cursor: pointer; transition: all 0.2s; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%); } .buttonCancel { @@ -134,12 +134,12 @@ .buttonPrimary:hover { background-color: #1d4ed8; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 6px -1px rgb(0 0 0 / 10%); } .buttonPrimary:focus { outline: none; - box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.5), 0 0 0 4px rgba(37, 99, 235, 0.1); + box-shadow: 0 0 0 2px rgb(37 99 235 / 50%), 0 0 0 4px rgb(37 99 235 / 10%); } .buttonPrimary:disabled { @@ -194,7 +194,7 @@ background-color: #dbeafe; border-color: #2563eb; color: #1e40af; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%); } .backButton { @@ -207,7 +207,7 @@ border-radius: 0.375rem; cursor: pointer; transition: all 0.2s; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%); } .backButton:hover { @@ -290,12 +290,16 @@ :global(.dark-mode) .input:focus { border-color: #3b82f6; - box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5); + box-shadow: 0 0 0 2px rgb(59 130 246 / 50%); +} + +:global(.dark-mode) .inputError { + border-color: #ef4444; } :global(.dark-mode) .inputError:focus { border-color: #ef4444; - box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.5); + box-shadow: 0 0 0 2px rgb(239 68 68 / 50%); } :global(.dark-mode) .errorMessage { @@ -310,4 +314,3 @@ background-color: #60a5fa; opacity: 0.7; } - diff --git a/src/components/PRGradingDashboard/GradingTable.jsx b/src/components/PRGradingDashboard/GradingTable.jsx index cbd0edea19..7eca33581e 100644 --- a/src/components/PRGradingDashboard/GradingTable.jsx +++ b/src/components/PRGradingDashboard/GradingTable.jsx @@ -34,6 +34,7 @@ function GradingTable({ onAddPRClick(null)} />