diff --git a/src/actions/prAnalytics/weeklyGradingActions.js b/src/actions/prAnalytics/weeklyGradingActions.js index 067a6fc0c5..ed3b3cbb65 100644 --- a/src/actions/prAnalytics/weeklyGradingActions.js +++ b/src/actions/prAnalytics/weeklyGradingActions.js @@ -1,15 +1,12 @@ import axios from 'axios'; import { ENDPOINTS } from '../../utils/URL'; -export const getWeeklyGrading = (teamCode, date, token) => async dispatch => { +export const getWeeklyGrading = (teamCode, date) => async dispatch => { try { const params = { team: teamCode }; if (date) params.date = date; - const response = await axios.get(ENDPOINTS.WEEKLY_GRADING, { - params, - headers: { Authorization: `${token}` }, - }); + const response = await axios.get(ENDPOINTS.WEEKLY_GRADING, { params }); return response.data; } catch (error) { dispatch({ type: 'SET_ERROR', error: error.message }); @@ -17,13 +14,13 @@ export const getWeeklyGrading = (teamCode, date, token) => async dispatch => { } }; -export const saveWeeklyGrading = (teamCode, date, gradings, token) => async dispatch => { +export const saveWeeklyGrading = (teamCode, date, gradings) => async dispatch => { try { - const response = await axios.post( - ENDPOINTS.WEEKLY_GRADING_SAVE, - { teamCode, date, gradings }, - { headers: { Authorization: `${token}` } }, - ); + const response = await axios.post(ENDPOINTS.WEEKLY_GRADING_SAVE, { + teamCode, + date, + gradings, + }); return response.data; } catch (error) { dispatch({ type: 'SET_ERROR', error: error.message }); diff --git a/src/components/PRGradingScreen/PRGradingScreen.jsx b/src/components/PRGradingScreen/PRGradingScreen.jsx index 4c96cf1d2f..1cb3c29cdb 100644 --- a/src/components/PRGradingScreen/PRGradingScreen.jsx +++ b/src/components/PRGradingScreen/PRGradingScreen.jsx @@ -1,12 +1,20 @@ +import axios from 'axios'; import PropTypes from 'prop-types'; -import { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Button, Card, Col, Container, Row } from 'react-bootstrap'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { v4 as uuidv4 } from 'uuid'; +import { + getWeeklyGrading, + saveWeeklyGrading, +} from '../../actions/prAnalytics/weeklyGradingActions'; import styles from './PRGradingScreen.module.css'; +import PromotionConfirmationBox from './PromotionConfirmationBox'; const PRGradingScreen = ({ teamData, reviewers }) => { const darkMode = useSelector(state => state.theme.darkMode); + const token = useSelector(state => state.auth.token); + const dispatch = useDispatch(); const [reviewerData, setReviewerData] = useState(reviewers || []); const [activeInput, setActiveInput] = useState(null); @@ -14,12 +22,45 @@ const PRGradingScreen = ({ teamData, reviewers }) => { const [inputError, setInputError] = useState(''); const [showGradingModal, setShowGradingModal] = useState(null); const [isFinalized, setIsFinalized] = useState(false); - - // Search state + const [isSaving, setIsSaving] = useState(false); + const [saveMessage, setSaveMessage] = useState(''); + const [isLoading, setIsLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [roleFilter, setRoleFilter] = useState(''); - - /* ---------------- SEARCH FILTER ---------------- */ + const [promotionCandidate, setPromotionCandidate] = useState(null); + const [confirmedPromotions, setConfirmedPromotions] = useState([]); + const [selectedForPromotion, setSelectedForPromotion] = useState([]); + const [showBatchConfirm, setShowBatchConfirm] = useState(false); + + useEffect(() => { + if (!teamData?.teamCode) return; + + const fetchData = async () => { + setIsLoading(true); + try { + const data = await dispatch( + getWeeklyGrading(teamData.teamCode, teamData.dateRange?.start, token), + ); + if (data && data.length > 0) { + const mapped = data.map(entry => ({ + id: uuidv4(), + reviewer: entry.reviewer, + prsNeeded: entry.prsNeeded, + prsReviewed: entry.prsReviewed, + gradedPrs: (entry.gradedPrs || []).map(pr => ({ ...pr, id: uuidv4() })), + })); + setReviewerData(mapped); + } + } catch { + // fallback to prop data + } finally { + setIsLoading(false); + } + }; + + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [teamData?.teamCode]); const availableRoles = useMemo(() => { const roles = reviewerData.map(r => r.role).filter(Boolean); @@ -69,7 +110,21 @@ const PRGradingScreen = ({ teamData, reviewers }) => { setInputError(validation.error); return; } - const newPREntry = { id: uuidv4(), prNumbers: inputValue.trim(), grade: 'Okay' }; + + const trimmed = inputValue.trim(); + const normalize = str => str.replace(/\s/g, '').toLowerCase(); + const reviewer = reviewerData.find(r => r.id === reviewerId); + const isDuplicate = reviewer?.gradedPrs.some( + pr => normalize(pr.prNumbers) === normalize(trimmed), + ); + + if (isDuplicate) { + setInputError('This PR already exists for this reviewer.'); + return; + } + + const newPREntry = { id: uuidv4(), prNumbers: trimmed, grade: 'Okay' }; + setReviewerData(prev => prev.map(r => r.id === reviewerId @@ -77,13 +132,13 @@ const PRGradingScreen = ({ teamData, reviewers }) => { : r, ), ); + setActiveInput(null); setInputValue(''); setInputError(''); }; const handleCancel = () => { - if (isFinalized) return; setActiveInput(null); setInputValue(''); setInputError(''); @@ -111,54 +166,185 @@ const PRGradingScreen = ({ teamData, reviewers }) => { }; const handleCloseGradingModal = () => setShowGradingModal(null); - const handleFinalize = () => setIsFinalized(true); + + /* ---------------- SAVE / FINALIZE ---------------- */ + + const handleFinalize = async () => { + if (!teamData.teamCode) { + setSaveMessage('Error: Team code is missing. Cannot save grades.'); + return; + } + setIsSaving(true); + setSaveMessage(''); + try { + const gradings = reviewerData.map(r => ({ + reviewer: r.reviewer, + prsReviewed: r.gradedPrs.length, + prsNeeded: r.prsNeeded, + gradedPrs: r.gradedPrs.map(pr => ({ prNumbers: pr.prNumbers, grade: pr.grade })), + })); + + await dispatch(saveWeeklyGrading(teamData.teamCode, teamData.dateRange?.start, gradings)); + setIsFinalized(true); + setSaveMessage('Grades saved successfully!'); + } catch { + setSaveMessage('Error saving grades. Please try again.'); + } finally { + setIsSaving(false); + } + }; + + /* ---------------- PROMOTION ---------------- */ + + const handlePromoteClick = async reviewer => { + try { + const response = await axios.get( + `${process.env.REACT_APP_APIENDPOINT}/promotion-details/${reviewer.id}`, + ); + setPromotionCandidate({ + reviewerId: reviewer.id, + reviewerName: response.data.reviewerName || reviewer.reviewer, + teamCode: response.data.teamCode || teamData.teamName, + teamReviewerName: response.data.teamReviewerName || reviewer.reviewer, + weeklyPRs: + response.data.weeklyPRs && response.data.weeklyPRs.length > 0 + ? response.data.weeklyPRs + : [{ week: teamData.dateRange.start, count: reviewer.gradedPrs.length }], + }); + } catch { + setPromotionCandidate({ + reviewerId: reviewer.id, + reviewerName: reviewer.reviewer, + teamCode: teamData.teamName, + teamReviewerName: reviewer.reviewer, + weeklyPRs: [{ week: teamData.dateRange.start, count: reviewer.gradedPrs.length }], + }); + } + }; + + const handleConfirmPromotion = async (reviewerName, reviewerId) => { + try { + if (reviewerId) { + await axios.post(`${process.env.REACT_APP_APIENDPOINT}/promote-members`, { + memberIds: [reviewerId], + }); + } + } catch { + // silently continue even if API fails + } + setConfirmedPromotions(prev => [...prev, reviewerName]); + setPromotionCandidate(null); + }; + + const handleCancelPromotion = () => { + setPromotionCandidate(null); + }; + + /* ---------------- BATCH PROMOTION ---------------- */ + + const handleCheckboxChange = reviewerId => { + setSelectedForPromotion(prev => + prev.includes(reviewerId) ? prev.filter(id => id !== reviewerId) : [...prev, reviewerId], + ); + }; + + const handleBatchConfirm = async () => { + try { + if (selectedForPromotion.length > 0) { + await axios.post(`${process.env.REACT_APP_APIENDPOINT}/promote-members`, { + memberIds: selectedForPromotion, + }); + } + } catch { + // silently continue + } + const selectedNames = reviewerData + .filter(r => selectedForPromotion.includes(r.id)) + .map(r => r.reviewer); + setConfirmedPromotions(prev => [...prev, ...selectedNames]); + setSelectedForPromotion([]); + setShowBatchConfirm(false); + }; + + const handleBatchCancel = () => { + setShowBatchConfirm(false); + }; /* ---------------- RENDER ---------------- */ - const dm = darkMode ? styles['dark-mode'] : ''; + if (isLoading) return
Loading...
; return ( - + - - + +
-

+

Weekly PR grading screen

-
+
{teamData.teamName} - {teamData.dateRange.start} to {teamData.dateRange.end}
- +
+ {saveMessage && ( + {saveMessage} + )} + +
- - {/* ── Search Bar ── */} -
+ + {/* Search Bar */} +
setSearchTerm(e.target.value)} - className={`${styles['pr-grading-screen-search-input']} ${dm}`} + className={`${styles['pr-grading-screen-search-input']} ${ + darkMode ? styles['dark-mode'] : '' + }`} /> - {availableRoles.length > 0 && ( )} - {(searchTerm || roleFilter) && ( )}
- +
@@ -188,86 +380,167 @@ const PRGradingScreen = ({ teamData, reviewers }) => { - {filteredReviewers.length === 0 ? ( - ) : ( filteredReviewers.map(reviewer => ( - - - - + + + + + + - - - + )} + + + + {/* Inline summary row per reviewer */} + {reviewer.gradedPrs.length > 0 && ( + + + + )} + )) )} @@ -278,9 +551,21 @@ const PRGradingScreen = ({ teamData, reviewers }) => { {showGradingModal && ( -
-
-
+
+
+

Grade PR

- -
-
Reviewer NamePR Numbers
+ No reviewers found
{reviewer.reviewer} - -
{reviewer.reviewer} + + {reviewer.prsNeeded} + {reviewer.gradedPrs.map(pr => ( + handlePRNumberClick(reviewer.id)} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handlePRNumberClick(reviewer.id); + } + }} + > + {pr.prNumbers} + + ))} - {reviewer.prsNeeded} - {reviewer.gradedPrs.map(pr => ( - handlePRNumberClick(reviewer.id)} - onKeyDown={e => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - handlePRNumberClick(reviewer.id); - } - }} - > - {pr.prNumbers} - - ))} - - {!isFinalized && activeInput !== reviewer.id && ( - - )} - - {!isFinalized && activeInput === reviewer.id && ( -
- setInputValue(e.target.value)} - className={styles['pr-grading-screen-pr-number-input']} - placeholder="1070 or 1070 + 1256" - /> + {!isFinalized && activeInput !== reviewer.id && ( - + + {inputError && ( +
+ {inputError} +
+ )} +
+ )} + + {reviewer.gradedPrs.length > 0 && ( + - - )} -
+ + + + + + + + + + + + {reviewer.gradedPrs.map(pr => ( + + + {[ + 'Exceptional', + 'Okay', + 'Unsatisfactory', + 'No Correct Image', + ].map(grade => ( + + ))} + + ))} + +
PR NumberExceptionalOkayUnsatisfactoryNo Correct Image
{pr.prNumbers} + + handleGradeChange(reviewer.id, pr.id, grade) + } + /> +
+
+
+
- + @@ -339,9 +631,9 @@ const PRGradingScreen = ({ teamData, reviewers }) => { - handleGradeChange(showGradingModal, pr.id, 'Cannot find image') + handleGradeChange(showGradingModal, pr.id, 'No Correct Image') } /> @@ -349,8 +641,11 @@ const PRGradingScreen = ({ teamData, reviewers }) => { ))}
PR Number Exceptional Okay UnsatisfactoryCannot find imageNo Correct Image
- -
+
@@ -359,31 +654,182 @@ const PRGradingScreen = ({ teamData, reviewers }) => {
)} + + {selectedForPromotion.length > 0 && ( +
+ + {selectedForPromotion.length} selected + + + +
+ )} + + {showBatchConfirm && ( +
+
+
+

+ 🏆 Confirm Batch Promotion +

+ +
+
+

+ You are about to promote {selectedForPromotion.length} reviewer(s): +

+
    + {reviewerData + .filter(r => selectedForPromotion.includes(r.id)) + .map(r => ( +
  • + {r.reviewer} +
  • + ))} +
+

+ Are you sure you want to promote all selected reviewers? +

+
+
+ + +
+
+
+ )} + + {promotionCandidate && ( + + )} ); }; PRGradingScreen.propTypes = { teamData: PropTypes.shape({ - teamName: PropTypes.string.isRequired, + teamCode: PropTypes.string, + teamName: PropTypes.string, dateRange: PropTypes.shape({ - start: PropTypes.string.isRequired, - end: PropTypes.string.isRequired, - }).isRequired, + start: PropTypes.string, + end: PropTypes.string, + }), }).isRequired, reviewers: PropTypes.arrayOf( PropTypes.shape({ - id: PropTypes.string.isRequired, - reviewer: PropTypes.string.isRequired, - role: PropTypes.string, + id: PropTypes.string, + reviewer: PropTypes.string, prsNeeded: PropTypes.number, - gradedPrs: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - prNumbers: PropTypes.string.isRequired, - grade: PropTypes.string.isRequired, - }), - ).isRequired, + prsReviewed: PropTypes.number, + gradedPrs: PropTypes.array, }), ).isRequired, }; diff --git a/src/components/PRGradingScreen/PRGradingTest.jsx b/src/components/PRGradingScreen/PRGradingTest.jsx index 0a6429d7c2..94eb192073 100644 --- a/src/components/PRGradingScreen/PRGradingTest.jsx +++ b/src/components/PRGradingScreen/PRGradingTest.jsx @@ -33,8 +33,6 @@ const PRGradingTest = () => { cfg.testDataType }${cfg.notes ? ` — ${cfg.notes}` : ''}`, fromDB: true, - reviewerCount: cfg.reviewerCount, - reviewerNames: cfg.reviewerNames || [], })); setDynamicTeams(fetched); } catch { @@ -50,7 +48,7 @@ const PRGradingTest = () => { const handleTeamSelect = teamId => { const team = allTeams.find(t => t.id === teamId); - history.push('/pr-grading-screen', { teamId, config: team }); + history.push('/pr-grading-screen', { teamId, teamName: team?.name }); }; const handleDeleteConfig = async (e, teamId) => { diff --git a/src/components/PRGradingScreen/index.jsx b/src/components/PRGradingScreen/index.jsx index fc465f994a..0cee00087c 100644 --- a/src/components/PRGradingScreen/index.jsx +++ b/src/components/PRGradingScreen/index.jsx @@ -1,37 +1,124 @@ +import { useEffect, useState } from 'react'; +import { Spinner } from 'react-bootstrap'; +import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; -import { v4 as uuidv4 } from 'uuid'; +import { getWeeklyGrading } from '../../actions/prAnalytics/weeklyGradingActions'; import { getDataByTeamId } from './mockData'; import PRGradingScreen from './PRGradingScreen'; -const STATIC_IDS = ['team1', 'team2', 'team3']; - const PRGradingScreenContainer = () => { const location = useLocation(); + const dispatch = useDispatch(); + const token = useSelector(state => state.auth.token); + const teamId = location.state?.teamId || 'team1'; - const config = location.state?.config || null; - - if (!STATIC_IDS.includes(teamId) && config) { - const reviewers = Array.from({ length: config.reviewerCount }, (_, i) => ({ - id: uuidv4(), - reviewer: config.reviewerNames?.[i] || `Reviewer ${i + 1}`, - prsNeeded: 10, - prsReviewed: 0, - gradedPrs: [], - })); - - const teamData = { - teamName: config.teamName, - dateRange: { - start: new Date().toLocaleDateString(), - end: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toLocaleDateString(), - }, + const isStaticTeam = ['team1', 'team2', 'team3'].includes(teamId); + + const [teamData, setTeamData] = useState(null); + const [reviewers, setReviewers] = useState([]); + const [loading, setLoading] = useState(!isStaticTeam); + + useEffect(() => { + if (isStaticTeam) { + const data = getDataByTeamId(teamId); + setTeamData(data.teamData); + setReviewers(data.reviewers); + return; + } + + // Dynamic team — fetch from backend + const fetchData = async () => { + setLoading(true); + try { + const today = new Date(); + const weekStart = new Date(today); + weekStart.setDate(today.getDate() - today.getDay()); + const weekEnd = new Date(weekStart); + weekEnd.setDate(weekStart.getDate() + 6); + + const formatDate = d => `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`; + + const weekStartStr = formatDate(weekStart); + const weekEndStr = formatDate(weekEnd); + + // Fetch team config to get reviewer names + const { default: axios } = await import('axios'); + const { ENDPOINTS } = await import('../../utils/URL'); + const configRes = await axios.get(ENDPOINTS.PR_GRADING_CONFIG); + const config = configRes.data.find(c => c._id === teamId); + + const teamName = config?.teamName || location.state?.teamName || teamId; + + setTeamData({ + teamCode: teamId, + teamName, + dateRange: { start: weekStartStr, end: weekEndStr }, + }); + + // Fetch existing grading data + const gradingData = await dispatch(getWeeklyGrading(teamId, weekStartStr, token)); + + const { v4: uuidv4 } = await import('uuid'); + + if (gradingData && gradingData.length > 0) { + const mapped = gradingData.map(entry => ({ + id: uuidv4(), + reviewer: entry.reviewer, + prsNeeded: entry.prsNeeded, + prsReviewed: entry.prsReviewed, + gradedPrs: (entry.gradedPrs || []).map(pr => ({ ...pr, id: uuidv4() })), + })); + setReviewers(mapped); + } else { + // No grading yet — populate from config using reviewerCount, auto-fill missing names + const count = config?.reviewerCount || 0; + const names = config?.reviewerNames || []; + const getPrsNeeded = dataType => { + switch (dataType) { + case 'minimal': + return 1; + case 'mixed': + return 5; + case 'edge cases': + return 12; + case 'custom': + return 10; + default: + return 10; + } + }; + + const mapped = Array.from({ length: count }, (_, index) => ({ + id: uuidv4(), + reviewer: names[index] || `Reviewer ${index + 1}`, + prsNeeded: getPrsNeeded(config?.testDataType), + prsReviewed: 0, + gradedPrs: [], + })); + setReviewers(mapped); + } + } catch { + setReviewers([]); + } finally { + setLoading(false); + } }; - return ; + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [teamId]); + + if (loading) { + return ( +
+ +
+ ); } - const data = getDataByTeamId(teamId); - return ; + if (!teamData) return
Error: Team not found
; + + return ; }; export default PRGradingScreenContainer; diff --git a/src/utils/URL.js b/src/utils/URL.js index a955a213d5..7e1927f605 100644 --- a/src/utils/URL.js +++ b/src/utils/URL.js @@ -32,7 +32,7 @@ export const ENDPOINTS = { }, MODIFY_BLUE_SQUARE: (userId, blueSquareId) => `${APIEndpoint}/userprofile/${userId}/infringements/${blueSquareId}`, - + // Blue Square Email Triggers BLUE_SQUARE_RESEND_INFRINGEMENT_EMAILS: () => `${APIEndpoint}/blueSquare/resend-infringement-emails-only`, @@ -41,6 +41,8 @@ export const ENDPOINTS = { USERS_ALLTEAMCODE_CHANGE: `${APIEndpoint}/AllTeamCodeChanges`, REPLACE_TEAM_CODE: `${APIEndpoint}/userProfile/replaceTeamCode`, + GET_JOB_FORMS: `${APIEndpoint}/jobForms`, + USERS_REMOVE_PROFILE_IMAGE: `${APIEndpoint}/userProfile/profileImage/remove`, USERS_UPDATE_PROFILE_FROM_WEBSITE: `${APIEndpoint}/userProfile/profileImage/imagefromwebsite`, USER_PROFILE_BASIC_INFO: source => `${APIEndpoint}/userProfile/basicInfo/${source}`, @@ -247,11 +249,11 @@ export const ENDPOINTS = { if (startDate) params.append('startDate', startDate); if (endDate) params.append('endDate', endDate); if (groupBy) params.append('groupBy', groupBy); - + const queryString = params.toString(); return queryString ? `${url}?${queryString}` : url; }, - + INJURY_PROJECTS: () => `${APIEndpoint}/injuries/projects`, PRESETS: () => `${APIEndpoint}/rolePreset`, @@ -303,34 +305,6 @@ export const ENDPOINTS = { GET_TOTAL_COUNTRY_COUNT: () => `${APIEndpoint}/getTotalCountryCount`, - ANALYTICS_AVAILABLE_ROLES: () => `${APIEndpoint}/analytics/roles`, - - // Country Application Map Chart endpoints - COUNTRY_APPLICATION_DATA: (params = {}) => { - let url = `${APIEndpoint}/analytics/country-applications`; - const queryParams = new URLSearchParams(); - - if (params.roles && params.roles.length > 0) { - queryParams.append('roles', params.roles.join(',')); - } - // Include ALL so the API uses filter=all; omit timeFrame when startDate/endDate define the range - if (params.timeFrame) { - queryParams.append('timeFrame', params.timeFrame); - } - if (params.startDate) { - queryParams.append('startDate', params.startDate); - } - if (params.endDate) { - queryParams.append('endDate', params.endDate); - } - if (params.customDateRange) { - queryParams.append('customDateRange', 'true'); - } - - const queryString = queryParams.toString(); - return queryString ? `${url}?${queryString}` : url; - }, - GET_ALL_FOLLOWUPS: () => `${APIEndpoint}/followup`, SET_USER_FOLLOWUP: (userId, taskId) => `${APIEndpoint}/followup/${userId}/${taskId}`, @@ -438,8 +412,8 @@ export const ENDPOINTS = { BM_ORGS_WITH_LOCATION: `${APIEndpoint}/bm/orgLocation`, ORG_DETAILS: projectId => `${APIEndpoint}/bm/orgLocation/${projectId}`, BM_PROJECT_MEMBERS: projectId => `${APIEndpoint}/bm/project/${projectId}/users`, - BM_UPDATE_NAME_AND_UNIT: invtypeId => `${APIEndpoint}/bm/invtypes/material/${invtypeId}`, - BM_ITEM_UPDATE_HISTORY: invtypeId => `${APIEndpoint}/bm/invtypes/${invtypeId}/history`, + BM_UPDATE_NAME_AND_UNIT :invtypeId => `${APIEndpoint}/bm/invtypes/material/${invtypeId}`, + BM_ITEM_UPDATE_HISTORY: invtypeId =>`${APIEndpoint}/bm/invtypes/${invtypeId}/history`, PROJECT_GLOBAL_DISTRIBUTION: `${APIEndpoint}/projectglobaldistribution`, @@ -484,8 +458,6 @@ export const ENDPOINTS = { USER_STATE_SELECTIONS_BATCH: `${APIEndpoint}/userstate/selections/batch`, USER_STATE_CATALOG_USAGE: key => `${APIEndpoint}/userstate/catalog/${key}/usage`, - HGN_FORM_GET_TEAM_MEMBERS_BY_SKILL: skill => `${APIEndpoint}/userProfile/skills/${skill}`, - CREATE_JOB_FORM: `${APIEndpoint}/jobforms`, UPDATE_JOB_FORM: `${APIEndpoint}/jobforms`, GET_JOB_FORM: formId => `${APIEndpoint}/jobforms/${formId}`, @@ -569,22 +541,8 @@ export const ENDPOINTS = { LB_LISTING_AVAILABILITY: `${APIEndpoint}/lb/listing/availability`, LB_LISTING_BOOK: `${APIEndpoint}/lb/listing/availability/booking`, HELP_CATEGORIES: `${APIEndpoint}/help-categories`, - HELP_REQUEST_CREATE: `${APIEndpoint}/helprequest/create`, APPLICANT_SOURCES: `${APIEndpoint}/applicant-analytics/applicant-sources`, - OPT_STATUS_BREAKDOWN: (startDate, endDate, role) => { - let url = `${APIEndpoint}/analytics/opt-status`; - const params = []; - - if (startDate) params.push(`startDate=${startDate}`); - if (endDate) params.push(`endDate=${endDate}`); - if (role) params.push(`role=${role}`); - - return params.length > 0 ? `${url}?${params.join("&")}` : url; -}, - - - // job analytics HOURS_PLEDGED: `${APIEndpoint}/analytics/hours-pledged`, JOB_HITS_AND_APPLICATIONS: `${APIEndpoint}/analytics/job-hits-and-applications`, @@ -637,11 +595,6 @@ export const ENDPOINTS = { WEEKLY_GRADING_SAVE: `${APIEndpoint}/weekly-grading/save`, // Education Portal endpoints - PROGRESS_EDUCATOR_STUDENT: studentId => `${APIEndpoint}/progress/educator/student-progress/${studentId}`, - EDUCATION_TASKS_BY_STUDENT: studentId => `${APIEndpoint}/education-tasks/student/${studentId}`, - EDUCATION_TASK: taskId => `${APIEndpoint}/education-tasks/${taskId}`, - EDUCATION_TASK_STATUS: taskId => `${APIEndpoint}/education-tasks/${taskId}/status`, - STUDENT_PROFILE: `${APIEndpoint}/student/profile`, STUDENT_SUBJECT_TASKS: subjectId => `${APIEndpoint}/student/profile/subject/${subjectId}`, EDUCATOR_ASSIGN_ATOMS: () => `${APIEndpoint}/educator/assign-atoms`, @@ -666,10 +619,10 @@ export const ENDPOINTS = { KI_CALENDAR_EVENTS: (month, year) => `${APIEndpoint}/kitchenandinventory/calendar?month=${month}&year=${year}`, // Help Request & Feedback Modal endpoints - HGN_FORM_RANKED: `${APIEndpoint}/hgnform/ranked`, - HELP_REQUEST_CHECK_MODAL: userId => `${APIEndpoint}/helprequest/check-modal/${userId}`, - FEEDBACK_CLOSE_PERMANENTLY: `${APIEndpoint}/feedback/close-permanently`, - FEEDBACK_SUBMIT: `${APIEndpoint}/feedback/submit`, +HGN_FORM_RANKED: `${APIEndpoint}/hgnform/ranked`, +HELP_REQUEST_CHECK_MODAL: userId => `${APIEndpoint}/helprequest/check-modal/${userId}`, +FEEDBACK_CLOSE_PERMANENTLY: `${APIEndpoint}/feedback/close-permanently`, +FEEDBACK_SUBMIT: `${APIEndpoint}/feedback/submit`, // application time analytics APPLICATION_TIME_DATA: (startDate, endDate, roles) => { let url = `${APIEndpoint}/analytics/application-time?`;