diff --git a/bun.lockb b/bun.lockb
index 05d15e7a6..9c7a61636 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/i18n.js b/i18n.js
index baba9084f..8d004e07d 100644
--- a/i18n.js
+++ b/i18n.js
@@ -22,6 +22,7 @@ module.exports = {
'/interactive-coding-tutorials': ['projects'],
'/interactive-exercise/[slug]': ['exercises', 'workshops'],
'/choose-program': ['choose-program', 'dashboard', 'profile', 'assignments'],
+ '/main-cohort/[mainCohortSlug]/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'],
'/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'],
'/survey/[surveyId]': ['survey'],
'/mentorship': ['mentorship'],
diff --git a/package-lock.json b/package-lock.json
index fee068f51..06cecd83a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -73,6 +73,7 @@
"react-player": "^2.12.0",
"react-plx": "^2.1.2",
"react-redux": "8.0.5",
+ "react-rewards": "^2.0.4",
"react-select": "^5.7.3",
"react-syntax-highlighter": "15.5.0",
"react-tagsinput": "^3.20.1",
@@ -28732,6 +28733,15 @@
}
}
},
+ "node_modules/react-rewards": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-rewards/-/react-rewards-2.0.4.tgz",
+ "integrity": "sha512-Lw7gIhD8yPDzC6boaVmcXwuTHRLSLAdqB3kZc+29YWvdHWsuc3fdAZlxI8Cm8fvD8fhP+3JkZBtzX224czw15w==",
+ "peerDependencies": {
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-select": {
"version": "5.7.5",
"license": "MIT",
diff --git a/package.json b/package.json
index ea1cc4bfe..42fc8fb93 100644
--- a/package.json
+++ b/package.json
@@ -108,6 +108,7 @@
"react-player": "^2.12.0",
"react-plx": "^2.1.2",
"react-redux": "8.0.5",
+ "react-rewards": "^2.0.4",
"react-select": "^5.7.3",
"react-syntax-highlighter": "15.5.0",
"react-tagsinput": "^3.20.1",
diff --git a/public/locales/en/alert-message.json b/public/locales/en/alert-message.json
index 7c0d686eb..dc1f2f950 100644
--- a/public/locales/en/alert-message.json
+++ b/public/locales/en/alert-message.json
@@ -8,6 +8,7 @@
"content-not-found2": "The endpoint could not access any content of this {{lesson}}",
"default-version-not-found": "Default version could not access any content of this {{lesson}}",
"invalid-cohort-slug": "Invalid cohort slug",
+ "error-fetching-syllabus": "There was a problem while fetching the syllabus data",
"no-cohort-modules-found": "No cohort modules found, first choose a valid cohort",
"language-not-found": "Data for language \"{{currentLanguageLabel}}\" not found, showing the english version",
"task-cant-sync-with-cohort": "Some Tasks cannot sync with current cohort",
diff --git a/public/locales/en/choose-program.json b/public/locales/en/choose-program.json
index 1668489db..c203d7fa1 100644
--- a/public/locales/en/choose-program.json
+++ b/public/locales/en/choose-program.json
@@ -4,6 +4,10 @@
},
"title": "Your Programs",
"welcome-back-user": "Welcome, {{name}}",
+ "hello-user": "Hello, {{name}}",
+ "rigo-chat": {
+ "welcome-message": "Hi {{firstName}}! I see you are on the course {{cohortName}}. Is there anything you would like to know about it?"
+ },
"welcome": "Welcome",
"your-active-programs": "Your active programs",
"join-our-community": "Join our community",
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index d1dbcc91b..c7e0eb153 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1,11 +1,15 @@
{
"change-language": "Change language",
"connect-with-github": "Connect with Github",
+ "connect-with-rigobot": "Connect with Rigobot",
+ "get-help-rigobot": "Get help from Rigobot",
"see-financing-options": "See financing options",
"your-tutors-in-this-cohort": "Your tutors:",
"main-instructor": "Main Instructor",
"teacher-assistant": "Teacher Assistant",
"rigo": "Rigo",
+ "see-workshops": "See all workshops",
+ "schedule-mentoring": "Schedule a mentoring session",
"rigobot-bubble": {
"greeting": "Hi!"
},
diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json
index b27b8bb98..5a5fddfb8 100644
--- a/public/locales/en/dashboard.json
+++ b/public/locales/en/dashboard.json
@@ -4,8 +4,26 @@
},
"title": "Your News",
"moduleMap": "Module map",
+ "module": "Module",
+ "modules-count": "{{count}} Modules",
+ "path-to-claim": "Path to claim your certificate",
+ "open": "Open",
+ "completed": "Completed!",
+ "hours-worked": "{{hours}} Hours worked",
+ "issued-on": "Issued on {{date}}",
+ "share": "Share",
+ "hide-content": "Hide content",
+ "show-content": "Show content",
+ "start-course": "Start course",
"backToChooseProgram": "Back to choose program",
"progressText": "progress in the program",
+ "students-modal": {
+ "students-course": "Students in this course",
+ "select-student": "Select a student to see their full report",
+ "filter-by-name": "Filter by name or email",
+ "student": "Student",
+ "no-students": "No students found"
+ },
"whiteLabeledText": "This course is brought to you thanks to our parnership with this university",
"free-trial-msg": "You are currently on a free trial, some features might be limited. Upgrade your plan to have unlimited access!",
"intro-video-title": "Welcome!",
@@ -198,6 +216,7 @@
"take-attendancy": "Take attendance",
"review-attendancy": "Review attendance",
"assignments": "Assignments",
+ "student-progress": "Student Progress",
"teacher-tutorial": "Teacher tutorial",
"no-instructions": ">:warning: No available instruction found for this module"
},
diff --git a/public/locales/es/alert-message.json b/public/locales/es/alert-message.json
index e78fb9f32..60913881f 100644
--- a/public/locales/es/alert-message.json
+++ b/public/locales/es/alert-message.json
@@ -8,6 +8,7 @@
"content-not-found2": "El endpoint no pudo acceder a ningún contenido de esta {{lesson}}",
"default-version-not-found": "La versión predeterminada no pudo acceder a ningún contenido de este {{lesson}}",
"invalid-cohort-slug": "Slug de cohorte no válido",
+ "error-fetching-syllabus": "Hubo un problema al mostrar la información del plan de estudios",
"no-cohort-modules-found": "No se encontraron módulos de cohorte, primero elija una cohorte válida",
"language-not-found": "No se encontró información para el idioma \"{{currentLanguageLabel}}\", mostrando la versión en inglés",
"task-cant-sync-with-cohort": "Algunas tareas no se pueden sincronizar con la cohorte actual",
diff --git a/public/locales/es/choose-program.json b/public/locales/es/choose-program.json
index 6a8d62e62..7e25e7825 100644
--- a/public/locales/es/choose-program.json
+++ b/public/locales/es/choose-program.json
@@ -4,6 +4,10 @@
},
"title": "Tus Programas",
"welcome-back-user": "Bienvenido, {{name}}",
+ "hello-user": "Hola, {{name}}",
+ "rigo-chat": {
+ "welcome-message": "¡Hola {{firstName}}! Veo que estas leyendo haciendo el curso {{cohortName}}. ¿Hay algo que pueda hacer para ayudarte?"
+ },
"welcome": "Bienvenido",
"your-active-programs": "Tus programas activos",
"join-our-community": "Únete a nuestra comunidad",
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index a0ebad0f0..7a1be183a 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -1,11 +1,18 @@
{
"change-language": "Cambiar idioma",
"connect-with-github": "Conéctate con Github",
+ "connect-with-rigobot": "Conéctate con Rigobot",
+ "get-help-rigobot": "Pide ayuda a Rigobot",
"see-financing-options": "Ver opciones de financiamiento",
"your-tutors-in-this-cohort": "Tus tutores:",
"main-instructor": "Instructor Principal",
"teacher-assistant": "Asistente de Profesor",
"rigo": "Rigo",
+ "see-workshops": "Ver todos los workshops",
+ "schedule-mentoring": "Agenda una sesión de mentoria",
+ "rigobot-bubble": {
+ "greeting": "Hola!"
+ },
"ai-tutor": "Tutor IA",
"clone-modal": {
"title": "¿Cómo clonar un proyecto?"
diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json
index 33ef3e7f8..5c7a6e248 100644
--- a/public/locales/es/dashboard.json
+++ b/public/locales/es/dashboard.json
@@ -4,8 +4,26 @@
},
"title": "Tus noticias",
"moduleMap": "Mapa de módulos",
+ "module": "Módulo",
+ "modules-count": "{{count}} Módulos",
+ "path-to-claim": "Vía para reclamar tu certificado",
+ "open": "Abrir",
+ "completed": "¡Completed!",
+ "hours-worked": "{{hours}} Horas trabajadas",
+ "issued-on": "Emitido en {{date}}",
+ "share": "Compartir",
+ "hide-content": "Ocultar contenido",
+ "show-content": "Mostrar contenido",
+ "start-course": "Empezar el curso",
"backToChooseProgram": "Volver a elegir programa",
"progressText": "Progreso en el programa",
+ "students-modal": {
+ "students-course": "Estudiantes en este curso",
+ "select-student": "Selecciona un estudiante para ver el reporte de su progreso",
+ "filter-by-name": "Filtrar por nombre o email",
+ "student": "Estudiante",
+ "no-students": "No se encontraron estudiantes"
+ },
"whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.",
"free-trial-msg": "Actualmente se encuentra en una prueba gratuita, algunas funciones pueden ser limitadas. ¡Actualiza tu plan para tener acceso ilimitado!",
"intro-video-title": "Bienvenido!",
@@ -199,6 +217,7 @@
"take-attendancy": "Tomar asistencia",
"review-attendancy": "Revisar asistencia",
"assignments": "Tareas",
+ "student-progress": "Progreso del Estudiante",
"teacher-tutorial": "Tutorial de profesor",
"no-instructions": ">:warning: No se encontró instrucción disponible para este módulo"
},
diff --git a/src/common/components/AttendanceModal/index.jsx b/src/common/components/AttendanceModal/index.jsx
index 4c0b28248..f47470a29 100644
--- a/src/common/components/AttendanceModal/index.jsx
+++ b/src/common/components/AttendanceModal/index.jsx
@@ -20,8 +20,7 @@ function AttendanceModal({
title, message, isOpen, onClose, students,
}) {
const { t } = useTranslation('dashboard');
- const { state, setCohortSession } = useCohortHandler();
- const { cohortSession, sortedAssignments } = state;
+ const { setCohortSession, cohortSession, sortedAssignments } = useCohortHandler();
const [historyLog, setHistoryLog] = useState();
const [day, setDay] = useState(cohortSession.current_day);
const [attendanceTaken, setAttendanceTaken] = useState({});
diff --git a/src/common/components/FooterTC.jsx b/src/common/components/FooterTC.jsx
index c72db4237..8217869f7 100644
--- a/src/common/components/FooterTC.jsx
+++ b/src/common/components/FooterTC.jsx
@@ -21,6 +21,7 @@ function FooterTC({ pageProps }) {
const noFooterRoutes = [
'/cohort/[cohortSlug]/[slug]/[version]',
'/syllabus/[cohortSlug]/[lesson]/[lessonSlug]',
+ '/main-cohort/[mainCohortSlug]/syllabus/[cohortSlug]/[lesson]/[lessonSlug]',
'/mentorship/schedule',
];
diff --git a/src/common/components/Icon/set/badge.jsx b/src/common/components/Icon/set/badge.jsx
new file mode 100644
index 000000000..3d6686172
--- /dev/null
+++ b/src/common/components/Icon/set/badge.jsx
@@ -0,0 +1,23 @@
+const badge = ({
+ width, height, style, color,
+}) => (
+
+);
+
+export default badge;
diff --git a/src/common/components/Icon/set/certificate-2.jsx b/src/common/components/Icon/set/certificate-2.jsx
new file mode 100644
index 000000000..e3399ab24
--- /dev/null
+++ b/src/common/components/Icon/set/certificate-2.jsx
@@ -0,0 +1,78 @@
+const certificate2 = ({
+ width, height, style, color, color2,
+}) => (
+
+
+);
+
+export default certificate2;
diff --git a/src/common/components/Icon/set/certificate-small.jsx b/src/common/components/Icon/set/certificate-small.jsx
new file mode 100644
index 000000000..95c7bbe6a
--- /dev/null
+++ b/src/common/components/Icon/set/certificate-small.jsx
@@ -0,0 +1,27 @@
+const certificateSmall = ({
+ width, height, style, color,
+}) => (
+
+);
+
+export default certificateSmall;
diff --git a/src/common/components/Icon/set/dots.jsx b/src/common/components/Icon/set/dots.jsx
new file mode 100644
index 000000000..3733b32d1
--- /dev/null
+++ b/src/common/components/Icon/set/dots.jsx
@@ -0,0 +1,27 @@
+const send = ({
+ width, height, style, color,
+}) => (
+
+);
+
+export default send;
diff --git a/src/common/components/Icon/set/party-popper-off.jsx b/src/common/components/Icon/set/party-popper-off.jsx
new file mode 100644
index 000000000..3264bd1b8
--- /dev/null
+++ b/src/common/components/Icon/set/party-popper-off.jsx
@@ -0,0 +1,35 @@
+const partyPopperOff = ({
+ width, height, style, color, color2,
+}) => (
+
+);
+
+export default partyPopperOff;
diff --git a/src/common/components/Icon/set/party-popper.jsx b/src/common/components/Icon/set/party-popper.jsx
new file mode 100644
index 000000000..1b962a6c1
--- /dev/null
+++ b/src/common/components/Icon/set/party-popper.jsx
@@ -0,0 +1,40 @@
+const partyPopperOff = ({
+ width, height, style,
+}) => (
+
+
+);
+
+export default partyPopperOff;
diff --git a/src/common/components/Icon/set/share.jsx b/src/common/components/Icon/set/share.jsx
new file mode 100644
index 000000000..74df4abb9
--- /dev/null
+++ b/src/common/components/Icon/set/share.jsx
@@ -0,0 +1,20 @@
+const send = ({
+ width, height, style, color,
+}) => (
+
+
+);
+
+export default send;
diff --git a/src/common/components/LiveEvent/MainEvent.jsx b/src/common/components/LiveEvent/MainEvent.jsx
index b798934a2..98bc85eec 100644
--- a/src/common/components/LiveEvent/MainEvent.jsx
+++ b/src/common/components/LiveEvent/MainEvent.jsx
@@ -1,3 +1,4 @@
+/* eslint-disable camelcase */
/* eslint-disable react/jsx-no-useless-fragment */
import { Box, Divider, Tag, TagLabel } from '@chakra-ui/react';
import PropTypes from 'prop-types';
@@ -26,7 +27,7 @@ function MainEvent({
const liveStartsAtDate = new Date(event?.starting_at);
const liveEndsAtDate = new Date(event?.ended_at || event?.ending_at);
- const isTeacher = cohorts.some(({ cohort, role }) => cohort.slug === event.cohort?.slug && ['TEACHER', 'ASSISTANT'].includes(role));
+ const isTeacher = cohorts.some(({ slug, cohort_user }) => slug === event.cohort?.slug && ['TEACHER', 'ASSISTANT'].includes(cohort_user.role));
const joinMessage = () => (isTeacher ? t('start-class') : event?.cohort?.name);
return (
diff --git a/src/common/components/Navbar/index.jsx b/src/common/components/Navbar/index.jsx
index 83741d5a4..a77bcb295 100644
--- a/src/common/components/Navbar/index.jsx
+++ b/src/common/components/Navbar/index.jsx
@@ -44,7 +44,6 @@ function NavbarWithSubNavigation({ translations, pageProps }) {
const isUtmMediumAcademy = userSession?.utm_medium === 'academy';
const { isAuthenticated, isLoading, user, logout } = useAuth();
const [ITEMS, setITEMS] = useState([]);
- const [allSubscriptions, setAllSubscriptions] = useState([]);
const [mktCourses, setMktCourses] = useState([]);
const [userCohorts, setUserCohorts] = useState([]);
const { state } = useCohortHandler();
@@ -78,60 +77,13 @@ function NavbarWithSubNavigation({ translations, pageProps }) {
} = navbarTR[locale];
const translationsPropsExists = translations?.length > 0;
- const { selectedProgramSlug } = cohortSession;
-
- const programSlug = cohortSession?.selectedProgramSlug || '/choose-program';
+ const programSlug = '/choose-program';
const whiteLabelitems = t('white-label-version-items', {
selectedProgramSlug: '/choose-program',
}, { returnObjects: true });
- useEffect(() => {
- if (cohortSession?.available_as_saas) {
- bc.payment({
- status: 'ACTIVE,FREE_TRIAL,FULLY_PAID,CANCELLED,PAYMENT_ISSUE,EXPIRED,ERROR',
- }).subscriptions()
- .then(async ({ data }) => {
- const planFinancings = data?.plan_financings?.length > 0 ? data?.plan_financings : [];
- const subscriptions = data?.subscriptions?.length > 0 ? data?.subscriptions : [];
-
- setAllSubscriptions([...planFinancings, ...subscriptions]);
- });
- }
- }, [cohortSession]);
-
- const allowNavigation = () => {
- const getAdditionalInfo = () => {
- if (allSubscriptions) {
- const currentSessionSubs = allSubscriptions?.filter((sub) => sub.academy?.id === cohortSession?.academy?.id);
- const cohortSubscriptions = currentSessionSubs?.filter((sub) => sub.selected_cohort_set?.cohorts.some((cohort) => cohort.id === cohortSession.id));
-
- if (cohortSubscriptions.length === 0) {
- return false;
- }
-
- const expiredCourse = cohortSubscriptions.find((sub) => sub.status === 'EXPIRED' || sub.status === 'ERROR');
- if (expiredCourse) return false;
-
- const fullyPaidSub = cohortSubscriptions.find((sub) => sub.status === 'FULLY_PAID' || sub.status === 'ACTIVE');
- if (fullyPaidSub) return true;
-
- const freeTrialSub = cohortSubscriptions.find((sub) => sub.status === 'FREE_TRIAL');
- const freeTrialExpDate = new Date(freeTrialSub?.valid_until);
- const todayDate = new Date();
-
- if (todayDate > freeTrialExpDate) return false;
- return true;
- }
- return false;
- };
-
- if (cohortSession?.available_as_saas === true && cohortSession.cohort_role === 'STUDENT') return getAdditionalInfo();
- if (Object.keys(cohortSession).length > 0 && (cohortSession.cohort_role !== 'STUDENT' || cohortSession.available_as_saas === false)) return true;
- return false;
- };
-
- const items = t('ITEMS', { selectedProgramSlug: allowNavigation() ? selectedProgramSlug : '/choose-program' }, { returnObjects: true });
+ const items = t('ITEMS', { selectedProgramSlug: '/choose-program' }, { returnObjects: true });
axios.defaults.headers.common['Accept-Language'] = locale;
@@ -215,7 +167,7 @@ function NavbarWithSubNavigation({ translations, pageProps }) {
setITEMS(preFilteredItems.filter((item) => item.disabled !== true));
}
}
- }, [user, userCohorts, isLoading, selectedProgramSlug, mktCourses, router.locale, location]);
+ }, [user, userCohorts, isLoading, cohortSession, mktCourses, router.locale, location]);
const closeSettings = () => {
setSettingsOpen(false);
diff --git a/src/common/components/ProgressBar/Progress.jsx b/src/common/components/ProgressBar/Progress.jsx
index e734b2bd1..4a69501c3 100644
--- a/src/common/components/ProgressBar/Progress.jsx
+++ b/src/common/components/ProgressBar/Progress.jsx
@@ -13,6 +13,7 @@ function Progress({
baseColor,
borderRadius,
widthSize,
+ width,
}) {
const [barWidth, setBarWidth] = useState(0);
const [initialized, setInitialized] = useState(false);
@@ -53,7 +54,7 @@ function Progress({
const baseColorDefault = useColorModeValue('gray.100', 'whiteAlpha.300');
return (
-
+
100 ? 100 : percentage;
const taskPercentageLimited = taskCount?.percentage > 100 ? 100 : taskCount?.percentage;
diff --git a/src/common/components/ReviewModal/index.jsx b/src/common/components/ReviewModal/index.jsx
index 6a249a900..4fc33c3ca 100644
--- a/src/common/components/ReviewModal/index.jsx
+++ b/src/common/components/ReviewModal/index.jsx
@@ -17,7 +17,6 @@ import LoaderScreen from '../LoaderScreen';
import ReviewCodeRevision from './ReviewCodeRevision';
import useCohortHandler from '../../hooks/useCohortHandler';
import PopoverTaskHandler from '../PopoverTaskHandler';
-import useModuleHandler from '../../hooks/useModuleHandler';
import iconDict from '../../utils/iconDict.json';
import UndoApprovalModal from '../UndoApprovalModal';
import useAuth from '../../hooks/useAuth';
@@ -53,8 +52,7 @@ function ReviewModal({ isExternal, externalFiles, isOpen, isStudent, externalDat
isApprovingOrRejecting: false,
});
const [comment, setComment] = useState('');
- const { updateAssignment } = useModuleHandler();
- const { state } = useCohortHandler();
+ const { updateAssignment, state } = useCohortHandler();
const { cohortSession } = state;
const [currentAssetData, setCurrentAssetData] = useState(null);
const [settingsOpen, setSettingsOpen] = useState(false);
diff --git a/src/common/components/ShareButton.jsx b/src/common/components/ShareButton.jsx
index 77a5e4ab3..0ebbf0d1d 100644
--- a/src/common/components/ShareButton.jsx
+++ b/src/common/components/ShareButton.jsx
@@ -14,7 +14,7 @@ import Link from './NextChakraLink';
import useStyle from '../hooks/useStyle';
function ShareButton({
- variant, title, shareText, message, link, socials, withParty, onlyModal, currentTask,
+ variant, title, shareText, message, link, socials, withParty, onlyModal, currentTask, onClose,
}) {
const { t } = useTranslation('profile');
const [party, setParty] = useState(true);
@@ -80,6 +80,7 @@ function ShareButton({
onClose={() => {
setIsOpen(false);
setParty(true);
+ onClose();
}}
size="xl"
>
@@ -210,6 +211,7 @@ ShareButton.propTypes = {
shareText: PropTypes.string,
message: PropTypes.string,
withParty: PropTypes.bool,
+ onClose: PropTypes.func,
};
ShareButton.defaultProps = {
@@ -221,6 +223,7 @@ ShareButton.defaultProps = {
shareText: '',
message: '',
withParty: false,
+ onClose: () => {},
};
export default memo(ShareButton);
diff --git a/src/common/components/SupportSidebar/MentoringConsumables.jsx b/src/common/components/SupportSidebar/MentoringConsumables.jsx
index 48484aeaf..77883a8fa 100644
--- a/src/common/components/SupportSidebar/MentoringConsumables.jsx
+++ b/src/common/components/SupportSidebar/MentoringConsumables.jsx
@@ -62,7 +62,7 @@ function NoConsumablesCard({ t, setMentoryProps, handleGetMoreMentorships, mento
}
function ProfilesSection({
- profiles,
+ profiles, size,
}) {
const { usersConnected } = useOnline();
@@ -73,8 +73,8 @@ function ProfilesSection({
const isOnline = usersConnected?.includes(c.user.id);
return (
list.filter((service) => {
if (allCohorts.length > 0) {
return allCohorts.some((elem) => {
- if (elem?.cohort?.academy?.id === service?.academy?.id && (elem?.finantial_status === 'LATE' || elem?.educational_status === 'SUSPENDED')) {
+ if (elem?.academy?.id === service?.academy?.id && (elem?.cohort_user.finantial_status === 'LATE' || elem?.cohort_user.educational_status === 'SUSPENDED')) {
return false;
}
return true;
diff --git a/src/common/components/TeacherSidebar.jsx b/src/common/components/TeacherSidebar.jsx
index ba980866d..f3e86e0a6 100644
--- a/src/common/components/TeacherSidebar.jsx
+++ b/src/common/components/TeacherSidebar.jsx
@@ -101,7 +101,7 @@ function TeacherSidebar({
{/* Start attendance */}
- {cohortSession.ending_date && (
+ {cohortSession?.ending_date && (
setOpenAttendance(true)}>
@@ -111,7 +111,7 @@ function TeacherSidebar({
)}
{/* Review attendance */}
- {cohortSession.ending_date && (
+ {cohortSession?.ending_date && (
{
window.open(`/cohort/${cohortSlug}/attendance`, '_blank');
diff --git a/src/common/context/AuthContext.jsx b/src/common/context/AuthContext.jsx
index 0753a7160..aa6bb5b95 100644
--- a/src/common/context/AuthContext.jsx
+++ b/src/common/context/AuthContext.jsx
@@ -16,6 +16,7 @@ import Text from '../components/Text';
import { SILENT_CODE } from '../../lib/types';
import { warn } from '../../utils/logging';
import { generateUserContext } from '../../utils/rigobotContext';
+import useCohortAction from '../store/actions/cohortAction';
const initialState = {
isLoading: true,
@@ -123,6 +124,7 @@ export const AuthContext = createContext({
function AuthProvider({ children, pageProps }) {
const router = useRouter();
+ const { setMyCohorts } = useCohortAction();
const { t, lang } = useTranslation('footer');
const toast = useToast();
const { rigo, isRigoInitialized } = useRigo();
@@ -371,6 +373,7 @@ function AuthProvider({ children, pageProps }) {
localStorage.removeItem('showGithubWarning');
localStorage.removeItem('redirect');
dispatch({ type: 'LOGOUT' });
+ setMyCohorts([]);
};
const updateProfile = async (payload) => {
diff --git a/src/common/handlers/index.js b/src/common/handlers/index.js
index d9cf65fa5..54bde8b9b 100644
--- a/src/common/handlers/index.js
+++ b/src/common/handlers/index.js
@@ -168,24 +168,24 @@ const handlers = {
date: formatedDate,
};
},
- getCohortsFinished: (cohorts) => cohorts.filter((program) => {
- const educationalStatus = program?.educational_status?.toUpperCase();
- const programCohortStage = program?.cohort?.stage?.toUpperCase();
+ getCohortsFinished: (cohorts) => cohorts.filter((cohort) => {
+ const educationalStatus = cohort.cohort_user.educational_status.toUpperCase();
+ const programCohortStage = cohort.stage.toUpperCase();
const hasEnded = ['ENDED'].includes(programCohortStage);
const isGraduated = educationalStatus === 'GRADUATED';
const showStudent = ['GRADUATED', 'POSTPONED', 'ACTIVE'].includes(educationalStatus);
const isNotHiddenOnPrework = programCohortStage === 'PREWORK'
- && program?.cohort?.is_hidden_on_prework === false
+ && cohort.is_hidden_on_prework === false
&& hasEnded;
return (isGraduated || hasEnded || isNotHiddenOnPrework) && showStudent;
}),
- getActiveCohorts: (cohorts) => cohorts.filter((program) => {
- const educationalStatus = program?.educational_status?.toUpperCase();
- const programRole = program?.role?.toUpperCase();
- const programCohortStage = program?.cohort?.stage?.toUpperCase();
+ getActiveCohorts: (cohorts) => cohorts.filter((cohort) => {
+ const educationalStatus = cohort.cohort_user.educational_status?.toUpperCase();
+ const programRole = cohort.cohort_user.role?.toUpperCase();
+ const programCohortStage = cohort.stage.toUpperCase();
const isGraduated = educationalStatus === 'GRADUATED';
const visibleForTeacher = programRole !== 'STUDENT';
@@ -201,7 +201,7 @@ const handlers = {
const cohortIsAvailable = showCohort && !hasEnded;
const isNotHiddenOnPrework = programCohortStage === 'PREWORK'
- && program?.cohort?.is_hidden_on_prework === false
+ && cohort.is_hidden_on_prework === false
&& !hasEnded;
const showStudent = ['ACTIVE'].includes(educationalStatus) && programRole === 'STUDENT';
diff --git a/src/common/hooks/useCohortHandler.js b/src/common/hooks/useCohortHandler.js
index 16b0bd4a0..618e2f45b 100644
--- a/src/common/hooks/useCohortHandler.js
+++ b/src/common/hooks/useCohortHandler.js
@@ -1,13 +1,14 @@
/* eslint-disable camelcase */
+import { useMemo } from 'react';
import axios from 'axios';
import { useToast } from '@chakra-ui/react';
import useTranslation from 'next-translate/useTranslation';
import { useRouter } from 'next/router';
import useAuth from './useAuth';
-import { devLog, getStorageItem } from '../../utils';
+import { getStorageItem } from '../../utils';
import useCohortAction from '../store/actions/cohortAction';
-import useModuleHandler from './useModuleHandler';
import { processRelatedAssignments } from '../handlers/cohorts';
+import { reportDatalayer } from '../../utils/requests';
import bc from '../services/breathecode';
import { BREATHECODE_HOST, DOMAIN_NAME } from '../../utils/variables';
@@ -15,13 +16,20 @@ function useCohortHandler() {
const router = useRouter();
const { user } = useAuth();
const { t, lang } = useTranslation('dashboard');
- const { setCohortSession, setTaskCohortNull, setSortedAssignments, setUserCapabilities, setMyCohorts, state } = useCohortAction();
- const { cohortProgram, taskTodo, setCohortProgram, setTaskTodo } = useModuleHandler();
+ const {
+ setCohortSession,
+ setTaskCohortNull,
+ setUserCapabilities,
+ setMyCohorts,
+ setCohortsAssingments,
+ state,
+ } = useCohortAction();
const {
cohortSession,
- sortedAssignments,
userCapabilities,
+ cohortsAssignments,
+ myCohorts,
} = state;
const toast = useToast();
const accessToken = getStorageItem('accessToken');
@@ -52,25 +60,127 @@ function useCohortHandler() {
}
};
- const getCohortAssignments = async ({
- slug, cohort, updatedUser = undefined,
+ const serializeModulesMap = (moduleData, tasks) => {
+ const assignmentsRecopilated = [];
+ moduleData.forEach((module) => {
+ const {
+ id, label, description, lessons, replits, assignments, quizzes,
+ } = module;
+ if (lessons && replits && assignments && quizzes) {
+ const nestedAssignments = processRelatedAssignments(module, tasks);
+
+ // this properties name's reassignment is done to keep compatibility with deprecated functions
+ const {
+ content,
+ filteredContent,
+ filteredContentByPending,
+ } = nestedAssignments;
+
+ // Data to be sent to [sortedAssignments] = state
+ const assignmentsStruct = {
+ id,
+ label,
+ description,
+ content,
+ exists_activities: content?.length > 0,
+ filteredContent,
+ filteredContentByPending: content?.length > 0 ? filteredContentByPending : null,
+ duration_in_days: module?.duration_in_days || null,
+ teacherInstructions: module.teacher_instructions,
+ extendedInstructions: module.extended_instructions || `${t('teacher-sidebar.no-instructions')}`,
+ keyConcepts: module['key-concepts'],
+ };
+
+ if (content.length > 0) {
+ // prevent duplicates when a new module has been started (added to sortedAssignments array)
+ const keyIndex = assignmentsRecopilated.findIndex((x) => x.id === id);
+ if (keyIndex > -1) {
+ assignmentsRecopilated.splice(keyIndex, 1, {
+ ...assignmentsStruct,
+ });
+ } else {
+ assignmentsRecopilated.push({
+ ...assignmentsStruct,
+ });
+ }
+ }
+ }
+ });
+
+ return assignmentsRecopilated;
+ };
+
+ const getCohortsModules = async (cohorts) => {
+ try {
+ const assignmentsMap = {};
+
+ const preFechedCohorts = cohorts.reduce((acum, curr) => {
+ if (curr.slug in cohortsAssignments) return [...acum, curr];
+ return acum;
+ }, []);
+
+ const cohortsToFetch = cohorts.filter((cohort) => !preFechedCohorts.some(({ slug }) => slug === cohort.slug));
+
+ const syllabusPromises = cohortsToFetch.map((cohort) => bc.syllabus().get(cohort.academy.id, cohort.syllabus_version.slug, cohort.syllabus_version.version).then((res) => ({ cohort: cohort.id, ...res })));
+ const tasksPromises = cohortsToFetch.map((cohort) => bc.todo({ cohort: cohort.id, limit: 1000 }).getTaskByStudent().then((res) => ({ cohort: cohort.id, ...res })));
+ const allResults = await Promise.all([
+ ...syllabusPromises,
+ ...tasksPromises,
+ ]);
+
+ preFechedCohorts.forEach(({ slug }) => {
+ assignmentsMap[slug] = { ...cohortsAssignments[slug] };
+ });
+
+ cohortsToFetch.forEach((cohort) => {
+ const cohortResults = allResults.filter((elem) => elem.cohort === cohort.id);
+
+ let syllabus = null;
+ let tasks = [];
+
+ cohortResults.forEach((elem) => {
+ const { data } = elem;
+ if ('json' in data) syllabus = data;
+ else tasks = data.results;
+ });
+ const cohortModules = serializeModulesMap(syllabus.json.days, tasks);
+
+ assignmentsMap[cohort.slug] = {
+ modules: cohortModules,
+ syllabus,
+ tasks,
+ };
+ });
+
+ setCohortsAssingments({ ...cohortsAssignments, ...assignmentsMap });
+
+ return assignmentsMap;
+ } catch (e) {
+ console.log(e);
+ toast({
+ position: 'top',
+ title: t('alert-message:error-fetching-syllabus'),
+ status: 'error',
+ duration: 7000,
+ isClosable: true,
+ });
+
+ return {};
+ }
+ };
+
+ const getCohortUserCapabilities = async ({
+ cohort, updatedUser = undefined,
}) => {
if (user) {
- const academyId = cohort?.academy.id;
- const version = cohort?.syllabus_version?.version;
- const syllabusSlug = cohort?.syllabus_version?.slug || slug;
+ const academyId = cohort?.academy?.id;
const currentAcademy = user.roles.find((role) => role.academy.id === academyId) || updatedUser?.roles.find((role) => role.academy.id === academyId);
if (currentAcademy) {
// Fetch cohortProgram and TaskTodo then apply to moduleMap store
try {
- const [taskTodoData, programData, userRoles] = await Promise.all([
- bc.todo({ cohort: cohort.id, limit: 1000 }).getTaskByStudent(), // Tasks with cohort id
- bc.syllabus().get(academyId, syllabusSlug, version), // cohortProgram
- bc.auth().getRoles(currentAcademy?.role), // Roles
- ]);
+ const userRoles = await bc.auth().getRoles(currentAcademy?.role); // Roles
+
setUserCapabilities(userRoles.data.capabilities);
- setTaskTodo(taskTodoData.data.results);
- setCohortProgram(programData.data);
} catch (err) {
console.log(err);
toast({
@@ -98,29 +208,37 @@ function useCohortHandler() {
}
};
+ const parseCohort = (elem) => {
+ const { cohort, ...cohort_user } = elem;
+ const { syllabus_version } = cohort;
+ return {
+ ...cohort,
+ selectedProgramSlug: `/cohort/${cohort.slug}/${syllabus_version.slug}/v${syllabus_version.version}`,
+ cohort_role: elem.role,
+ cohort_user,
+ };
+ };
+
const getCohortData = async ({
cohortSlug,
}) => {
try {
// Fetch cohort data with pathName structure
if (cohortSlug && accessToken) {
- const { data } = await bc.admissions().me();
- if (!data) throw new Error('No data');
- const { cohorts } = data;
-
- const parsedCohorts = cohorts.map(((elem) => {
- const { cohort, ...cohort_user } = elem;
- const { syllabus_version } = cohort;
- return {
- ...cohort,
- selectedProgramSlug: `/cohort/${cohort.slug}/${syllabus_version.slug}/v${syllabus_version.version}`,
- cohort_role: elem.role,
- cohort_user,
- };
- }));
-
// find cohort with current slug
- const currentCohort = parsedCohorts.find((c) => c.slug === cohortSlug);
+ let parsedCohorts = myCohorts.map((cohort) => ({ ...cohort }));
+ let currentCohort = myCohorts.find((c) => c.slug === cohortSlug);
+
+ //we make sure that we have already loaded the data of the cohort and its micro cohorts
+ if (!currentCohort || (currentCohort.micro_cohorts.length > 0 && currentCohort.micro_cohorts.every((cohort) => myCohorts.some(({ slug }) => cohort.slug === slug)))) {
+ const { data } = await bc.admissions().me();
+ if (!data) throw new Error('No data');
+ const { cohorts } = data;
+
+ parsedCohorts = cohorts.map(parseCohort);
+
+ currentCohort = parsedCohorts.find((c) => c.slug === cohortSlug);
+ }
if (!currentCohort) {
if (assetSlug) return handleRedirectToPublicPage();
@@ -128,6 +246,10 @@ function useCohortHandler() {
return router.push('/choose-program');
}
+ const cohorts = currentCohort.micro_cohorts.length > 0 ? parsedCohorts.filter((c) => currentCohort.micro_cohorts.some((elem) => elem.slug === c.slug)) : [currentCohort];
+
+ await getCohortsModules(cohorts);
+
setCohortSession(currentCohort);
setMyCohorts(parsedCohorts);
return currentCohort;
@@ -147,71 +269,189 @@ function useCohortHandler() {
}
};
- // Sort all data fetched in order of taskTodo
- const prepareTasks = () => {
- const moduleData = cohortProgram?.json?.days || cohortProgram?.json?.modules || [];
- const assignmentsRecopilated = [];
- devLog('json.days:', moduleData);
+ const taskTodo = useMemo(() => {
+ if (cohortSession && cohortSession.slug in cohortsAssignments) {
+ return cohortsAssignments[cohortSession.slug].tasks;
+ }
+ return [];
+ }, [cohortsAssignments, cohortSession]);
- if (cohortProgram?.json && taskTodo) {
- moduleData.forEach((assignment) => {
- const {
- id, label, description, lessons, replits, assignments, quizzes,
- } = assignment;
- if (lessons && replits && assignments && quizzes) {
- const nestedAssignments = processRelatedAssignments(assignment, taskTodo);
-
- // this properties name's reassignment is done to keep compatibility with deprecated functions
- const {
- content: modules,
- filteredContent: filteredModules,
- filteredContentByPending: filteredModulesByPending,
- } = nestedAssignments;
-
- // Data to be sent to [sortedAssignments] = state
- const assignmentsStruct = {
- id,
- label,
- description,
- modules,
- exists_activities: modules?.length > 0,
- filteredModules,
- filteredModulesByPending: modules?.length > 0 ? filteredModulesByPending : null,
- duration_in_days: assignment?.duration_in_days || null,
- teacherInstructions: assignment.teacher_instructions,
- extendedInstructions: assignment.extended_instructions || `${t('teacher-sidebar.no-instructions')}`,
- keyConcepts: assignment['key-concepts'],
- };
+ const cohortProgram = useMemo(() => {
+ if (cohortSession && cohortSession.slug in cohortsAssignments) {
+ return cohortsAssignments[cohortSession.slug].syllabus;
+ }
+ return null;
+ }, [cohortsAssignments, cohortSession]);
- // prevent duplicates when a new module has been started (added to sortedAssignments array)
- const keyIndex = assignmentsRecopilated.findIndex((x) => x.id === id);
- if (keyIndex > -1) {
- assignmentsRecopilated.splice(keyIndex, 1, {
- ...assignmentsStruct,
- });
- } else {
- assignmentsRecopilated.push({
- ...assignmentsStruct,
- });
- }
+ const sortedAssignments = useMemo(() => {
+ if (cohortSession?.slug in cohortsAssignments) return cohortsAssignments[cohortSession.slug].modules;
+ return [];
+ }, [cohortsAssignments, cohortSession]);
+
+ const updateTask = (task, cohort) => {
+ const { id, name, slug } = cohort;
+ const cohortData = cohortsAssignments[slug];
+
+ const keyIndex = cohortData.tasks.findIndex((x) => x.id === task.id);
+
+ const newTasks = [
+ ...cohortData.tasks.slice(0, keyIndex), // before keyIndex (inclusive)
+ { ...task, cohort: { id, name, slug } }, // key item (updated)
+ ...cohortData.tasks.slice(keyIndex + 1), // after keyIndex (exclusive)
+ ];
+
+ setCohortsAssingments({
+ ...cohortsAssignments,
+ [slug]: {
+ ...cohortData,
+ tasks: newTasks,
+ modules: serializeModulesMap(cohortData.syllabus.json.days, newTasks),
+ },
+ });
+ };
+
+ const addTasks = (tasks, cohort) => {
+ const { id, slug, name } = cohort;
+ const cohortData = cohortsAssignments[cohort.slug];
+
+ const newTasks = [
+ ...cohortData.tasks,
+ ...tasks.map((task) => ({ ...task, cohort: { id, slug, name } })),
+ ];
+
+ setCohortsAssingments({
+ ...cohortsAssignments,
+ [cohort.slug]: {
+ ...cohortData,
+ tasks: newTasks,
+ modules: serializeModulesMap(cohortData.syllabus.json.days, newTasks),
+ },
+ });
+ };
+
+ const updateAssignment = async ({
+ task, closeSettings, githubUrl, taskStatus,
+ }) => {
+ // Task case
+ const { cohort, ...taskData } = task;
+ const toggleStatus = (task.task_status === undefined || task.task_status === 'PENDING') ? 'DONE' : 'PENDING';
+ const isProject = task.task_type && task.task_type === 'PROJECT';
+
+ try {
+ const projectUrl = githubUrl || '';
+
+ const isDelivering = projectUrl !== '';
+
+ let taskToUpdate;
+
+ const toastMessage = () => {
+ if (!isProject) return t('alert-message:assignment-updated');
+ return isDelivering ? t('alert-message:delivery-success') : t('alert-message:delivery-removed');
+ };
+
+ if (isProject) {
+ taskToUpdate = {
+ ...taskData,
+ task_status: taskStatus || toggleStatus,
+ github_url: projectUrl,
+ revision_status: 'PENDING',
+ delivered_at: new Date(),
+ };
+ } else {
+ taskToUpdate = {
+ ...taskData,
+ id: task.id,
+ task_status: toggleStatus,
+ };
+ }
+
+ const response = await bc.todo().update(taskToUpdate);
+ if (response.status < 400) {
+ updateTask(taskToUpdate, cohort);
+ reportDatalayer({
+ dataLayer: {
+ event: 'assignment_status_updated',
+ task_status: taskStatus,
+ task_id: task.id,
+ task_title: task.title,
+ task_associated_slug: task.associated_slug,
+ task_type: task.task_type,
+ task_revision_status: task.revision_status,
+ },
+ });
+ toast({
+ position: 'top',
+ title: toastMessage(),
+ status: 'success',
+ duration: 6000,
+ isClosable: true,
+ });
+ closeSettings();
+ } else {
+ toast({
+ position: 'top',
+ title: isProject ? t('alert-message:delivery-error') : t('alert-message:assignment-update-error'),
+ status: 'error',
+ duration: 5000,
+ isClosable: true,
+ });
+ closeSettings();
+ }
+ } catch (error) {
+ console.log(error);
+ toast({
+ position: 'top',
+ title: isProject ? t('alert-message:delivery-error') : t('alert-message:assignment-update-error'),
+ status: 'error',
+ duration: 5000,
+ isClosable: true,
+ });
+ closeSettings();
+ }
+ };
+
+ const startDay = async ({
+ newTasks, cohort, label, customHandler = () => {}, updateContext = true,
+ }) => {
+ try {
+ const response = await bc.todo().add(newTasks);
+
+ if (response.status < 400) {
+ toast({
+ position: 'top',
+ title: label
+ ? t('alert-message:module-started', { title: label })
+ : t('alert-message:module-sync-success'),
+ status: 'success',
+ duration: 6000,
+ isClosable: true,
+ });
+ if (updateContext) {
+ addTasks(response.data, cohort);
}
+ customHandler(response.data);
+ }
+ } catch (err) {
+ console.log('error_ADD_TASK 🔴 ', err);
+ toast({
+ position: 'top',
+ title: t('alert-message:module-start-error'),
+ status: 'error',
+ duration: 6000,
+ isClosable: true,
});
- const filterNotEmptyModules = assignmentsRecopilated.filter(
- (l) => l.modules.length > 0,
- );
- setSortedAssignments(filterNotEmptyModules);
}
};
const getTasksWithoutCohort = ({ setModalIsOpen }) => {
// Tasks with cohort null
- if (router.asPath === cohortSession.selectedProgramSlug) {
+ if (router.asPath === cohortSession?.selectedProgramSlug) {
bc.todo({ cohort: null }).getTaskByStudent()
.then(({ data }) => {
const filteredUnsyncedCohortTasks = sortedAssignments.flatMap(
- (assignment) => data.filter(
- (task) => assignment.modules.some(
- (module) => task.associated_slug === module.slug,
+ (module) => data.filter(
+ (task) => module.content.some(
+ (assignment) => task.associated_slug === assignment.slug,
),
),
);
@@ -233,7 +473,7 @@ function useCohortHandler() {
let lastDoneTaskModule = null;
sortedAssignments.forEach(
(module) => {
- if (module.modules.some((task) => task.task_status === 'DONE')) lastDoneTaskModule = module;
+ if (module.content.some((task) => task.task_status === 'DONE')) lastDoneTaskModule = module;
},
);
return lastDoneTaskModule;
@@ -241,7 +481,7 @@ function useCohortHandler() {
const getMandatoryProjects = () => {
const mandatoryProjects = sortedAssignments.flatMap(
- (assignment) => assignment.filteredModules.filter(
+ (module) => module.filteredContent.filter(
(l) => {
const isMandatoryTimeOut = l.task_type === 'PROJECT' && l.task_status === 'PENDING'
&& l.mandatory === true && l.daysDiff >= 14; // exceeds 2 weeks
@@ -255,16 +495,27 @@ function useCohortHandler() {
return {
setCohortSession,
- setSortedAssignments,
- getCohortAssignments,
+ setMyCohorts,
+ getCohortUserCapabilities,
getCohortData,
- prepareTasks,
getDailyModuleData,
getLastDoneTaskModuleData,
getMandatoryProjects,
getTasksWithoutCohort,
userCapabilities,
state,
+ setCohortsAssingments,
+ serializeModulesMap,
+ parseCohort,
+ taskTodo,
+ cohortProgram,
+ addTasks,
+ updateTask,
+ updateAssignment,
+ startDay,
+ getCohortsModules,
+ sortedAssignments,
+ ...state,
};
}
diff --git a/src/common/hooks/useModuleHandler.js b/src/common/hooks/useModuleHandler.js
index 83f5c9224..4cf70e653 100644
--- a/src/common/hooks/useModuleHandler.js
+++ b/src/common/hooks/useModuleHandler.js
@@ -1,161 +1,9 @@
-import { useToast } from '@chakra-ui/react';
-import useTranslation from 'next-translate/useTranslation';
import useModuleMap from '../store/actions/moduleMapAction';
-import bc from '../services/breathecode';
-import { reportDatalayer } from '../../utils/requests';
function useModuleHandler() {
- const { t } = useTranslation('alert-message');
- const toast = useToast();
- const { setTaskTodo, setCohortProgram, state, setCurrentTask, setSubTasks, setNextModule, setPrevModule } = useModuleMap();
- const { taskTodo } = state;
-
- const updateAssignment = async ({
- task, closeSettings, githubUrl, taskStatus,
- }) => {
- // Task case
- const { cohort, ...taskData } = task;
- const toggleStatus = (task.task_status === undefined || task.task_status === 'PENDING') ? 'DONE' : 'PENDING';
- if (task.task_type && task.task_type !== 'PROJECT') {
- const taskToUpdate = {
- ...taskData,
- id: taskData.id,
- task_status: toggleStatus,
- };
-
- try {
- await bc.todo().update(taskToUpdate);
- const keyIndex = taskTodo.findIndex((x) => x.id === task.id);
- setTaskTodo([
- ...taskTodo.slice(0, keyIndex), // before keyIndex (inclusive)
- taskToUpdate, // key item (updated)
- ...taskTodo.slice(keyIndex + 1), // after keyIndex (exclusive)
- ]);
- toast({
- position: 'top',
- title: t('alert-message:assignment-updated'),
- status: 'success',
- duration: 6000,
- isClosable: true,
- });
- closeSettings();
- } catch (error) {
- console.log(error);
- toast({
- position: 'top',
- title: t('alert-message:assignment-update-error'),
- status: 'error',
- duration: 5000,
- isClosable: true,
- });
- closeSettings();
- }
- } else {
- // Project case
- const getProjectUrl = () => {
- if (githubUrl) {
- return githubUrl;
- }
- return '';
- };
-
- const projectUrl = getProjectUrl();
-
- const isDelivering = projectUrl !== '';
- // const linkIsRemoved = task.task_type === 'PROJECT' && !isDelivering;
- const taskToUpdate = {
- ...taskData,
- task_status: taskStatus || toggleStatus,
- github_url: projectUrl,
- revision_status: 'PENDING',
- delivered_at: new Date(),
- };
-
- try {
- const response = await bc.todo({}).update(taskToUpdate);
- // verify if form is equal to the response
- if (response.data.github_url === projectUrl) {
- const keyIndex = taskTodo.findIndex((x) => x.id === task.id);
- setTaskTodo([
- ...taskTodo.slice(0, keyIndex), // before keyIndex (inclusive)
- taskToUpdate, // key item (updated)
- ...taskTodo.slice(keyIndex + 1), // after keyIndex (exclusive)
- ]);
- reportDatalayer({
- dataLayer: {
- event: 'assignment_status_updated',
- task_status: taskStatus,
- task_id: task.id,
- task_title: task.title,
- task_associated_slug: task.associated_slug,
- task_type: task.task_type,
- task_revision_status: task.revision_status,
- },
- });
- toast({
- position: 'top',
- title: isDelivering
- ? t('alert-message:delivery-success')
- : t('alert-message:delivery-removed'),
- status: 'success',
- duration: 6000,
- isClosable: true,
- });
- closeSettings();
- }
- } catch (error) {
- console.log(error);
- toast({
- position: 'top',
- title: t('alert-message:delivery-error'),
- status: 'error',
- duration: 5000,
- isClosable: true,
- });
- closeSettings();
- }
- }
- };
-
- const startDay = async ({
- newTasks, label, customHandler = () => {},
- }) => {
- try {
- const response = await bc.todo({}).add(newTasks);
-
- if (response.status < 400) {
- toast({
- position: 'top',
- title: label
- ? t('alert-message:module-started', { title: label })
- : t('alert-message:module-sync-success'),
- status: 'success',
- duration: 6000,
- isClosable: true,
- });
- setTaskTodo([
- ...taskTodo,
- ...response.data,
- ]);
- customHandler();
- }
- } catch (err) {
- console.log('error_ADD_TASK 🔴 ', err);
- toast({
- position: 'top',
- title: t('alert-message:module-start-error'),
- status: 'error',
- duration: 6000,
- isClosable: true,
- });
- }
- };
+ const { state, setCurrentTask, setSubTasks, setNextModule, setPrevModule } = useModuleMap();
return {
- updateAssignment,
- startDay,
- setTaskTodo,
- setCohortProgram,
setCurrentTask,
setSubTasks,
setNextModule,
diff --git a/src/common/store/actions/cohortAction.js b/src/common/store/actions/cohortAction.js
index 1f188bcff..a635838c8 100644
--- a/src/common/store/actions/cohortAction.js
+++ b/src/common/store/actions/cohortAction.js
@@ -2,9 +2,9 @@ import { useDispatch, useSelector } from 'react-redux';
import {
SET_MY_COHORTS,
SET_COHORT_SESSION,
- SET_SORTED_ASSIGNMENTS,
SET_TASK_COHORT_NULL,
SET_USER_CAPABILITIES,
+ SET_COHORTS_ASSIGNMENTS,
} from '../types';
import { usePersistent } from '../../hooks/usePersistent';
@@ -41,20 +41,20 @@ const useCohortAction = () => {
});
};
- const setSortedAssignments = (payload) => {
+ const setUserCapabilities = (paylaod) => {
dispatch({
- type: SET_SORTED_ASSIGNMENTS,
+ type: SET_USER_CAPABILITIES,
payload: {
- sortedAssignments: payload,
+ userCapabilities: paylaod,
},
});
};
- const setUserCapabilities = (paylaod) => {
+ const setCohortsAssingments = (paylaod) => {
dispatch({
- type: SET_USER_CAPABILITIES,
+ type: SET_COHORTS_ASSIGNMENTS,
payload: {
- userCapabilities: paylaod,
+ cohortsAssignments: paylaod,
},
});
};
@@ -64,8 +64,8 @@ const useCohortAction = () => {
setMyCohorts,
setCohortSession,
setTaskCohortNull,
- setSortedAssignments,
setUserCapabilities,
+ setCohortsAssingments,
};
};
diff --git a/src/common/store/actions/moduleMapAction.js b/src/common/store/actions/moduleMapAction.js
index f89770730..5236278d7 100644
--- a/src/common/store/actions/moduleMapAction.js
+++ b/src/common/store/actions/moduleMapAction.js
@@ -4,20 +4,6 @@ const useModuleMap = () => {
const dispatch = useDispatch();
const state = useSelector((reducerState) => reducerState.moduleMapReducer);
- const setTaskTodo = (newState) => {
- dispatch({
- type: 'CHANGE_TASK_TO_DO',
- payload: newState,
- });
- };
-
- const setCohortProgram = (newState) => {
- dispatch({
- type: 'CHANGE_COHORT_PROGRAM',
- payload: newState,
- });
- };
-
const setCurrentTask = (newState) => {
dispatch({
type: 'CHANGE_CURRENT_TASK',
@@ -47,8 +33,6 @@ const useModuleMap = () => {
};
return {
- setTaskTodo,
- setCohortProgram,
setCurrentTask,
setSubTasks,
setNextModule,
diff --git a/src/common/store/reducers/cohortReducer.js b/src/common/store/reducers/cohortReducer.js
index 05a430257..6d0f9af55 100644
--- a/src/common/store/reducers/cohortReducer.js
+++ b/src/common/store/reducers/cohortReducer.js
@@ -1,15 +1,15 @@
import {
SET_MY_COHORTS,
SET_COHORT_SESSION,
- SET_SORTED_ASSIGNMENTS,
SET_TASK_COHORT_NULL,
SET_USER_CAPABILITIES,
+ SET_COHORTS_ASSIGNMENTS,
} from '../types';
const initialState = {
myCohorts: [],
- cohortSession: {},
- sortedAssignments: [],
+ cohortSession: null,
+ cohortsAssignments: {},
taskCohortNull: [],
userCapabilities: [],
};
@@ -30,13 +30,6 @@ const cohortHandlerReducer = (state = initialState, action) => {
cohortSession,
};
}
- case SET_SORTED_ASSIGNMENTS: {
- const { sortedAssignments } = action.payload;
- return {
- ...state,
- sortedAssignments,
- };
- }
case SET_TASK_COHORT_NULL: {
const { taskCohortNull } = action.payload;
return {
@@ -51,6 +44,13 @@ const cohortHandlerReducer = (state = initialState, action) => {
userCapabilities,
};
}
+ case SET_COHORTS_ASSIGNMENTS: {
+ const { cohortsAssignments } = action.payload;
+ return {
+ ...state,
+ cohortsAssignments,
+ };
+ }
default: {
return state;
}
diff --git a/src/common/store/reducers/moduleMapReducer.js b/src/common/store/reducers/moduleMapReducer.js
index 5a036bc36..459c8be3c 100644
--- a/src/common/store/reducers/moduleMapReducer.js
+++ b/src/common/store/reducers/moduleMapReducer.js
@@ -1,6 +1,4 @@
const initialState = {
- cohortProgram: {},
- taskTodo: [],
currentTask: null,
subTasks: [],
nextModule: null,
@@ -9,16 +7,6 @@ const initialState = {
const moduleMapReducer = (state = initialState, action) => {
switch (action.type) {
- case 'CHANGE_TASK_TO_DO':
- return {
- ...state,
- taskTodo: action.payload,
- };
- case 'CHANGE_COHORT_PROGRAM':
- return {
- ...state,
- cohortProgram: action.payload,
- };
case 'CHANGE_CURRENT_TASK':
return {
...state,
diff --git a/src/common/store/types/index.js b/src/common/store/types/index.js
index e2d5526d3..f3b2c7253 100644
--- a/src/common/store/types/index.js
+++ b/src/common/store/types/index.js
@@ -22,8 +22,8 @@ const SET_SUBMITTING_CARD = 'SET_SUBMITTING_CARD';
const SET_SUBMITTING_PAYMENT = 'SET_SUBMITTING_PAYMENT';
const SET_SELF_APPLIED_COUPON = 'SET_SELF_APPLIED_COUPON';
const SET_MY_COHORTS = 'SET_MY_COHORTS';
+const SET_COHORTS_ASSIGNMENTS = 'SET_COHORTS_ASSIGNMENTS';
const SET_COHORT_SESSION = 'SET_COHORT_SESSION';
-const SET_SORTED_ASSIGNMENTS = 'SET_SORTED_ASSIGNMENTS';
const SET_TASK_COHORT_NULL = 'SET_TASK_COHORT_NULL';
const SET_USER_CAPABILITIES = 'SET_USER_CAPABILITIES';
@@ -55,8 +55,8 @@ export {
SET_SELECTED_SERVICE,
SET_PAYMENT_METHODS,
SET_MY_COHORTS,
+ SET_COHORTS_ASSIGNMENTS,
SET_COHORT_SESSION,
- SET_SORTED_ASSIGNMENTS,
SET_TASK_COHORT_NULL,
SET_USER_CAPABILITIES,
SET_SELF_APPLIED_COUPON,
diff --git a/src/js_modules/Cohort/CohortModules.jsx b/src/js_modules/Cohort/CohortModules.jsx
new file mode 100644
index 000000000..2a718a05b
--- /dev/null
+++ b/src/js_modules/Cohort/CohortModules.jsx
@@ -0,0 +1,480 @@
+/* eslint-disable no-unused-vars */
+import React, { useRef, useMemo, useState, useEffect } from 'react';
+import {
+ Box,
+ Button,
+ useColorMode,
+ Spinner,
+ CircularProgress,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
+} from '@chakra-ui/react';
+import { useRouter } from 'next/router';
+import useTranslation from 'next-translate/useTranslation';
+import PropTypes from 'prop-types';
+import { format } from 'date-fns';
+import { es, en } from 'date-fns/locale';
+import { useReward } from 'react-rewards';
+import useCohortHandler from '../../common/hooks/useCohortHandler';
+import useStyle from '../../common/hooks/useStyle';
+import Heading from '../../common/components/Heading';
+import ShareButton from '../../common/components/ShareButton';
+import Text from '../../common/components/Text';
+import Icon from '../../common/components/Icon';
+import Progress from '../../common/components/ProgressBar/Progress';
+
+const locales = { es, en };
+
+function CohortModules({ cohort, modules, mainCohort, certificate, openByDefault }) {
+ const containerRef = useRef(null);
+ const { reward } = useReward(cohort.slug, 'confetti', {
+ lifetime: 50,
+ zIndex: 100,
+ spread: 50,
+ position: 'absolute',
+ });
+ const { t, lang } = useTranslation('dashboard');
+ const langDict = {
+ en: 'us',
+ us: 'us',
+ };
+ const [loadingStartCourse, setLoadingStartCourse] = useState(false);
+ const [loadingModule, setLoadingModule] = useState(null);
+ const [shareModal, setShareModal] = useState(false);
+ const router = useRouter();
+ const { backgroundColor, hexColor } = useStyle();
+ const { colorMode } = useColorMode();
+ const { serializeModulesMap, startDay, cohortsAssignments, setCohortsAssingments } = useCohortHandler();
+
+ const cohortColor = cohort.color || hexColor.blueDefault;
+ const isGraduated = !!certificate;
+
+ const getModuleLabel = (module) => {
+ if (typeof module.label === 'string') return module.label;
+ if (lang in module.label) return module.label[lang];
+ return module.label[langDict[lang]];
+ };
+
+ const getModulesProgress = (acc, curr) => {
+ if (!(curr.task_type in acc)) {
+ acc[curr.task_type] = {
+ total: 1,
+ icon: curr.icon,
+ done: curr.task_status === 'DONE' ? 1 : 0,
+ };
+ } else {
+ acc[curr.task_type].total += 1;
+ if (curr.task_status === 'DONE') acc[curr.task_type].done += 1;
+ }
+ return acc;
+ };
+
+ const modulesProgress = useMemo(() => {
+ if (!modules || !Array.isArray(modules)) return null;
+
+ const modulesDict = {};
+ modules.forEach((module) => {
+ const assignmentsCount = module.content.reduce(getModulesProgress, {});
+
+ const typesPerModule = Object.keys(assignmentsCount);
+ const moduleTotalAssignments = typesPerModule.reduce((acc, curr) => assignmentsCount[curr].total + acc, 0);
+ const moduleDoneAssignments = typesPerModule.reduce((acc, curr) => assignmentsCount[curr].done + acc, 0);
+ const isStarted = module.filteredContent.length > 0;
+ modulesDict[module.id] = {
+ moduleTotalAssignments,
+ moduleDoneAssignments,
+ assignmentsCount,
+ isStarted,
+ };
+ });
+
+ return modulesDict;
+ }, [modules]);
+
+ const cohortProgress = useMemo(() => {
+ if (!modulesProgress) return null;
+
+ const allModules = Object.values(modulesProgress);
+ const totalAssignments = allModules.reduce((acc, curr) => curr.moduleTotalAssignments + acc, 0);
+ const doneAssignments = allModules.reduce((acc, curr) => curr.moduleDoneAssignments + acc, 0);
+
+ const percentage = cohort.cohort_user.educational_status === 'GRADUATED' ? 100 : Math.floor((doneAssignments * 100) / 100);
+
+ const isCohortStarted = allModules.some((module) => module.isStarted);
+
+ return {
+ totalAssignments,
+ doneAssignments,
+ percentage,
+ isCohortStarted,
+ };
+ }, [modulesProgress]);
+
+ const updateMicroCohortModules = (tasks) => {
+ const cohortModulesUpdated = serializeModulesMap(cohortsAssignments[cohort.slug].syllabus.json.days, tasks);
+ const allMicroCohortAssignments = {
+ ...cohortsAssignments,
+ [cohort.slug]: {
+ ...cohortsAssignments[cohort.slug],
+ modules: cohortModulesUpdated,
+ tasks: [...cohortsAssignments[cohort.slug].tasks, tasks],
+ },
+ };
+
+ setCohortsAssingments(allMicroCohortAssignments);
+ };
+
+ const startCourse = async () => {
+ try {
+ const firstModule = modules[0];
+
+ const moduleToUpdate = firstModule?.content;
+ const newTasks = moduleToUpdate?.map((l) => ({
+ ...l,
+ associated_slug: l.slug,
+ cohort: cohort.id,
+ }));
+
+ setLoadingStartCourse(true);
+ await startDay({ newTasks, cohort });
+ setLoadingStartCourse(false);
+ } catch (e) {
+ console.log(e);
+ setLoadingStartCourse(false);
+ }
+ };
+
+ const getColorVariations = (colorHex) => {
+ if (!colorHex) return {};
+ const lightRange = [0.2, 0.3, 0.5, 0.8, 0.9];
+ const darkRange = [0.2, 0.3, 0.4, 0.7, 0.8];
+ const r = parseInt(colorHex.slice(1, 3), 16); // r = 102
+ const g = parseInt(colorHex.slice(3, 5), 16); // g = 51
+ const b = parseInt(colorHex.slice(5, 7), 16); // b = 153
+
+ const [light1, light2, light3, light4, light5] = lightRange.map((variation) => {
+ const tintR = Math.round(Math.min(255, r + (255 - r) * variation)); // 117
+ const tintG = Math.round(Math.min(255, g + (255 - g) * variation)); // 71
+ const tintB = Math.round(Math.min(255, b + (255 - b) * variation)); // 163
+
+ return `#${
+ [tintR, tintG, tintB]
+ .map((x) => x.toString(16).padStart(2, '0'))
+ .join('')}`;
+ });
+
+ const [dark1, dark2, dark3, dark4, dark5] = darkRange.map((variation) => {
+ const shadeR = Math.round(Math.max(0, r - r * variation)); // 92
+ const shadeG = Math.round(Math.max(0, g - g * variation)); // 46
+ const shadeB = Math.round(Math.max(0, b - b * variation)); // 138
+
+ return `#${
+ [shadeR, shadeG, shadeB]
+ .map((x) => x.toString(16).padStart(2, '0'))
+ .join('')}`;
+ });
+
+ return {
+ light: {
+ mode1: light1, mode2: light2, mode3: light3, mode4: light4, mode5: light5,
+ },
+ dark: {
+ mode1: dark1, mode2: dark2, mode3: dark3, mode4: dark4, mode5: dark5,
+ },
+ };
+ };
+
+ const colorVariations = getColorVariations(cohortColor);
+
+ const redirectToModule = async (module) => {
+ try {
+ const { isStarted } = modulesProgress[module.id];
+ //start module
+ if (!isStarted) {
+ const moduleToUpdate = module?.content;
+ const newTasks = moduleToUpdate?.map((l) => ({
+ ...l,
+ associated_slug: l.slug,
+ cohort: cohort.id,
+ }));
+
+ setLoadingModule(module.id);
+ await startDay({ newTasks, cohort });
+ setLoadingModule(null);
+ }
+
+ const moduleFirstAssignment = module?.content[0];
+
+ let syllabusRoute = `/syllabus/${cohort.slug}/${moduleFirstAssignment.type.toLowerCase()}/${moduleFirstAssignment.slug}`;
+ if (mainCohort) syllabusRoute = `/main-cohort/${mainCohort.slug}/${syllabusRoute}`;
+
+ router.push(syllabusRoute);
+ } catch (e) {
+ console.log(e);
+ setLoadingModule(null);
+ }
+ };
+
+ const progressBoxStyles = () => {
+ if (!isGraduated) {
+ return {
+ paddingY: '8px',
+ background: colorVariations[colorMode].mode4,
+ };
+ }
+
+ return {
+ display: 'flex',
+ flexDirection: 'column',
+ width: '100%',
+ justifyContent: 'space-between',
+ };
+ };
+
+ const certfToken = certificate?.preview_url && certificate.preview_url?.split('/')?.pop();
+ const certfLink = certfToken ? `https://certificate.4geeks.com/${certfToken}` : '#';
+ const profession = certificate?.specialty.name;
+ const socials = t('profile:share-certificate.socials', { certfLink, profession }, { returnObjects: true });
+
+ const share = (e) => {
+ e.stopPropagation();
+ setShareModal(true);
+ };
+
+ const showCertificate = (e) => {
+ e.stopPropagation();
+ window.open(certfLink);
+ };
+
+ useEffect(() => {
+ if (certificate) {
+ setTimeout(() => {
+ reward();
+ }, 1500);
+ }
+ }, [certificate]);
+
+ return (
+
+
+ {({ isExpanded }) => (
+ <>
+
+
+
+
+
+
+ {cohort.name}
+
+
+
+ {isGraduated && (
+
+
+ {t('completed')}
+
+
+ )}
+
+
+ {t('modules-count', { count: modules?.length })}
+
+
+
+
+ {cohortProgress?.isCohortStarted && (
+
+
+ {t(isExpanded ? 'hide-content' : 'show-content')}
+
+
+
+ )}
+
+
+ {isGraduated && (
+
+
+ {t('completed')}
+
+
+ )}
+
+
+ {t('modules-count', { count: modules?.length })}
+
+
+
+
+ {isGraduated && (
+
+
+
+ {t('open')}
+
+
+ )}
+ {cohortProgress?.isCohortStarted && (
+
+
+
+
+ {t('path-to-claim')}
+
+
+
+ {cohortProgress.percentage !== 100 ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ {`${cohortProgress.percentage}%`}
+
+
+
+ {isGraduated && (
+
+
+
+
+ {t('hours-worked', { hours: cohort.syllabus_version.duration_in_hours })}
+
+
+
+
+
+ {t('issued-on', { date: format(new Date(certificate.issued_at), 'MMMM d y', {
+ locale: locales[lang],
+ }) })}
+
+
+ {shareModal && (
+ setShareModal(false)}
+ />
+ )}
+
+
+ )}
+
+ )}
+
+
+ {cohortProgress?.isCohortStarted ? (
+
+ {modules?.map((module) => {
+ const assignmentsCount = modulesProgress?.[module.id].assignmentsCount;
+ const moduleTotalAssignments = modulesProgress?.[module.id].moduleTotalAssignments;
+ const moduleDoneAssignments = modulesProgress?.[module.id].moduleDoneAssignments;
+
+ const typesPerModule = Object.keys(assignmentsCount);
+
+ return (
+ redirectToModule(module)} background={backgroundColor} cursor="pointer" _hover={{ opacity: 0.7 }} display="flex" alignItems="center" justifyContent="space-between" padding="8px" borderRadius="8px">
+
+ {loadingModule === module.id ? (
+
+ ) : (
+ <>
+ {moduleTotalAssignments === moduleDoneAssignments ? (
+
+ ) : (
+
+ )}
+ >
+ )}
+
+ {getModuleLabel(module)}
+
+
+
+ {typesPerModule.map((taskType) => {
+ const { icon, total, done } = assignmentsCount[taskType];
+ return (
+
+
+
+ {`${done}/`}
+ {total}
+
+ {done === total && }
+
+ );
+ })}
+
+
+ );
+ })}
+
+ ) : (
+
+ )}
+ >
+ )}
+
+
+ );
+}
+
+export default CohortModules;
+
+CohortModules.propTypes = {
+ cohort: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.any])).isRequired,
+ modules: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.any])),
+ mainCohort: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.any])),
+ certificate: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.any])),
+ openByDefault: PropTypes.bool,
+};
+
+CohortModules.defaultProps = {
+ mainCohort: null,
+ modules: null,
+ certificate: null,
+ openByDefault: false,
+};
diff --git a/src/js_modules/Cohort/Header.jsx b/src/js_modules/Cohort/Header.jsx
new file mode 100644
index 000000000..4799229a7
--- /dev/null
+++ b/src/js_modules/Cohort/Header.jsx
@@ -0,0 +1,175 @@
+/* eslint-disable no-unused-vars */
+import React, { useState, useEffect } from 'react';
+import {
+ Flex, Box, Container,
+} from '@chakra-ui/react';
+import { useRouter } from 'next/router';
+import useTranslation from 'next-translate/useTranslation';
+import bc from '../../common/services/breathecode';
+import useAuth from '../../common/hooks/useAuth';
+import useCohortHandler from '../../common/hooks/useCohortHandler';
+import useStyle from '../../common/hooks/useStyle';
+import useRigo from '../../common/hooks/useRigo';
+import { SimpleSkeleton } from '../../common/components/Skeleton';
+import Heading from '../../common/components/Heading';
+import Text from '../../common/components/Text';
+import Icon from '../../common/components/Icon';
+import StudentsModal from './StudentsModal';
+import { ProfilesSection } from '../../common/components/SupportSidebar/MentoringConsumables';
+import { BREATHECODE_HOST } from '../../utils/variables';
+import { getStorageItem } from '../../utils';
+
+// eslint-disable-next-line react/prop-types
+function CustomButton({ children, ...props }) {
+ const { backgroundColor, backgroundColor4 } = useStyle();
+ return (
+
+ {children}
+
+ );
+}
+
+function Header() {
+ const { t } = useTranslation('choose-program');
+ const router = useRouter();
+ const { user, isAuthenticatedWithRigobot, conntectToRigobot } = useAuth();
+ const { rigo, isRigoInitialized } = useRigo();
+ const { featuredLight, hexColor } = useStyle();
+ const { cohortSession } = useCohortHandler();
+ const [mentors, setMentors] = useState([]);
+ const [showStudentsModal, setShowStudentsModal] = useState(false);
+
+ const fetchServices = async () => {
+ try {
+ const { data } = await bc.mentorship({
+ status: 'ACTIVE',
+ academy: cohortSession?.academy?.id,
+ }).getMentor();
+ setMentors(data);
+ } catch (e) {
+ console.log(e);
+ }
+ };
+
+ useEffect(() => {
+ if (cohortSession && cohortSession.cohort_role === 'STUDENT') fetchServices();
+ }, [cohortSession]);
+
+ const hasGithub = user?.github && user.github.username !== '';
+
+ const getRigobotButtonText = () => {
+ if (!hasGithub) return t('common:connect-with-github');
+ if (!isAuthenticatedWithRigobot) return t('common:connect-with-tigobot');
+
+ return t('common:get-help-rigobot');
+ };
+
+ const rigobotMessage = () => {
+ if (!hasGithub) {
+ const accessToken = getStorageItem('accessToken');
+ window.location.href = `${BREATHECODE_HOST}/v1/auth/github/${accessToken}?url=${window.location.href}`;
+ } else if (!isAuthenticatedWithRigobot) {
+ conntectToRigobot();
+ } else {
+ rigo.updateOptions({
+ showBubble: true,
+ // highlight: true,
+ welcomeMessage: t('rigo-chat.welcome-message', { firstName: user?.first_name, cohortName: cohortSession?.name }),
+ collapsed: false,
+ purposeSlug: '4geekscom-public-agent',
+ });
+ }
+ };
+
+ return (
+
+ {cohortSession ? (
+
+
+
+ {t('hello-user', { name: user?.first_name })}
+
+
+ {t('read-to-start-learning')}
+
+
+
+ {cohortSession.cohort_role === 'STUDENT' ? (
+ <>
+ router.push('/workshops')}>
+
+
+ {t('common:see-workshops')}
+
+
+
+ router.push('/mentorship/schedule')}>
+
+
+ {t('common:schedule-mentoring')}
+
+
+
+ {isRigoInitialized && (
+
+
+
+ {getRigobotButtonText()}
+
+
+ )}
+ >
+ ) : (
+ <>
+ setShowStudentsModal(true)}>
+
+
+ {t('dashboard:teacher-sidebar.student-progress')}
+
+
+ window.open(`/cohort/${cohortSession?.slug}/assignments?academy=${cohortSession?.academy?.id}`, '_blank')}>
+
+
+ {t('dashboard:teacher-sidebar.assignments')}
+
+
+ window.open('https://www.notion.so/4geeksacademy/Mentor-training-433451eb9dac4dc680b7c5dae1796519', '_blank')}>
+
+
+ {t('dashboard:teacher-sidebar.teacher-tutorial')}
+
+
+ >
+ )}
+
+
+ ) : (
+
+ )}
+ {cohortSession && cohortSession.cohort_role !== 'STUDENT' && (
+ setShowStudentsModal(false)} />
+ )}
+
+ );
+}
+
+export default Header;
diff --git a/src/js_modules/Cohort/StudentsModal.jsx b/src/js_modules/Cohort/StudentsModal.jsx
new file mode 100644
index 000000000..4635c408a
--- /dev/null
+++ b/src/js_modules/Cohort/StudentsModal.jsx
@@ -0,0 +1,154 @@
+import React, { useEffect, useState } from 'react';
+import useTranslation from 'next-translate/useTranslation';
+import PropTypes from 'prop-types';
+import {
+ Modal, ModalOverlay, ModalContent, ModalHeader, ModalFooter, ModalBody, Button, Box,
+ FormControl, FormLabel, Input, Flex, Grid, Avatar, Spinner,
+ useColorMode, ModalCloseButton,
+} from '@chakra-ui/react';
+import Text from '../../common/components/Text';
+import bc from '../../common/services/breathecode';
+import useStyle from '../../common/hooks/useStyle';
+import useCohortHandler from '../../common/hooks/useCohortHandler';
+
+function StudentsModal({
+ isOpen, onClose,
+}) {
+ const { t } = useTranslation('dashboard');
+ const { state } = useCohortHandler();
+ const { cohortSession } = state;
+ const [students, setStudents] = useState([]);
+ const [studentsCount, setStudentsCount] = useState(0);
+ const [filterStudent, setFilterStudent] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const { colorMode } = useColorMode();
+
+ const { hexColor, lightColor, borderColor } = useStyle();
+
+ const loadStudents = async (offset, append = false, like) => {
+ try {
+ setIsLoading(true);
+ const { data } = await bc.cohort({ offset, limit: 10, like }).getStudents(cohortSession.slug);
+
+ const { count, results } = data;
+ setStudentsCount(parseInt(count, 10));
+ if (append) setStudents((prev) => [...prev, ...results]);
+ else setStudents(results);
+ } catch (e) {
+ console.log(e);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ if (cohortSession?.cohort_role !== 'STUDENT' && students.length === 0) loadStudents();
+ }, [cohortSession]);
+
+ const handleFilterChange = (e) => {
+ setFilterStudent(e.target.value);
+ };
+
+ useEffect(() => {
+ let timeoutId;
+ if (!filterStudent) loadStudents(0, false);
+ else timeoutId = setTimeout(() => loadStudents(0, false, filterStudent), 1000);
+ return () => clearTimeout(timeoutId);
+ }, [filterStudent]);
+
+ return (
+
+
+
+
+ {t('dashboard:students-modal.students-course')}
+
+
+
+
+ {t('dashboard:students-modal.select-student')}
+
+
+
+ {t('dashboard:students-modal.student')}
+
+
+
+
+
+
+ {students.map(({ user }) => (
+ {
+ window.open(`/cohort/${cohortSession?.slug}/student/${user.id}?academy=${cohortSession?.academy?.id}`, '_blank');
+ }}
+ >
+
+
+
+ {`${user.first_name} ${user.last_name}`}
+
+
+
+ ))}
+
+ {!isLoading && students.length === 0 && (
+
+
+ {t('dashboard:students-modal.no-students')}
+
+
+ )}
+ {isLoading && (
+
+
+
+ )}
+
+ {!isLoading && studentsCount !== students.length && (
+
+ )}
+
+
+
+ {t('attendance-modal.showing-students-with-active-educational-status')}
+
+
+
+
+ );
+}
+
+StudentsModal.propTypes = {
+ isOpen: PropTypes.bool,
+ onClose: PropTypes.func,
+};
+StudentsModal.defaultProps = {
+ isOpen: true,
+ onClose: () => { },
+};
+
+export default StudentsModal;
diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx
index c02621477..f9312c676 100644
--- a/src/js_modules/checkout/PaymentInfo.jsx
+++ b/src/js_modules/checkout/PaymentInfo.jsx
@@ -14,7 +14,6 @@ import useAuth from '../../common/hooks/useAuth';
import { reportDatalayer } from '../../utils/requests';
import { getQueryString, getStorageItem } from '../../utils';
import useCohortHandler from '../../common/hooks/useCohortHandler';
-import useModuleHandler from '../../common/hooks/useModuleHandler';
import { getCohort } from '../../common/handlers/cohorts';
import axiosInstance from '../../axios';
import { getAllMySubscriptions } from '../../common/handlers/subscriptions';
@@ -36,9 +35,7 @@ function PaymentInfo() {
const {
checkoutData, selectedPlanCheckoutData, cohortPlans, paymentMethods, loader, isSubmittingPayment, paymentStatus,
} = state;
- const { state: cohortState, setCohortSession, getCohortAssignments, prepareTasks } = useCohortHandler();
- const { sortedAssignments } = cohortState;
- const { cohortProgram, taskTodo, startDay } = useModuleHandler();
+ const { cohortsAssignments, getCohortsModules, startDay, setCohortSession } = useCohortHandler();
const [readyToRedirect, setReadyToRedirect] = useState(false);
const [isRedirecting, setIsRedirecting] = useState(false);
const [updatedUser, setUpdatedUser] = useState(undefined);
@@ -63,11 +60,14 @@ function PaymentInfo() {
const openSyllabusAndRedirect = () => {
const langLink = lang !== 'en' ? `/${lang}` : '';
- const firstAssigmentSlug = sortedAssignments[0].modules[0].slug;
- const firstAssigmentType = sortedAssignments[0].modules[0].type.toLowerCase();
+
+ const modules = cohortsAssignments[cohortFound.slug]?.modules;
+
+ const firstAssigmentSlug = modules[0].content[0].slug;
+ const firstAssigmentType = modules[0].content[0].type.toLowerCase();
const syllabusRedirectURL = `${langLink}/syllabus/${cohortFound?.slug}/${firstAssigmentType}/${firstAssigmentSlug}`;
- const updatedTasks = (sortedAssignments[0].modules || [])?.map((l) => ({
+ const updatedTasks = (modules[0].content || [])?.map((l) => ({
...l,
title: l.title,
associated_slug: l?.slug?.slug || l.slug,
@@ -83,6 +83,7 @@ function PaymentInfo() {
},
});
startDay({
+ cohort: cohortFound,
newTasks: updatedTasks,
});
@@ -105,7 +106,7 @@ function PaymentInfo() {
selectedProgramSlug: cohortDashboardLink,
});
- if (!sortedAssignments.length > 0) {
+ if (cohortFound?.micro_cohorts?.length > 0 || !(cohortFound.slug in cohortsAssignments)) {
router.push(cohortDashboardLink);
return;
}
@@ -114,23 +115,19 @@ function PaymentInfo() {
};
useEffect(() => {
- if (!(sortedAssignments.length > 0)) return undefined;
+ if (!cohortFound || (cohortFound.micro_cohorts.length === 0 && !(cohortFound.slug in cohortsAssignments))) return undefined;
const timer = setTimeout(() => {
setReadyToRedirect(true);
}, 1000);
return () => clearTimeout(timer);
- }, [sortedAssignments]);
-
- useEffect(() => {
- prepareTasks();
- }, [taskTodo, cohortProgram]);
+ }, [cohortsAssignments, cohortFound]);
useEffect(() => {
- getCohortAssignments(
- { slug: cohortFound?.syllabus_version?.slug, cohort: cohortFound, updatedUser },
- );
+ if (cohortFound?.micro_cohorts.length === 0) {
+ getCohortsModules([cohortFound]);
+ }
}, [updatedUser]);
useEffect(() => {
@@ -147,37 +144,36 @@ function PaymentInfo() {
fetchMyCohorts();
}, [cohortFound]);
- const joinCohort = (cohort) => {
- reportDatalayer({
- dataLayer: {
- event: 'join_cohort',
- cohort_id: cohort?.id,
- },
- });
- bc.cohort().join(cohort?.id)
- .then(async (resp) => {
- const dataRequested = await resp.json();
- if (resp.status >= 400) {
- toast({
- position: 'top',
- title: dataRequested?.detail,
- status: 'info',
- duration: 5000,
- isClosable: true,
- });
- setReadyToRefetch(false);
- }
- if (dataRequested?.status === 'ACTIVE') {
- setCohortFound(cohort);
- }
- })
- .catch((error) => {
- console.error('Error al unirse a la cohorte:', error);
- setIsSubmittingPayment(false);
- setTimeout(() => {
- setReadyToRefetch(false);
- }, 600);
+ const joinCohort = async (cohort) => {
+ try {
+ reportDatalayer({
+ dataLayer: {
+ event: 'join_cohort',
+ cohort_id: cohort?.id,
+ },
});
+ const resp = await bc.cohort().join(cohort?.id);
+ const dataRequested = await resp.json();
+ if (resp.status >= 400) {
+ toast({
+ position: 'top',
+ title: dataRequested?.detail,
+ status: 'info',
+ duration: 5000,
+ isClosable: true,
+ });
+ setReadyToRefetch(false);
+ }
+ if (dataRequested?.status === 'ACTIVE') {
+ setCohortFound(cohort);
+ }
+ } catch (error) {
+ console.error('Error al unirse a la cohorte:', error);
+ setIsSubmittingPayment(false);
+ setTimeout(() => {
+ setReadyToRefetch(false);
+ }, 600);
+ }
};
useEffect(() => {
@@ -194,7 +190,7 @@ function PaymentInfo() {
if (readyToRefetch && timeElapsed < 10 && isPaymentSuccess) {
interval = setInterval(() => {
getAllMySubscriptions()
- .then((subscriptions) => {
+ .then(async (subscriptions) => {
const currentSubscription = subscriptions?.find(
(subscription) => checkoutData?.plans[0]?.plan_slug === subscription.plans[0]?.slug,
);
@@ -202,19 +198,16 @@ function PaymentInfo() {
(subscription) => checkoutData?.plans[0]?.plan_slug === subscription.plans[0]?.slug,
);
const cohortsForSubscription = currentSubscription?.selected_cohort_set.cohorts;
- const findedCohort = cohortsForSubscription?.length > 0 ? cohortsForSubscription.find(
+ const foundCohort = cohortsForSubscription?.find(
(cohort) => cohort?.id === cohortId,
- ) : {};
+ );
if (isPurchasedPlanFound) {
- if (findedCohort?.id) {
- getCohort(findedCohort?.id)
- .then((cohort) => {
- joinCohort(cohort);
- })
- .finally(() => {
- clearInterval(interval);
- });
+ if (foundCohort?.id) {
+ const cohort = await getCohort(foundCohort?.id);
+ joinCohort(cohort);
+
+ clearInterval(interval);
setReadyToRefetch(false);
} else {
clearInterval(interval);
diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx
index 6be91f929..88b774bc6 100644
--- a/src/js_modules/checkout/Summary.jsx
+++ b/src/js_modules/checkout/Summary.jsx
@@ -16,14 +16,11 @@ import { getAllMySubscriptions } from '../../common/handlers/subscriptions';
import { SILENT_CODE } from '../../lib/types';
import axiosInstance from '../../axios';
import useCohortHandler from '../../common/hooks/useCohortHandler';
-import useModuleHandler from '../../common/hooks/useModuleHandler';
import { getCohort } from '../../common/handlers/cohorts';
function Summary() {
const { t, lang } = useTranslation('signup');
- const { state: cohortState, setCohortSession, getCohortAssignments, prepareTasks } = useCohortHandler();
- const { sortedAssignments } = cohortState;
- const { cohortProgram, taskTodo, startDay } = useModuleHandler();
+ const { cohortsAssignments, startDay, setCohortSession, getCohortsModules } = useCohortHandler();
const [readyToRedirect, setReadyToRedirect] = useState(false);
const [isRedirecting, setIsRedirecting] = useState(false);
const [updatedUser, setUpdatedUser] = useState(undefined);
@@ -96,11 +93,14 @@ function Summary() {
const openSyllabusAndRedirect = () => {
const langLink = lang !== 'en' ? `/${lang}` : '';
- const firstAssigmentSlug = sortedAssignments[0].modules[0].slug;
- const firstAssigmentType = sortedAssignments[0].modules[0].type.toLowerCase();
+
+ const modules = cohortsAssignments[cohortFound.slug]?.modules;
+
+ const firstAssigmentSlug = modules[0].content[0].slug;
+ const firstAssigmentType = modules[0].content[0].type.toLowerCase();
const syllabusRedirectURL = `${langLink}/syllabus/${cohortFound?.slug}/${firstAssigmentType}/${firstAssigmentSlug}`;
- const updatedTasks = (sortedAssignments[0].modules || [])?.map((l) => ({
+ const updatedTasks = (modules[0].content || [])?.map((l) => ({
...l,
title: l.title,
associated_slug: l?.slug?.slug || l.slug,
@@ -116,6 +116,7 @@ function Summary() {
},
});
startDay({
+ cohort: cohortFound,
newTasks: updatedTasks,
});
@@ -134,7 +135,7 @@ function Summary() {
selectedProgramSlug: cohortDashboardLink,
});
- if (!sortedAssignments.length > 0) {
+ if (cohortFound?.micro_cohorts?.length > 0 || !(cohortFound.slug in cohortsAssignments)) {
router.push(cohortDashboardLink);
return;
}
@@ -143,23 +144,19 @@ function Summary() {
};
useEffect(() => {
- if (!(sortedAssignments.length > 0)) return undefined;
+ if (!cohortFound || (cohortFound.micro_cohorts.length === 0 && !(cohortFound.slug in cohortsAssignments))) return undefined;
const timer = setTimeout(() => {
setReadyToRedirect(true);
}, 1000);
return () => clearTimeout(timer);
- }, [sortedAssignments]);
+ }, [cohortsAssignments, cohortFound]);
useEffect(() => {
- prepareTasks();
- }, [taskTodo, cohortProgram]);
-
- useEffect(() => {
- getCohortAssignments(
- { slug: cohortFound?.syllabus_version?.slug, cohort: cohortFound, updatedUser },
- );
+ if (cohortFound?.micro_cohorts.length === 0) {
+ getCohortsModules([cohortFound]);
+ }
}, [updatedUser]);
useEffect(() => {
diff --git a/src/js_modules/chooseProgram/Programs.jsx b/src/js_modules/chooseProgram/Programs.jsx
index 69c084d28..56ca85753 100644
--- a/src/js_modules/chooseProgram/Programs.jsx
+++ b/src/js_modules/chooseProgram/Programs.jsx
@@ -1,3 +1,4 @@
+/* eslint-disable camelcase */
import PropTypes from 'prop-types';
import { useRouter } from 'next/router';
import { subMinutes } from 'date-fns';
@@ -11,7 +12,7 @@ function Programs({ item, onOpenModal, setLateModalProps }) {
const { setCohortSession } = useCohortHandler();
const [isLoadingPageContent, setIsLoadingPageContent] = useState(false);
const { programsList } = useProgramList();
- const { cohort, ...cohortUser } = item;
+ const { cohort_user: cohortUser, ...cohort } = item;
const signInDate = item.created_at;
const { version, slug } = cohort.syllabus_version;
const currentCohortProps = programsList[cohort.slug];
@@ -97,8 +98,8 @@ function Programs({ item, onOpenModal, setLateModalProps }) {
// isBought={!isFreeTrial}
isLoadingPageContent={isLoadingPageContent}
isLoading={currentCohortProps === undefined}
- startsIn={item?.cohort?.kickoff_date}
- endsAt={item?.cohort?.ending_date}
+ startsIn={item?.kickoff_date}
+ endsAt={item?.ending_date}
signInDate={signInDate}
icon="coding"
subscription={subscription || {}}
diff --git a/src/js_modules/chooseProgram/index.jsx b/src/js_modules/chooseProgram/index.jsx
index 4eba9cac1..f990484ff 100644
--- a/src/js_modules/chooseProgram/index.jsx
+++ b/src/js_modules/chooseProgram/index.jsx
@@ -28,15 +28,13 @@ function ChooseProgram({ chooseList, handleChoose, setLateModalProps }) {
const cardColumnSize = 'repeat(auto-fill, minmax(17rem, 1fr))';
const finishedCohorts = handlers.getCohortsFinished(chooseList);
- const activeCohorts = handlers.getActiveCohorts(chooseList).map((item) => {
- const cohort = item?.cohort;
+ const activeCohorts = handlers.getActiveCohorts(chooseList).map((cohort) => {
+ const { cohort_user: cohortUser } = cohort;
const currentCohortProps = programsList[cohort.slug];
return ({
- ...item,
- cohort: {
- ...cohort,
- available_as_saas: item?.role === 'TEACHER' ? false : cohort?.available_as_saas,
- },
+ ...cohort,
+ available_as_saas: cohortUser?.role === 'TEACHER' ? false : cohort.available_as_saas,
+ cohort_user: { ...cohortUser },
subscription: currentCohortProps?.subscription,
plan_financing: currentCohortProps?.plan_financing,
all_subscriptions: currentCohortProps?.all_subscriptions,
@@ -44,11 +42,11 @@ function ChooseProgram({ chooseList, handleChoose, setLateModalProps }) {
});
});
- const hasNonSaasCourse = chooseList.some(({ cohort }) => !cohort.available_as_saas);
+ const hasNonSaasCourse = chooseList.some((cohort) => !cohort.available_as_saas);
const marketingCourses = marketingCursesList.filter(
(item) => !activeCohorts.some(
- ({ cohort }) => cohort.slug === item?.cohort?.slug,
+ (cohort) => cohort.slug === item?.cohort?.slug,
) && item?.course_translation?.title,
);
diff --git a/src/js_modules/moduleMap/index.jsx b/src/js_modules/moduleMap/index.jsx
index a25a65c6d..1edfa9742 100644
--- a/src/js_modules/moduleMap/index.jsx
+++ b/src/js_modules/moduleMap/index.jsx
@@ -8,25 +8,23 @@ import { reportDatalayer } from '../../utils/requests';
import { languageFix } from '../../utils';
import Text from '../../common/components/Text';
import Module from './module';
-import useModuleHandler from '../../common/hooks/useModuleHandler';
import useCohortHandler from '../../common/hooks/useCohortHandler';
import Icon from '../../common/components/Icon';
function ModuleMap({
- index, slug, modules, filteredModules,
- title, description, cohortData, filteredModulesByPending,
+ index, slug, content, filteredContent,
+ title, description, cohortData, filteredContentByPending,
showPendingTasks, searchValue, existsActivities,
}) {
const { t, lang } = useTranslation('dashboard');
- const { startDay } = useModuleHandler();
- const { state } = useCohortHandler();
+ const { state, startDay } = useCohortHandler();
const { taskCohortNull } = state;
const commonBorderColor = useColorModeValue('gray.200', 'gray.900');
- const currentModules = showPendingTasks ? filteredModulesByPending : filteredModules;
+ const currentModules = showPendingTasks ? filteredContentByPending : filteredContent;
const cohortId = cohortData?.id || cohortData?.cohort_id;
const handleStartDay = () => {
- const updatedTasks = (modules || [])?.map((l) => ({
+ const updatedTasks = (content || [])?.map((l) => ({
...l,
title: l.title,
associated_slug: l?.slug?.slug || l.slug,
@@ -42,25 +40,26 @@ function ModuleMap({
},
});
startDay({
+ cohort: cohortData,
newTasks: updatedTasks,
});
};
const taskCohortNullExistsInModules = taskCohortNull.some((el) => {
- const task = modules.find((l) => l.slug === el.associated_slug);
+ const task = content.find((l) => l.slug === el.associated_slug);
return task;
});
const isAvailableToSync = () => {
if (!taskCohortNullExistsInModules
- && filteredModules.length > 0
+ && filteredContent.length > 0
&& searchValue.length === 0
- && modules.length !== filteredModules.length
+ && content.length !== filteredContent.length
) return true;
return false;
};
- return ((showPendingTasks && filteredModulesByPending !== null) || (showPendingTasks === false)) && (
+ return ((showPendingTasks && filteredContentByPending !== null) || (showPendingTasks === false)) && (
- {t('modules.activitiesLength', { count: filteredModules.length })}
+ {t('modules.activitiesLength', { count: filteredContent.length })}
@@ -88,7 +87,7 @@ function ModuleMap({
{isAvailableToSync() && (
- {t('modules.newActivities.title', { tasksLength: (modules.length - filteredModules.length) })}
+ {t('modules.newActivities.title', { tasksLength: (content.length - filteredContent.length) })}
- {getMandatoryProjects().map((module, i) => (
+ {mandatoryProjects.map((module, i) => (
- >
+ setModalIsOpen(false)}
+ title={t('unsynced.title', { taskLength: taskCohortNull && taskCohortNull.length })}
+ description={t('unsynced.description')}
+ handlerColorButton="blue"
+ rejectHandler={() => removeUnsyncedTasks()}
+ forceHandler
+ rejectData={{
+ title: t('unsynced.reject-unsync-title'),
+ closeText: t('unsynced.cancel'),
+ handlerText: t('unsynced.confirm'),
+ }}
+ closeText={t('unsynced.unsync')}
+ actionHandler={() => syncTaskWithCohort()}
+ handlerText={t('unsynced.sync')}
+ />
+
);
}
diff --git a/src/pages/main-cohort/[mainCohortSlug]/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx b/src/pages/main-cohort/[mainCohortSlug]/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx
new file mode 100644
index 000000000..2ee20800d
--- /dev/null
+++ b/src/pages/main-cohort/[mainCohortSlug]/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx
@@ -0,0 +1,3 @@
+import SyllabusPage from '../../../../../../syllabus/[cohortSlug]/[lesson]/[lessonSlug]';
+
+export default SyllabusPage;
diff --git a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx
index 1147f310a..28eeb427f 100644
--- a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx
+++ b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx
@@ -54,11 +54,6 @@ function SyllabusContent() {
const { user, isLoading, isAuthenticatedWithRigobot } = useAuth();
const { rigo, isRigoInitialized } = useRigo();
const {
- taskTodo,
- cohortProgram,
- setTaskTodo,
- startDay,
- updateAssignment,
setCurrentTask,
currentTask,
nextModule,
@@ -99,9 +94,9 @@ function SyllabusContent() {
const [learnpackStart, setLearnpackStart] = useState(false);
const taskIsNotDone = currentTask && currentTask.task_status !== 'DONE';
const {
- getCohortAssignments, getCohortData, prepareTasks, state,
+ getCohortUserCapabilities, getCohortData, cohortSession, sortedAssignments, setCohortSession, taskTodo,
+ updateAssignment, startDay, updateTask,
} = useCohortHandler();
- const { cohortSession, sortedAssignments } = state;
// const isAvailableAsSaas = false;
const isAvailableAsSaas = cohortSession?.available_as_saas;
@@ -119,8 +114,8 @@ function SyllabusContent() {
const Open = !isOpen;
const { label, teacherInstructions, keyConcepts } = selectedSyllabus;
- const firstTask = nextModule?.modules[0];
- const lastPrevTask = prevModule?.modules && prevModule.modules[prevModule.modules.length - 1];
+ const firstTask = nextModule?.content[0];
+ const lastPrevTask = prevModule?.content && prevModule.content[prevModule.content.length - 1];
const cohortSlug = router?.query?.cohortSlug;
const lesson = router?.query?.lesson;
@@ -134,8 +129,8 @@ function SyllabusContent() {
const isLesson = lesson === 'read';
const filteredCurrentAssignments = sortedAssignments.map((section) => (showPendingTasks
- ? section.filteredModulesByPending
- : section.filteredModules));
+ ? section.filteredContentByPending
+ : section.filteredContent));
const currentModuleIndex = filteredCurrentAssignments.findIndex((s) => s?.some((l) => l.slug === lessonSlug || l.translations?.[language]?.slug === lessonSlug || (currentAsset?.id && l.translations?.[language]?.slug === currentAsset.slug)));
@@ -179,7 +174,7 @@ function SyllabusContent() {
};
const handleStartDay = async (module = null, avoidRedirect = false) => {
- const moduleToUpdate = module?.modules || nextModule.modules;
+ const moduleToUpdate = module?.content || nextModule.content;
const updatedTasks = moduleToUpdate?.map((l) => ({
...l,
associated_slug: l.slug,
@@ -192,6 +187,7 @@ function SyllabusContent() {
};
if (user?.id) {
await startDay({
+ cohort: cohortSession,
newTasks: updatedTasks,
customHandler,
});
@@ -202,7 +198,7 @@ function SyllabusContent() {
const cohort = await getCohortData({
cohortSlug,
});
- getCohortAssignments({
+ getCohortUserCapabilities({
cohort,
});
};
@@ -219,8 +215,11 @@ function SyllabusContent() {
if (result.data) {
const updateTasks = taskTodo.map((task) => ({ ...task }));
const index = updateTasks.findIndex((el) => el.task_type === assetTypeValues[lesson] && el.associated_slug === lessonSlug);
- updateTasks[index].opened_at = result.data.opened_at;
- setTaskTodo([...updateTasks]);
+ const updatedTask = {
+ ...updateTasks[index],
+ opened_at: result.data.opened_at,
+ };
+ updateTask(updatedTask, cohortSession);
}
} catch (e) {
log('update_task_error:', e);
@@ -258,6 +257,13 @@ function SyllabusContent() {
}
}, [currentTask]);
+ // eslint-disable-next-line arrow-body-style
+ useEffect(() => {
+ return () => {
+ setCohortSession(null);
+ };
+ }, []);
+
useEffect(() => {
const translations = currentAsset?.translations
? Object.values(currentAsset.translations)
@@ -343,7 +349,7 @@ function SyllabusContent() {
setGrantAccess(true);
}
- if (Object.keys(cohortSession).length > 0 && (cohortSession.cohort_role !== 'STUDENT' || cohortSession.available_as_saas === false)) setGrantAccess(true);
+ if (cohortSession?.cohort_role !== 'STUDENT' || cohortSession?.available_as_saas === false) setGrantAccess(true);
}, [cohortSession, allSubscriptions]);
const toggleSettings = () => {
@@ -390,9 +396,13 @@ function SyllabusContent() {
};
const onClickAssignment = (e, item) => {
- const link = `/syllabus/${cohortSlug}/${item.type?.toLowerCase()}/${item.slug}`;
-
- router.push(link);
+ router.push({
+ query: {
+ ...router.query,
+ lesson: item.type?.toLowerCase(),
+ lessonSlug: item?.slug,
+ },
+ });
cleanCurrentData();
};
@@ -406,7 +416,7 @@ function SyllabusContent() {
};
useEffect(() => {
- const currTask = sortedAssignments[currentModuleIndex]?.modules?.find((l) => l.slug === lessonSlug);
+ const currTask = sortedAssignments[currentModuleIndex]?.content?.find((l) => l.slug === lessonSlug);
const currentLanguageTaskUrl = currTask?.translations?.[lang === 'en' ? 'us' : lang]?.slug || lessonSlug;
bc.lesson({ asset_type: assetTypeValues[lesson] }).getAsset(currentLanguageTaskUrl).then(({ data }) => {
const translations = data?.translations;
@@ -477,13 +487,13 @@ function SyllabusContent() {
useEffect(() => {
const currentSyllabus = sortedAssignments.find((l) => l.id === currentSelectedModule);
const currModuleIndex = sortedAssignments.findIndex(
- (l) => l.modules.some((m) => m.slug === lessonSlug),
+ (l) => l.content.some((m) => m.slug === lessonSlug),
);
const nextModuleData = sortedAssignments[currModuleIndex + 1];
const prevModuleData = sortedAssignments[currModuleIndex - 1];
const defaultSyllabus = sortedAssignments.find(
- (l) => l.modules.find((m) => m.slug === lessonSlug),
+ (l) => l.content.find((m) => m.slug === lessonSlug),
);
if (defaultSyllabus) {
@@ -502,11 +512,7 @@ function SyllabusContent() {
}
}, [selectedSyllabus]);
- useEffect(() => {
- prepareTasks();
- }, [cohortProgram, taskTodo, router]);
-
- const teacherActions = professionalRoles.includes(cohortSession.cohort_role)
+ const teacherActions = professionalRoles.includes(cohortSession?.cohort_role)
? [
{
icon: 'key',
@@ -555,6 +561,7 @@ function SyllabusContent() {
setCurrentBlankProps(nextAssignment);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: nextAssignment?.type?.toLowerCase(),
lessonSlug: nextAssignment?.slug,
@@ -564,6 +571,7 @@ function SyllabusContent() {
setCurrentBlankProps(null);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: nextAssignment?.type?.toLowerCase(),
lessonSlug: nextAssignment?.slug,
@@ -572,9 +580,10 @@ function SyllabusContent() {
}
} else if (nextModule) {
if (firstTask.target !== 'blank') {
- if (cohortSlug && !!firstTask && nextModule?.filteredModules[0]) {
+ if (cohortSlug && !!firstTask && nextModule?.filteredContent[0]) {
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: firstTask?.type?.toLowerCase(),
lessonSlug: firstTask?.slug,
@@ -586,6 +595,7 @@ function SyllabusContent() {
} else {
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: firstTask?.type?.toLowerCase(),
lessonSlug: firstTask?.slug,
@@ -599,12 +609,12 @@ function SyllabusContent() {
const handlePrevPage = () => {
cleanCurrentData();
scrollMainContainerTop();
- console.log('HEY!!');
if (previousAssignment !== null) {
if (previousAssignment?.target === 'blank') {
setCurrentBlankProps(previousAssignment);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: previousAssignment?.type?.toLowerCase(),
lessonSlug: previousAssignment?.slug,
@@ -614,6 +624,7 @@ function SyllabusContent() {
setCurrentBlankProps(null);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: previousAssignment?.type?.toLowerCase(),
lessonSlug: previousAssignment?.slug,
@@ -625,6 +636,7 @@ function SyllabusContent() {
if (cohortSlug && !!lastPrevTask) {
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: lastPrevTask?.type?.toLowerCase(),
lessonSlug: lastPrevTask?.slug,
@@ -636,6 +648,7 @@ function SyllabusContent() {
setCurrentAsset(lastPrevTask);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: lastPrevTask?.type?.toLowerCase(),
lessonSlug: lastPrevTask?.slug,
@@ -651,6 +664,7 @@ function SyllabusContent() {
setCurrentBlankProps(previousAssignment);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: previousAssignment?.type?.toLowerCase(),
lessonSlug: previousAssignment?.slug,
@@ -670,6 +684,7 @@ function SyllabusContent() {
setCurrentBlankProps(nextAssignment);
router.push({
query: {
+ ...router.query,
cohortSlug,
lesson: nextAssignment?.type?.toLowerCase(),
lessonSlug: nextAssignment?.slug,
@@ -831,7 +846,7 @@ function SyllabusContent() {
setShowPendingTasks={setShowPendingTasks}
isOpen={isOpen}
onToggle={onToggle}
- isStudent={!professionalRoles.includes(cohortSession.cohort_role)}
+ isStudent={!professionalRoles.includes(cohortSession?.cohort_role)}
teacherInstructions={{
existContentToShow: extendedInstructions !== null,
actionHandler: () => {