From e98549b0cd6fb36c24b0bf4aba4b544b43768a68 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Thu, 5 Sep 2024 17:07:34 -0400 Subject: [PATCH 01/67] :sparkles: new url for students to schedule mentorships --- i18n.js | 1 + public/locales/en/dashboard.json | 18 +- public/locales/es/dashboard.json | 18 +- src/pages/mentorship/schedule.jsx | 706 ++++++++++++++++++++++++++++++ 4 files changed, 733 insertions(+), 10 deletions(-) create mode 100644 src/pages/mentorship/schedule.jsx diff --git a/i18n.js b/i18n.js index c81e33453..f85d909ff 100644 --- a/i18n.js +++ b/i18n.js @@ -25,6 +25,7 @@ module.exports = { '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'], '/survey/[surveyId]': ['survey'], '/mentorship': ['mentorship'], + '/mentorship/schedule': ['mentorship', 'dashboard'], '/how-to': ['how-to'], '/pricing': ['pricing', 'signup'], '/how-to/[slug]': ['how-to'], diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 2eba43f76..28c6eb444 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -3,6 +3,7 @@ "title": "Dashboard" }, "title": "Your News", + "back-to-dash": "Back to Dashboard", "backToChooseProgram": "Back to choose program", "moduleMap": "Module map", "progressText": "progress in the program", @@ -14,9 +15,9 @@ "cta-description": "Your current plan does not include access to this cohort, please upgrade to access the content.", "cta-cohort-not-found": "This cohort does not exist or you don't have access to join.", "cta-button": "Review plan", - "join-next-cohort":"Join next cohort", - "start-course":"Start this course", - "join-more":"Join more than 100 people taking this course right now", + "join-next-cohort": "Join next cohort", + "start-course": "Start this course", + "join-more": "Join more than 100 people taking this course right now", "preview-description": "You are reviewing this cohort dashboard on \"preview mode\", in order to start learning and interact with the materials please join the cohort." }, "already-have-this-cohort": "You are already a member of this cohort", @@ -104,7 +105,14 @@ "get-unlimited-mentorship": "and get unlimited mentorship", "you-have": "You have", "available-sessions": "available sessions", - "tooltip": "Mentorships reload every week" + "tooltip": "Mentorships reload every week", + "mentor-sessions-available": "mentor sessions available", + "action": "Schedule session" + }, + "schedule-steps": { + "select-mentorship": "First select a type of mentorship", + "select-mentor": "Now search for a mentor", + "schedule": "Schedule the session" }, "deliverProject": { "title": "Deliver assignment", @@ -194,4 +202,4 @@ "title": "Welcome to 4Geeks!", "description": "Watch this short video that explains how to get the most out of 4Geeks and enhance your learning experience" } -} +} \ No newline at end of file diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 0a4b24808..4e9891d61 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -4,6 +4,7 @@ }, "title": "Tus noticias", "moduleMap": "Mapa de módulos", + "back-to-dash": "Regresar al Dashboard", "backToChooseProgram": "Volver a elegir programa", "progressText": "Progreso en el programa", "whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.", @@ -15,9 +16,9 @@ "cta-description": "Tu plan actual no incluye acceso a esta cohorte, por favor actualiza para acceder al contenido.", "cta-cohort-not-found": "Esta cohorte no existe o no tienes acceso para unirte.", "cta-button": "Revisar plan", - "join-next-cohort":"Únete a la cohorte", - "start-course":"Empezar este curso", - "join-more":"Únete a más de 100 personas que toman este curso ahora mismo", + "join-next-cohort": "Únete a la cohorte", + "start-course": "Empezar este curso", + "join-more": "Únete a más de 100 personas que toman este curso ahora mismo", "preview-description": "Estás revisando esta cohorte en \"modo de vista previa\". Para comenzar a aprender e interactuar con el material, únete a la cohorte." }, "modules": { @@ -104,7 +105,14 @@ "get-unlimited-mentorship": "y obtén tutoría ilimitada", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana" + "tooltip": "Las tutorías se recargan cada semana", + "mentor-sessions-available": "sesiones de mentoria disponibles", + "action": "Reservar mentoria" + }, + "schedule-steps": { + "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentor": "Ahora busca un mentor", + "schedule": "Agenda la sesión" }, "deliverProject": { "title": "Entregar tarea", @@ -194,4 +202,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} +} \ No newline at end of file diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx new file mode 100644 index 000000000..32b2578dd --- /dev/null +++ b/src/pages/mentorship/schedule.jsx @@ -0,0 +1,706 @@ +import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; + +import { useRouter } from 'next/router'; +import useTranslation from 'next-translate/useTranslation'; +import { getStorageItem } from '../../utils'; +import { reportDatalayer } from '../../utils/requests'; +import useStyle from '../../common/hooks/useStyle'; +import useAuth from '../../common/hooks/useAuth'; +import Icon from '../../common/components/Icon'; +import Link from '../../common/components/NextChakraLink'; +import Heading from '../../common/components/Heading'; +import Text from '../../common/components/Text'; +import Image from '../../common/components/Image'; +import bc from '../../common/services/breathecode'; +import modifyEnv from '../../../modifyEnv'; + +function MentorshipSchedule() { + let isTabletOrPhone = false; + if (typeof window !== 'undefined') { + isTabletOrPhone = window.innerWidth < 780; + } + const { t } = useTranslation('dashboard'); + const router = useRouter(); + const { service, mentor } = router.query; + const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); + const accessToken = getStorageItem('accessToken'); + const toast = useToast(); + const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); + const { borderColor } = useStyle(); + + const { isLoading, user, isAuthenticated } = useAuth(); + const [mentorshipServices, setMentorshipServices] = useState({ + isLoading: true, + data: [], + }); + const [searchProps, setSearchProps] = useState({ + serviceSearch: '', + mentorSearch: '', + }); + const [admissionsData, setAdmissionsData] = useState(); + const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); + const [consumableOfService, setConsumableOfService] = useState({}); + const [consumables, setConsumables] = useState([]); + const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); + const [mentorsByService, setMentorsByService] = useState([]); + const [mentoryProps, setMentoryProps] = useState({}); + + const calculateExistenceOfConsumable = () => { + if (consumableOfService.available_as_saas === false) return true; + if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; + return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; + }; + const availableForConsume = calculateExistenceOfConsumable(); + + const getAllSyllabus = () => { + const syllabus = []; + const allCohorts = admissionsData?.cohorts || []; + + allCohorts.forEach(({ cohort }) => { + if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); + }); + return syllabus; + }; + + const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); + + const getServices = async (userRoles) => { + if (userRoles?.length > 0) { + const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + .then((resp) => { + const data = resp?.data; + if (data !== undefined && data.length > 0) { + return data.map((serv) => ({ + ...serv, + academy: { + id: role?.academy.id, + available_as_saas: role?.academy?.available_as_saas, + }, + })); + } + return []; + })); + const mentorshipResults = await Promise.all(mentorshipPromises); + const recopilatedServices = mentorshipResults.flat(); + + setMentorshipServices({ + isLoading: false, + data: recopilatedServices, + }); + } + }; + + const getAdmissionsData = async () => { + try { + const response = await bc.admissions().me(); + const admissionsFromDB = response.data; + setAdmissionsData(admissionsFromDB); + getServices(admissionsData.roles); + } catch (error) { + console.error('Error fetching admissions data:', error); + } + }; + + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + + const academies = {}; + + mentorshipServices.data.forEach((serv) => { + const { academy, ...restOfService } = serv; + if (!academies[academy.id]) { + academies[academy.id] = { services: [] }; + } + academies[academy.id].services.push(restOfService); + }); + + const academyData = Object.entries(academies).map(([academy, values]) => ({ + id: Number(academy), + services: values.services, + })); + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentors = academyData.map((academy) => bc.mentorship({ + services: academy.services.map((s) => s.slug).join(','), + syllabus: allSyllabus?.join(','), + status: 'ACTIVE', + academy: academy.id, + }).getMentor() + .then((res) => { + const allMentors = res?.data; + return allMentors; + })); + const mentorsList = (await Promise.all(mentors)).flat(); + return mentorsList; + } + + return []; + }; + + const getMentorsAndConsumables = async () => { + const mentors = await getAllMentorsAvailable(); + const reqConsumables = await bc.payment().service().consumable() + .then((res) => res?.data?.mentorship_service_sets.map((mentorshipServiceSet) => bc.mentorship() + .getServiceSet(mentorshipServiceSet?.id) + .then((rs) => ({ + ...rs?.data, + ...mentorshipServiceSet, + })))); + + const allConsumables = await Promise.all(reqConsumables); + + setConsumables(allConsumables); + setAllMentorsAvailable(mentors); + }; + + const manageMentorsData = (serv) => { + const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); + + const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + + // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); + if (relatedConsumables) { + reportDatalayer({ + dataLayer: { + event: 'select_mentorship_service', + path: router.pathname, + consumables_amount: relatedConsumables.balance?.unit, + mentorship_service: serv?.slug, + }, + }); + } + setConsumableOfService({ + ...relatedConsumables, + balance: { + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + }, + available_as_saas: serv?.academy?.available_as_saas, + }); + setMentoryProps({ ...mentoryProps, serv }); + }; + + const handleService = async (serv) => { + try { + if (allMentorsAvailable.length > 0) { + const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); + setMentorsByService(mentorsByServices); + manageMentorsData(serv, mentorsByServices); + if (openSearchService) setOpenSearchService(false); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } else { + manageMentorsData(serv); + if (openSearchService) setOpenSearchService(false); + } + } catch (e) { + toast({ + position: 'top', + title: 'Error', + description: t('alert-message:error-finding-mentors'), + status: 'error', + duration: 7000, + isClosable: true, + }); + } + }; + + const handleServicesByMentorsAvailability = () => { + const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + + if (service) { + const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); + handleService(mentorshipFound[0]); + if (mentorshipFound.length > 0) { + setMentoryProps((prev) => ({ + ...prev, + service: mentorshipFound[0], + })); + } + } + + if (mentor) { + const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); + const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + if (servicesOfMentor.length > 0) { + setOpenSearchMentor(false); + setMentoryProps((prev) => ({ + ...prev, + mentor: isolateMentor[0], + })); + } + } + + return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; + }; + + const handleMentorSelection = (ment) => { + if (mentoryProps.service.slug) { + setMentoryProps({ ...mentoryProps, ment }); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } + if (openSearchMentor) setOpenSearchMentor(false); + }; + + useEffect(() => { + if (mentorshipServices.data && allMentorsAvailable) { + const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); + setServicesByMentorsAvailable(servicesWithMentorsAvailable); + } + }, [mentorshipServices.data, allMentorsAvailable]); + + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + useEffect(() => { + if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { + getMentorsAndConsumables(); + } + }, [mentorshipServices]); + + const handleOpenCloseService = () => { + if (!openSearchService) { + if (!openSearchMentor) setOpenSearchMentor(true); + setMentoryProps({}); + setOpenSearchService(true); + } + }; + + const handleOpenCloseMentor = () => { + setMentoryProps((prevState) => { + const { mentor: mentorToRemove, ...remainingProps } = prevState; + return remainingProps; + }); + setOpenSearchMentor(true); + }; + + const reportBookMentor = () => { + reportDatalayer({ + dataLayer: { + event: 'book_mentorship_session', + path: router.pathname, + consumables_amount: consumableOfService.balance.unit, + mentorship_service: mentoryProps?.service?.slug, + mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, + mentor_id: mentoryProps.mentor.slug, + mentor_booking_url: mentoryProps.mentor.booking_url, + }, + }); + }; + + const handleTitleStep = () => { + if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); + if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + return t('schedule-steps.schedule'); + }; + + // console.log(isTabletOrPhone) + // console.log("available for consume", availableForConsume) + // console.log(servicesByMentorAvailable); + // console.log("Soy admissions Data", admissionsData) + // console.log("All sylabus", allSyllabus) + // console.log("mentorship services", mentorshipServices.data) + // console.log("consumibles", consumables) + // console.log("mentores disponibles", allMentorsAvailable) + // console.log("consumable of service", consumableOfService) + // console.log("consumable of service balance", consumableOfService?.balance?.unit) + console.log('mentoryProps', mentoryProps); + // console.log("available for consume", availableForConsume) + // console.log("access token", accessToken) + // console.log("opensearch service",openSearchService) + // console.log("opensearch mentor",openSearchMentor) + + return !isLoading && user && !mentorshipServices.isLoading && ( + + + {`← ${t('back-to-dash')}`} + + + + {t('supportSideBar.schedule-button')} + + + {t('mentorship.you-have')} + {' '} + {servicesByMentorAvailable?.length} + {' '} + {t('mentorship.mentor-sessions-available')} + + + {handleTitleStep()} + + + {openSearchService + && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.select-type')} + /> + )} + + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading + ? ( + <> + {mentoryProps.service + ? ( + + + {mentoryProps?.service && !openSearchService + && ( + + )} + {mentoryProps.service?.name} + + {mentoryProps?.service && !openSearchService + && ( + + )} + + ) + : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)} + > + + {mentoryProps.serv ? mentoryProps.serv.name : serv.name} + + + ))} + + ) + : ( + + No service + + )} + + + {mentoryProps?.service && ( + <> + {openSearchMentor && !mentoryProps.mentor + && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.search-mentor')} + /> + + )} + + {mentorsByService.length > 0 + ? ( + <> + {(mentoryProps.service && mentoryProps.mentor) + ? ( + + + + {`${mentoryProps.mentor?.user?.first_name} + + + + + {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + + + + + + + + ) + : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( + + {i !== 0 && ( + + )} + handleMentorSelection(ment)} + > + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + + + + + + ))} + + ) + : ( + + {t('supportSideBar.no-mentors')} + + )} + + + )} + {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume + && ( + <> + {mentoryProps.mentor.booking_url + ? ( + reportBookMentor()} + href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} + target="_blank" + rel="noopener noreferrer" + background="#0196d1" + display="flex" + marginTop="12px" + padding="10px" + borderRadius="5px" + alignItems="center" + > + + {t('mentorship.action')} + + + + ) + : ( + + {t('supportSideBar.no-mentor-link')} + + )} + + )} + {mentoryProps.service && mentoryProps.mentor && !availableForConsume + && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + + + + ); +} + +export default MentorshipSchedule; From 7671db492f31b397cdc01b89aab24f31b7696891 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Fri, 13 Sep 2024 17:44:14 -0400 Subject: [PATCH 02/67] :recycle: refactor of the jsx --- src/pages/mentorship/schedule.jsx | 561 +++++++++--------------------- 1 file changed, 167 insertions(+), 394 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 32b2578dd..d0cb3d1c5 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useEffect, useState, useMemo } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; import { useRouter } from 'next/router'; @@ -55,21 +55,9 @@ function MentorshipSchedule() { }; const availableForConsume = calculateExistenceOfConsumable(); - const getAllSyllabus = () => { - const syllabus = []; - const allCohorts = admissionsData?.cohorts || []; - - allCohorts.forEach(({ cohort }) => { - if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); - }); - return syllabus; - }; - - const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); - const getServices = async (userRoles) => { if (userRoles?.length > 0) { - const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + const mentorshipPromises = await userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() .then((resp) => { const data = resp?.data; if (data !== undefined && data.length > 0) { @@ -98,42 +86,48 @@ function MentorshipSchedule() { const response = await bc.admissions().me(); const admissionsFromDB = response.data; setAdmissionsData(admissionsFromDB); - getServices(admissionsData.roles); + getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const getAllMentorsAvailable = async () => { - const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + const allSyllabus = useMemo(() => { + const allCohorts = admissionsData?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissionsData]); - const academies = {}; + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); - mentorshipServices.data.forEach((serv) => { - const { academy, ...restOfService } = serv; - if (!academies[academy.id]) { - academies[academy.id] = { services: [] }; + const academies = mentorshipServices.data.reduce((acc, { academy, ...restOfService }) => { + if (!acc[academy.id]) { + acc[academy.id] = { services: [] }; } - academies[academy.id].services.push(restOfService); - }); + acc[academy.id].services.push(restOfService); + return acc; + }, {}); - const academyData = Object.entries(academies).map(([academy, values]) => ({ - id: Number(academy), - services: values.services, + const academyData = Object.entries(academies).map(([id, { services }]) => ({ + id: Number(id), + services, })); - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { - const mentors = academyData.map((academy) => bc.mentorship({ + const getMentorsForAcademy = async (academy) => { + const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, - }).getMentor() - .then((res) => { - const allMentors = res?.data; - return allMentors; - })); - const mentorsList = (await Promise.all(mentors)).flat(); + }).getMentor(); + + return res?.data || []; + }; + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentorsPromises = academyData.map(getMentorsForAcademy); + const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; } @@ -153,6 +147,7 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); + console.log(allConsumables); setAllMentorsAvailable(mentors); }; @@ -160,10 +155,9 @@ function MentorshipSchedule() { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); if (relatedConsumables) { reportDatalayer({ dataLayer: { @@ -181,9 +175,12 @@ function MentorshipSchedule() { }, available_as_saas: serv?.academy?.available_as_saas, }); - setMentoryProps({ ...mentoryProps, serv }); + + setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; + console.log(consumableOfService); + const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -220,7 +217,7 @@ function MentorshipSchedule() { if (mentorshipFound.length > 0) { setMentoryProps((prev) => ({ ...prev, - service: mentorshipFound[0], + serviceSelected: mentorshipFound[0], })); } } @@ -232,7 +229,7 @@ function MentorshipSchedule() { setOpenSearchMentor(false); setMentoryProps((prev) => ({ ...prev, - mentor: isolateMentor[0], + mentorSelected: isolateMentor[0], })); } } @@ -241,8 +238,8 @@ function MentorshipSchedule() { }; const handleMentorSelection = (ment) => { - if (mentoryProps.service.slug) { - setMentoryProps({ ...mentoryProps, ment }); + if (mentoryProps.serviceSelected.slug) { + setMentoryProps({ ...mentoryProps, mentorSelected: ment }); setSearchProps({ serviceSearch: '', mentorSearch: '', @@ -286,7 +283,7 @@ function MentorshipSchedule() { const handleOpenCloseMentor = () => { setMentoryProps((prevState) => { - const { mentor: mentorToRemove, ...remainingProps } = prevState; + const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; return remainingProps; }); setOpenSearchMentor(true); @@ -298,17 +295,17 @@ function MentorshipSchedule() { event: 'book_mentorship_session', path: router.pathname, consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.service?.slug, - mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, - mentor_id: mentoryProps.mentor.slug, - mentor_booking_url: mentoryProps.mentor.booking_url, + mentorship_service: mentoryProps?.serviceSelected?.slug, + mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, + mentor_id: mentoryProps.mentorSelected.slug, + mentor_booking_url: mentoryProps.mentorSelected.booking_url, }, }); }; const handleTitleStep = () => { - if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); - if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; @@ -322,382 +319,158 @@ function MentorshipSchedule() { // console.log("mentores disponibles", allMentorsAvailable) // console.log("consumable of service", consumableOfService) // console.log("consumable of service balance", consumableOfService?.balance?.unit) - console.log('mentoryProps', mentoryProps); + // console.log('mentoryProps', mentoryProps); // console.log("available for consume", availableForConsume) // console.log("access token", accessToken) // console.log("opensearch service",openSearchService) // console.log("opensearch mentor",openSearchMentor) return !isLoading && user && !mentorshipServices.isLoading && ( - - + + {`← ${t('back-to-dash')}`} - + {t('supportSideBar.schedule-button')} - - {t('mentorship.you-have')} - {' '} - {servicesByMentorAvailable?.length} - {' '} - {t('mentorship.mentor-sessions-available')} + + {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - + {handleTitleStep()} - - {openSearchService - && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.select-type')} - /> - )} - - - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading - ? ( - <> - {mentoryProps.service - ? ( - - - {mentoryProps?.service && !openSearchService - && ( - - )} - {mentoryProps.service?.name} - - {mentoryProps?.service && !openSearchService - && ( - - )} - - ) - : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)} - > - - {mentoryProps.serv ? mentoryProps.serv.name : serv.name} - + {openSearchService && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> + )} + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( +
+ {mentoryProps.serviceSelected ? ( + + + {mentoryProps?.serviceSelected && !openSearchService && ( + + )} + {mentoryProps.serviceSelected?.name} + + {mentoryProps?.serviceSelected && !openSearchService + && ( + + )} + + ) : ( + servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)}> + + {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - ))} - - ) - : ( - - No service - - )} + + )) + )} +
+ ) : ( + + No service + + )}
- {mentoryProps?.service && ( - <> - {openSearchMentor && !mentoryProps.mentor - && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.search-mentor')} - /> - - )} - - {mentorsByService.length > 0 - ? ( - <> - {(mentoryProps.service && mentoryProps.mentor) - ? ( - + + {mentoryProps?.serviceSelected && ( + + {openSearchMentor && !mentoryProps.mentorSelected && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> + + )} + + {mentorsByService.length > 0 ? ( + + {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( + - - {`${mentoryProps.mentor?.user?.first_name} + + {`${mentoryProps.mentorSelected?.user?.first_name} - - - - {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - ) - : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( - - {i !== 0 && ( - - )} - handleMentorSelection(ment)} - > - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + ) : ( + mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( +
+ {i !== 0 && ( + + )} + handleMentorSelection(ment)}> + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + - - - ))} - - ) - : ( - - {t('supportSideBar.no-mentors')} +
+ )) + )} +
+ ) : ( + + {t('supportSideBar.no-mentors')} + + )} +
+
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( + + {mentoryProps.mentorSelected.booking_url ? ( + reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> + + {t('mentorship.action')} + + + + ) : ( + + {t('supportSideBar.no-mentor-link')} )} - - - )} - {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume - && ( - <> - {mentoryProps.mentor.booking_url - ? ( - reportBookMentor()} - href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} - target="_blank" - rel="noopener noreferrer" - background="#0196d1" - display="flex" - marginTop="12px" - padding="10px" - borderRadius="5px" - alignItems="center" - > - - {t('mentorship.action')} - - - - ) - : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - {mentoryProps.service && mentoryProps.mentor && !availableForConsume - && ( - - {t('supportSideBar.no-mentoring-available')} - - )} - +
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + +
); From b7da943596efd49169a2cec1139414a97843f6d8 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 14:49:50 -0400 Subject: [PATCH 03/67] :sparkles: page to schedule mentorship --- public/locales/en/dashboard.json | 4 +- public/locales/es/dashboard.json | 4 +- src/pages/mentorship/schedule.jsx | 191 ++++++++++++++++++++---------- 3 files changed, 134 insertions(+), 65 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 28c6eb444..b37db731b 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -107,7 +107,9 @@ "available-sessions": "available sessions", "tooltip": "Mentorships reload every week", "mentor-sessions-available": "mentor sessions available", - "action": "Schedule session" + "action": "Schedule session", + "no-available": "No services available", + "no-available-for-teacher": "No serives available for" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 4e9891d61..5b5eb9cac 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -107,7 +107,9 @@ "available-sessions": "sesiones disponibles", "tooltip": "Las tutorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", - "action": "Reservar mentoria" + "action": "Reservar mentoria", + "no-available": "No hay servicios disponibles", + "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { "select-mentorship": "Primero selecciona un tipo de tutoria", diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index d0cb3d1c5..6f442a1ba 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,10 +1,13 @@ -import { useEffect, useState, useMemo } from 'react'; +import { useEffect, useState } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; - import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; +import PropTypes from 'prop-types'; import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; +import { validatePlanExistence } from '../../common/handlers/subscriptions'; +import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; +import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; @@ -15,30 +18,44 @@ import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; import modifyEnv from '../../../modifyEnv'; +function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { + return ( + + {t('supportSideBar.no-mentoring-available')} + + + ); +} + function MentorshipSchedule() { let isTabletOrPhone = false; if (typeof window !== 'undefined') { isTabletOrPhone = window.innerWidth < 780; } - const { t } = useTranslation('dashboard'); const router = useRouter(); + const { t } = useTranslation('dashboard'); + const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; + const { borderColor } = useStyle(); + const { isLoading, user, isAuthenticated } = useAuth(); const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); const accessToken = getStorageItem('accessToken'); const toast = useToast(); const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); - const { borderColor } = useStyle(); - - const { isLoading, user, isAuthenticated } = useAuth(); - const [mentorshipServices, setMentorshipServices] = useState({ - isLoading: true, - data: [], - }); - const [searchProps, setSearchProps] = useState({ - serviceSearch: '', - mentorSearch: '', - }); - const [admissionsData, setAdmissionsData] = useState(); + const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); + const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); const [openSearchService, setOpenSearchService] = useState(true); const [openSearchMentor, setOpenSearchMentor] = useState(true); @@ -47,6 +64,10 @@ function MentorshipSchedule() { const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); + const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); + const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); + const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -85,19 +106,12 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; - setAdmissionsData(admissionsFromDB); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const allSyllabus = useMemo(() => { - const allCohorts = admissionsData?.cohorts || []; - const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; - return syllabus; - }, [admissionsData]); - const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -117,7 +131,6 @@ function MentorshipSchedule() { const getMentorsForAcademy = async (academy) => { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), - syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, }).getMentor(); @@ -125,7 +138,7 @@ function MentorshipSchedule() { return res?.data || []; }; - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + if (servicesSlugs.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -147,31 +160,30 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); - console.log(allConsumables); setAllMentorsAvailable(mentors); }; const manageMentorsData = (serv) => { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - if (relatedConsumables) { + if (relatedConsumable) { reportDatalayer({ dataLayer: { event: 'select_mentorship_service', path: router.pathname, - consumables_amount: relatedConsumables.balance?.unit, + consumables_amount: relatedConsumable.balance?.unit, mentorship_service: serv?.slug, }, }); } setConsumableOfService({ - ...relatedConsumables, + ...relatedConsumable, balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, }, available_as_saas: serv?.academy?.available_as_saas, }); @@ -179,8 +191,6 @@ function MentorshipSchedule() { setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; - console.log(consumableOfService); - const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -232,6 +242,7 @@ function MentorshipSchedule() { mentorSelected: isolateMentor[0], })); } + return servicesOfMentor; } return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; @@ -304,31 +315,69 @@ function MentorshipSchedule() { }; const handleTitleStep = () => { - if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; - // console.log(isTabletOrPhone) - // console.log("available for consume", availableForConsume) - // console.log(servicesByMentorAvailable); - // console.log("Soy admissions Data", admissionsData) - // console.log("All sylabus", allSyllabus) - // console.log("mentorship services", mentorshipServices.data) - // console.log("consumibles", consumables) - // console.log("mentores disponibles", allMentorsAvailable) - // console.log("consumable of service", consumableOfService) - // console.log("consumable of service balance", consumableOfService?.balance?.unit) - // console.log('mentoryProps', mentoryProps); - // console.log("available for consume", availableForConsume) - // console.log("access token", accessToken) - // console.log("opensearch service",openSearchService) - // console.log("opensearch mentor",openSearchMentor) + const handleDescription = () => { + if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; + return t('mentorship.no-available'); + }; + + useEffect(() => { + if (mentoryProps.serviceSelected) { + fetchSubscriptions() + .then((data) => { + setSubscriptionData(data); + reportDatalayer({ + dataLayer: { + event: 'subscriptions_load', + method: 'native', + plan_financings: data?.plan_financings?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + subscriptions: data?.subscriptions?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + }, + }); + }); + } + }, [mentoryProps.serviceSelected]); + + const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { + const paidAtDate = new Date(invoice.paid_at); + return paidAtDate > latest ? paidAtDate : latest; + }, new Date(0)); + + const sortByMostRecentInvoice = (a, b) => { + const latestA = getMostRecentPaidAt(a.invoices); + const latestB = getMostRecentPaidAt(b.invoices); + return latestB - latestA; + }; + + const handleGetMoreMentorships = async () => { + setIsFetchingDataForModal(true); + const academyService = mentoryProps?.serviceSelected; + const allSubscriptions = subscriptionData.subscriptions; + const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); + const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; + + validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { + setDataToGetAccessModal({ + ...data, + event: '', + academyService, + }); + setIsModalToGetAccessOpen(true); + }) + .finally(() => setIsFetchingDataForModal(false)); + }; return !isLoading && user && !mentorshipServices.isLoading && ( - + useColorModeValue('#f9f9f9', '#171f2a')}> - {`← ${t('back-to-dash')}`} + + + {`${t('back-to-dash')}`} + @@ -347,16 +396,16 @@ function MentorshipSchedule() { {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? (
{mentoryProps.serviceSelected ? ( - + {mentoryProps?.serviceSelected && !openSearchService && ( - + )} {mentoryProps.serviceSelected?.name} {mentoryProps?.serviceSelected && !openSearchService && ( - )} @@ -373,7 +422,7 @@ function MentorshipSchedule() {
) : ( - No service + {handleDescription()} )} @@ -390,17 +439,17 @@ function MentorshipSchedule() { {mentorsByService.length > 0 ? ( {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - + - + {`${mentoryProps.mentorSelected?.user?.first_name} - - + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} @@ -426,7 +475,7 @@ function MentorshipSchedule() { {`${ment.user.first_name} ${ment.user.last_name}`} - + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} @@ -465,15 +514,31 @@ function MentorshipSchedule() { {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - {t('supportSideBar.no-mentoring-available')} - + )} + { + setIsModalToGetAccessOpen(false); + }} + />
); } +NoConsumablesCard.defaultProps = { + isLoading: false, // Valor por defecto cuando no se pasa esta prop +}; + +NoConsumablesCard.propTypes = { + t: PropTypes.func.isRequired, + handleGetMoreMentorships: PropTypes.func.isRequired, + isLoading: PropTypes.bool, +}; + export default MentorshipSchedule; From 2f75f799a7ce65be428266dcaaae726ed4b1367c Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 15:58:10 -0400 Subject: [PATCH 04/67] small change --- src/pages/mentorship/schedule.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 6f442a1ba..75b0ea9ad 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -57,17 +57,17 @@ function MentorshipSchedule() { const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); const [consumableOfService, setConsumableOfService] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [subscriptionData, setSubscriptionData] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -393,7 +393,7 @@ function MentorshipSchedule() { setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> )} - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( + {servicesByMentorAvailable?.length > 0 ? (
{mentoryProps.serviceSelected ? ( From 0cff98b308793ad7413a49149e5fe0f788ae6a08 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:57:50 -0400 Subject: [PATCH 05/67] Update dashboard.json --- public/locales/es/dashboard.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 5b5eb9cac..32bb65c11 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -73,17 +73,17 @@ "no-mentors": "No hay mentores disponibles", "no-mentor-link": "No hay enlace para crear sesión con este mentor", "search-mentor": "Buscar mentor por nombre", - "start-mentorship": "Para comenzar, seleccione un tipo de tutoría", + "start-mentorship": "Para comenzar, seleccione un tipo de mentoría", "mentoring-label": "Mentoría", - "mentoring": "Programe una sesión de tutoría 1-1.", - "select-type": "Seleccione un tipo de tutoría", - "mentoring-available": "Tutorías disponibles", - "no-mentoring-available": "No tienes tutorías disponibles.", + "mentoring": "Programe una sesión de mentoría 1-1.", + "select-type": "Seleccione un tipo de mentoría", + "mentoring-available": "Mentorías disponibles", + "no-mentoring-available": "No tienes mentorías disponibles.", "create-session-text": "Agendar ahora", "learn-more": "Aprender mas.", "learn-more-link": "https://4geeks.com/es/docs/knowledge-base-4geeks/sesiones-de-tutoria", "get-more-mentorships": "Consigue más mentorías", - "schedule-button": "Programar una sesión de tutoría", + "schedule-button": "Programar una sesión de mentoría", "mentors-available": "+{{count}} Mentores disponibles", "actionButtons": [ { @@ -101,18 +101,18 @@ ] }, "mentorship": { - "no-mentorship": "Se ha quedado sin tutorías, pero no se preocupe, puede obtener más con unos pocos clics.", - "get-unlimited-mentorship": "y obtén tutoría ilimitada", + "no-mentorship": "Se ha quedado sin mentorías, pero no se preocupe, puede obtener más con unos pocos clics.", + "get-unlimited-mentorship": "y obtén mentorías ilimitadas", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana", + "tooltip": "Las mentorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", "action": "Reservar mentoria", "no-available": "No hay servicios disponibles", "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { - "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentorship": "Primero selecciona un tipo de mentoría", "select-mentor": "Ahora busca un mentor", "schedule": "Agenda la sesión" }, @@ -204,4 +204,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} \ No newline at end of file +} From 16991aa60ea2c2cc8026cbf6b55ba30d181bb9cc Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:42:31 -0400 Subject: [PATCH 06/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 460 +++++------------------------- 1 file changed, 70 insertions(+), 390 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 75b0ea9ad..89308803a 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,43 +1,14 @@ -import { useEffect, useState } from 'react'; -import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; +import { useEffect, useState, useMemo } from 'react'; +import { useColorModeValue, Container, Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; -import PropTypes from 'prop-types'; -import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; -import { validatePlanExistence } from '../../common/handlers/subscriptions'; -import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; -import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; import Link from '../../common/components/NextChakraLink'; -import Heading from '../../common/components/Heading'; -import Text from '../../common/components/Text'; -import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; -import modifyEnv from '../../../modifyEnv'; - -function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { - return ( - - {t('supportSideBar.no-mentoring-available')} - - - ); -} +import MentoringConsumables from '../../common/components/SupportSidebar/MentoringConsumables'; function MentorshipSchedule() { let isTabletOrPhone = false; @@ -48,33 +19,15 @@ function MentorshipSchedule() { const { t } = useTranslation('dashboard'); const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; - const { borderColor } = useStyle(); const { isLoading, user, isAuthenticated } = useAuth(); - const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); - const accessToken = getStorageItem('accessToken'); - const toast = useToast(); - const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); - const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [consumableOfService, setConsumableOfService] = useState({}); + const [mentoryProps, setMentoryProps] = useState({}); + const [admissions, setAdmissions] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); - const [mentoryProps, setMentoryProps] = useState({}); - const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); const [subscriptionData, setSubscriptionData] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); - const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); - const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - - const calculateExistenceOfConsumable = () => { - if (consumableOfService.available_as_saas === false) return true; - if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; - return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; - }; - const availableForConsume = calculateExistenceOfConsumable(); const getServices = async (userRoles) => { if (userRoles?.length > 0) { @@ -106,12 +59,31 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; + setAdmissions(response.data); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + const allSyllabus = useMemo(() => { + const allCohorts = admissions?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissions]); + const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -132,13 +104,14 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', + syllabus: allSyllabus?.join(','), academy: academy.id, }).getMentor(); return res?.data || []; }; - if (servicesSlugs.length > 0) { + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -163,167 +136,37 @@ function MentorshipSchedule() { setAllMentorsAvailable(mentors); }; - const manageMentorsData = (serv) => { - const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - - const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - - if (relatedConsumable) { - reportDatalayer({ - dataLayer: { - event: 'select_mentorship_service', - path: router.pathname, - consumables_amount: relatedConsumable.balance?.unit, - mentorship_service: serv?.slug, - }, - }); - } - setConsumableOfService({ - ...relatedConsumable, - balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, - }, - available_as_saas: serv?.academy?.available_as_saas, - }); - - setMentoryProps({ ...mentoryProps, serviceSelected: serv }); - }; - - const handleService = async (serv) => { - try { - if (allMentorsAvailable.length > 0) { - const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); - setMentorsByService(mentorsByServices); - manageMentorsData(serv, mentorsByServices); - if (openSearchService) setOpenSearchService(false); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } else { - manageMentorsData(serv); - if (openSearchService) setOpenSearchService(false); - } - } catch (e) { - toast({ - position: 'top', - title: 'Error', - description: t('alert-message:error-finding-mentors'), - status: 'error', - duration: 7000, - isClosable: true, - }); - } - }; - - const handleServicesByMentorsAvailability = () => { - const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - - if (service) { - const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); - handleService(mentorshipFound[0]); - if (mentorshipFound.length > 0) { - setMentoryProps((prev) => ({ - ...prev, - serviceSelected: mentorshipFound[0], - })); - } - } - - if (mentor) { - const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); - const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - if (servicesOfMentor.length > 0) { - setOpenSearchMentor(false); - setMentoryProps((prev) => ({ - ...prev, - mentorSelected: isolateMentor[0], - })); - } - return servicesOfMentor; - } - - return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; - }; - - const handleMentorSelection = (ment) => { - if (mentoryProps.serviceSelected.slug) { - setMentoryProps({ ...mentoryProps, mentorSelected: ment }); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } - if (openSearchMentor) setOpenSearchMentor(false); - }; - - useEffect(() => { - if (mentorshipServices.data && allMentorsAvailable) { - const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); - setServicesByMentorsAvailable(servicesWithMentorsAvailable); - } - }, [mentorshipServices.data, allMentorsAvailable]); - - useEffect(() => { - const checkAuthAndFetchData = async () => { - if (!isLoading && !isAuthenticated) { - router.push('/login'); - } else if (isAuthenticated) { - await getAdmissionsData(); - } - }; - - checkAuthAndFetchData(); - }, [isLoading, isAuthenticated]); - useEffect(() => { if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { getMentorsAndConsumables(); } }, [mentorshipServices]); - const handleOpenCloseService = () => { - if (!openSearchService) { - if (!openSearchMentor) setOpenSearchMentor(true); - setMentoryProps({}); - setOpenSearchService(true); - } - }; - - const handleOpenCloseMentor = () => { - setMentoryProps((prevState) => { - const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; - return remainingProps; - }); - setOpenSearchMentor(true); - }; - - const reportBookMentor = () => { - reportDatalayer({ - dataLayer: { - event: 'book_mentorship_session', - path: router.pathname, - consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.serviceSelected?.slug, - mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, - mentor_id: mentoryProps.mentorSelected.slug, - mentor_booking_url: mentoryProps.mentorSelected.booking_url, - }, - }); - }; + const mentorsFiltered = mentorsByService.filter( + (ment) => { + const fullName = `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase(); + return ( + fullName.includes(searchProps.mentorSearch) + && ment.services.some((sv) => sv.status === 'ACTIVE' && sv.slug === mentoryProps?.service?.slug) + ); + }, + ); - const handleTitleStep = () => { - if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); - if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); - return t('schedule-steps.schedule'); - }; + const filterServices = () => { + if (subscriptionData?.selected_mentorship_service_set?.mentorship_services?.length > 0) { + return subscriptionData?.selected_mentorship_service_set?.mentorship_services?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } + if (mentorshipServices.data.length > 0) { + return mentorshipServices.data?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } - const handleDescription = () => { - if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; - return t('mentorship.no-available'); + return []; }; + const suscriptionServicesFiltered = filterServices(); useEffect(() => { if (mentoryProps.serviceSelected) { @@ -342,203 +185,40 @@ function MentorshipSchedule() { } }, [mentoryProps.serviceSelected]); - const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { - const paidAtDate = new Date(invoice.paid_at); - return paidAtDate > latest ? paidAtDate : latest; - }, new Date(0)); - - const sortByMostRecentInvoice = (a, b) => { - const latestA = getMostRecentPaidAt(a.invoices); - const latestB = getMostRecentPaidAt(b.invoices); - return latestB - latestA; - }; - - const handleGetMoreMentorships = async () => { - setIsFetchingDataForModal(true); - const academyService = mentoryProps?.serviceSelected; - const allSubscriptions = subscriptionData.subscriptions; - const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); - const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; - - validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { - setDataToGetAccessModal({ - ...data, - event: '', - academyService, - }); - setIsModalToGetAccessOpen(true); - }) - .finally(() => setIsFetchingDataForModal(false)); - }; - return !isLoading && user && !mentorshipServices.isLoading && ( - useColorModeValue('#f9f9f9', '#171f2a')}> + useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> {`${t('back-to-dash')}`} - - - {t('supportSideBar.schedule-button')} - - - {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - - - {handleTitleStep()} - - {openSearchService && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> - )} - - {servicesByMentorAvailable?.length > 0 ? ( -
- {mentoryProps.serviceSelected ? ( - - - {mentoryProps?.serviceSelected && !openSearchService && ( - - )} - {mentoryProps.serviceSelected?.name} - - {mentoryProps?.serviceSelected && !openSearchService - && ( - - )} - - ) : ( - servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)}> - - {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - - - )) - )} -
- ) : ( - - {handleDescription()} - - )} -
-
- - {mentoryProps?.serviceSelected && ( - - {openSearchMentor && !mentoryProps.mentorSelected && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> - - )} - - {mentorsByService.length > 0 ? ( - - {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - - - - {`${mentoryProps.mentorSelected?.user?.first_name} - - - - - {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - - - {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - - - - - - ) : ( - mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( -
- {i !== 0 && ( - - )} - handleMentorSelection(ment)}> - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} - - - - -
- )) - )} -
- ) : ( - - {t('supportSideBar.no-mentors')} - - )} -
-
- )} -
- - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( - - {mentoryProps.mentorSelected.booking_url ? ( - reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> - - {t('mentorship.action')} - - - - ) : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - - - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - )} - - { - setIsModalToGetAccessOpen(false); + + + - +
); } -NoConsumablesCard.defaultProps = { - isLoading: false, // Valor por defecto cuando no se pasa esta prop -}; - -NoConsumablesCard.propTypes = { - t: PropTypes.func.isRequired, - handleGetMoreMentorships: PropTypes.func.isRequired, - isLoading: PropTypes.bool, -}; - export default MentorshipSchedule; From c68d9568502508331e4095d80e4ad09f4c0cb8b0 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:50:12 -0400 Subject: [PATCH 07/67] :recycle: using Mentoring consumables component --- public/locales/en/dashboard.json | 2 +- public/locales/es/dashboard.json | 2 +- src/pages/mentorship/schedule.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index b37db731b..071ce11ca 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -3,7 +3,7 @@ "title": "Dashboard" }, "title": "Your News", - "back-to-dash": "Back to Dashboard", + "back-to-dashboard": "Back to dashboard", "backToChooseProgram": "Back to choose program", "moduleMap": "Module map", "progressText": "progress in the program", diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 32bb65c11..9556e3c10 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -4,7 +4,7 @@ }, "title": "Tus noticias", "moduleMap": "Mapa de módulos", - "back-to-dash": "Regresar al Dashboard", + "back-to-dashboard": "Volver a dashboard", "backToChooseProgram": "Volver a elegir programa", "progressText": "Progreso en el programa", "whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.", diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 89308803a..9cd560e58 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -190,7 +190,7 @@ function MentorshipSchedule() { - {`${t('back-to-dash')}`} + {`${t('back-to-dashboard')}`} From 07de5fd767efeb2f3f337e345dd1caf112a38313 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 19:01:09 -0400 Subject: [PATCH 08/67] :zap: introducing users to cohort wihout clicking he butotn --- src/js_modules/checkout/PaymentInfo.jsx | 38 +++++++++++----------- src/js_modules/checkout/Summary.jsx | 42 +++++++++++++------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx index 9af2ec223..775be9c4f 100644 --- a/src/js_modules/checkout/PaymentInfo.jsx +++ b/src/js_modules/checkout/PaymentInfo.jsx @@ -43,6 +43,7 @@ function PaymentInfo() { description: '', }); const [readyToRefetch, setReadyToRefetch] = useState(false); + const [cohortFound, setCohortFound] = useState(undefined); const [timeElapsed, setTimeElapsed] = useState(0); const toast = useToast(); const redirect = getStorageItem('redirect'); @@ -53,16 +54,18 @@ function PaymentInfo() { const isPaymentIdle = paymentStatus === 'idle'; const paymentStatusBgColor = isPaymentSuccess ? 'green.light' : '#ffefef'; - const redirectTocohort = (cohort) => { - const langLink = lang !== 'en' ? `/${lang}` : ''; - const syllabusVersion = cohort?.syllabus_version; - axiosInstance.defaults.headers.common.Academy = cohort.academy.id; - const cohortDashboardLink = `${langLink}/cohort/${cohort?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; - setCohortSession({ - ...cohort, - selectedProgramSlug: cohortDashboardLink, - }); - router.push(cohortDashboardLink); + const redirectTocohort = () => { + if (isPaymentSuccess) { + const langLink = lang !== 'en' ? `/${lang}` : ''; + const syllabusVersion = cohortFound?.syllabus_version; + axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; + const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; + setCohortSession({ + ...cohortFound, + selectedProgramSlug: cohortDashboardLink, + }); + router.push(cohortDashboardLink); + } else setPaymentStatus('idle'); }; const joinCohort = (cohort) => { reportDatalayer({ @@ -85,7 +88,7 @@ function PaymentInfo() { setReadyToRefetch(false); } if (dataRequested?.status === 'ACTIVE') { - redirectTocohort(cohort); + setCohortFound(cohort); } }) .catch(() => { @@ -163,7 +166,7 @@ function PaymentInfo() { if (selectedPlanCheckoutData?.owner?.id) getPaymentMethods(selectedPlanCheckoutData.owner.id); }, [selectedPlanCheckoutData, isAuthenticated]); - const handlePaymentErrors = (data, actions = {}, callback = () => {}) => { + const handlePaymentErrors = (data, actions = {}, callback = () => { }) => { const silentCode = data?.silent_code; setIsSubmittingPayment(false); actions?.setSubmitting(false); @@ -213,6 +216,8 @@ function PaymentInfo() { .then((respPayment) => { if (respPayment.status === 'FULFILLED') { setPaymentStatus('success'); + setReadyToRefetch(true); + setIsSubmittingPayment(true); setSelectedPlanCheckoutData({ ...selectedPlanCheckoutData, payment_success: true, @@ -367,14 +372,7 @@ function PaymentInfo() { variant="default" // mt="12px" isLoading={isSubmittingPayment} - onClick={() => { - if (isPaymentSuccess) { - setIsSubmittingPayment(true); - setReadyToRefetch(true); - } else { - setPaymentStatus('idle'); - } - }} + onClick={() => redirectTocohort()} > {isPaymentSuccess ? 'Start learning' : 'Try again'} diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index bb99c5fde..cc9a2d2e1 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -38,6 +38,7 @@ function Summary() { title: '', description: '', }); + const [cohortFound, setCohortFound] = useState(undefined); const redirect = getStorageItem('redirect'); const redirectedFrom = getStorageItem('redirected-from'); const router = useRouter(); @@ -89,18 +90,23 @@ function Summary() { }); }, []); - const redirectTocohort = (cohort) => { - const langLink = lang !== 'en' ? `/${lang}` : ''; - const syllabusVersion = cohort?.syllabus_version; + console.log(cohortFound); - axiosInstance.defaults.headers.common.Academy = cohort.academy.id; - const cohortDashboardLink = `${langLink}/cohort/${cohort?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; - setCohortSession({ - ...cohort, - selectedProgramSlug: cohortDashboardLink, - }); - router.push(cohortDashboardLink); + const redirectTocohort = () => { + if (isPaymentSuccess && cohortFound) { + const langLink = lang !== 'en' ? `/${lang}` : ''; + const syllabusVersion = cohortFound?.syllabus_version; + + axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; + const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; + setCohortSession({ + ...cohortFound, + selectedProgramSlug: cohortDashboardLink, + }); + router.push(cohortDashboardLink); + } else setPaymentStatus('idle'); }; + const joinCohort = (cohort) => { reportDatalayer({ dataLayer: { @@ -122,7 +128,7 @@ function Summary() { setReadyToRefetch(false); } if (dataRequested?.id) { - redirectTocohort(cohort); + setCohortFound(cohort); } }) .catch(() => { @@ -242,11 +248,13 @@ function Summary() { } } if (respPayment.status === 'FULFILLED') { + setPaymentStatus('success'); + setReadyToRefetch(true); + setIsSubmitting(true); setSelectedPlanCheckoutData({ ...selectedPlanCheckoutData, payment_success: true, }); - setPaymentStatus('success'); } }) .catch(() => { @@ -261,6 +269,7 @@ function Summary() { }); } }; + useEffect(() => { setHasMounted(true); }, []); @@ -486,14 +495,7 @@ function Summary() { variant="default" // mt="12px" isLoading={isSubmitting} - onClick={() => { - if (isPaymentSuccess) { - setIsSubmitting(true); - setReadyToRefetch(true); - } else { - setPaymentStatus('idle'); - } - }} + onClick={() => redirectTocohort()} > {isPaymentSuccess ? t('start-free-course') : t('try-again')} From b1c6d1381044589233b155b7ad3422ac6eee6936 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Thu, 19 Sep 2024 16:50:49 -0400 Subject: [PATCH 09/67] :zap: user is added inmedialy to the cohort when paymen is successful --- src/js_modules/checkout/PaymentInfo.jsx | 19 +++++++++----- src/js_modules/checkout/Summary.jsx | 35 ++++++++++++++----------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx index 775be9c4f..bba24265d 100644 --- a/src/js_modules/checkout/PaymentInfo.jsx +++ b/src/js_modules/checkout/PaymentInfo.jsx @@ -49,6 +49,7 @@ function PaymentInfo() { const redirect = getStorageItem('redirect'); const redirectedFrom = getStorageItem('redirected-from'); const router = useRouter(); + const { backgroundColor, fontColor, hexColor } = useStyle(); const isPaymentSuccess = paymentStatus === 'success'; const isPaymentIdle = paymentStatus === 'idle'; @@ -67,6 +68,7 @@ function PaymentInfo() { router.push(cohortDashboardLink); } else setPaymentStatus('idle'); }; + const joinCohort = (cohort) => { reportDatalayer({ dataLayer: { @@ -91,7 +93,8 @@ function PaymentInfo() { setCohortFound(cohort); } }) - .catch(() => { + .catch((error) => { + console.error('Error al unirse a la cohorte:', error); setIsSubmittingPayment(false); setTimeout(() => { setReadyToRefetch(false); @@ -99,8 +102,6 @@ function PaymentInfo() { }); }; - const { backgroundColor, fontColor, hexColor } = useStyle(); - useEffect(() => { reportDatalayer({ dataLayer: { @@ -112,7 +113,7 @@ function PaymentInfo() { useEffect(() => { let interval; - if (readyToRefetch && timeElapsed < 10) { + if (readyToRefetch && timeElapsed < 10 && isPaymentSuccess) { interval = setInterval(() => { getAllMySubscriptions() .then((subscriptions) => { @@ -162,6 +163,13 @@ function PaymentInfo() { return () => clearInterval(interval); }, [readyToRefetch, timeElapsed]); + useEffect(() => { + if (isPaymentSuccess) { + setIsSubmittingPayment(true); + setReadyToRefetch(true); + } + }, [isPaymentSuccess]); + useEffect(() => { if (selectedPlanCheckoutData?.owner?.id) getPaymentMethods(selectedPlanCheckoutData.owner.id); }, [selectedPlanCheckoutData, isAuthenticated]); @@ -216,8 +224,6 @@ function PaymentInfo() { .then((respPayment) => { if (respPayment.status === 'FULFILLED') { setPaymentStatus('success'); - setReadyToRefetch(true); - setIsSubmittingPayment(true); setSelectedPlanCheckoutData({ ...selectedPlanCheckoutData, payment_success: true, @@ -371,6 +377,7 @@ function PaymentInfo() { height="45px" variant="default" // mt="12px" + isDisabled={isPaymentSuccess && !cohortFound} isLoading={isSubmittingPayment} onClick={() => redirectTocohort()} > diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index cc9a2d2e1..d903a7e91 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -90,21 +90,17 @@ function Summary() { }); }, []); - console.log(cohortFound); - const redirectTocohort = () => { - if (isPaymentSuccess && cohortFound) { - const langLink = lang !== 'en' ? `/${lang}` : ''; - const syllabusVersion = cohortFound?.syllabus_version; + const langLink = lang !== 'en' ? `/${lang}` : ''; + const syllabusVersion = cohortFound?.syllabus_version; - axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; - const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; - setCohortSession({ - ...cohortFound, - selectedProgramSlug: cohortDashboardLink, - }); - router.push(cohortDashboardLink); - } else setPaymentStatus('idle'); + axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; + const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; + setCohortSession({ + ...cohortFound, + selectedProgramSlug: cohortDashboardLink, + }); + router.push(cohortDashboardLink); }; const joinCohort = (cohort) => { @@ -131,7 +127,8 @@ function Summary() { setCohortFound(cohort); } }) - .catch(() => { + .catch((error) => { + console.log(error); setTimeout(() => { setReadyToRefetch(false); }, 600); @@ -190,6 +187,13 @@ function Summary() { return () => clearInterval(interval); }, [readyToRefetch, timeElapsed]); + useEffect(() => { + if (isPaymentSuccess) { + setIsSubmitting(true); + setReadyToRefetch(true); + } + }, [isPaymentSuccess]); + const handleSubmit = () => { if (!isPaymentIdle || isSubmitting || !selectedPlanCheckoutData?.plan_id) return; setIsSubmitting(true); @@ -249,8 +253,6 @@ function Summary() { } if (respPayment.status === 'FULFILLED') { setPaymentStatus('success'); - setReadyToRefetch(true); - setIsSubmitting(true); setSelectedPlanCheckoutData({ ...selectedPlanCheckoutData, payment_success: true, @@ -494,6 +496,7 @@ function Summary() { height="45px" variant="default" // mt="12px" + isDisabled={isPaymentSuccess && !cohortFound} isLoading={isSubmitting} onClick={() => redirectTocohort()} > From 89ae1b971a56a28dd4f14f0c3c05e8391c7c714d Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Fri, 20 Sep 2024 11:16:48 -0400 Subject: [PATCH 10/67] small changes to accept node --- src/common/components/CodeViewer.jsx | 6 +++++- src/common/components/MarkDownParser/index.jsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common/components/CodeViewer.jsx b/src/common/components/CodeViewer.jsx index 3f607c0f7..05ec03ea8 100644 --- a/src/common/components/CodeViewer.jsx +++ b/src/common/components/CodeViewer.jsx @@ -33,6 +33,8 @@ export const languagesLabels = { jsx: 'JS', js: 'JS', javascript: 'JS', + node: 'Node.js', + 'node.js': 'Node.js', python: 'Python', py: 'Python', html: 'Html', @@ -47,6 +49,8 @@ export const languagesNames = { jsx: 'javascript', js: 'javascript', javascript: 'javascript', + node: 'javascript', + 'node.js': 'javascript', python: 'python', py: 'python', html: 'html', @@ -60,7 +64,7 @@ function CodeViewer({ languagesData, allowNotLogged, fileContext, ...rest }) { const editorContainerRef = useRef(); const router = useRouter(); const { hexColor } = useStyle(); - const { t, lang } = useTranslation('code-viewer'); + const { t } = useTranslation('code-viewer'); const { isAuthenticated } = useAuth(); const toast = useToast(); const [initialTouchY, setInitialTouchY] = useState(null); diff --git a/src/common/components/MarkDownParser/index.jsx b/src/common/components/MarkDownParser/index.jsx index a2ab5e5c4..978106962 100644 --- a/src/common/components/MarkDownParser/index.jsx +++ b/src/common/components/MarkDownParser/index.jsx @@ -264,6 +264,7 @@ function MarkDownParser({ const emptyCodeRegex = /```([a-zA-Z]+).*runable=("true"|true|'true').*(\n{1,}|\s{1,}\n{1,})?```/gm; //This regex is to wrap all the runable codeblocks inside of a tag const codeViewerRegex = /(```(?\w+).*runable=("true"|'true'|true).*(?(?:.|\n)*?)```\n?)+/gm; + // const codeViewerRegex = /(```(?[\w.]+).*runable=("true"|'true'|true).*(?(?:.|\n)*?)```\n?)+/gm; const removedEmptyCodeViewers = content?.length > 0 ? content.replace(emptyCodeRegex, () => '') : ''; const contentReplace = removedEmptyCodeViewers.replace(codeViewerRegex, (match) => `
\n${match}
\n`); From 255f327c72dd980f3be94c70849ab9d3793a013e Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Sun, 22 Sep 2024 22:16:58 -0400 Subject: [PATCH 11/67] small changes --- src/common/components/PricingCard.jsx | 1 + src/pages/checkout/index.jsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/components/PricingCard.jsx b/src/common/components/PricingCard.jsx index d95a09bb2..543e3da0b 100644 --- a/src/common/components/PricingCard.jsx +++ b/src/common/components/PricingCard.jsx @@ -105,6 +105,7 @@ export default function PricingCard({ item, courseData, isFetching, relatedSubsc const alreadyHaveIt = relatedSubscription?.plans?.[0]?.slug === item?.plan_slug; const handlePlan = () => { + console.log('EJECUTE'); const langPath = lang === 'en' ? '' : `/${lang}`; const qs = parseQuerys({ plan: selectedFinancing?.plan_slug || item?.plan_slug, diff --git a/src/pages/checkout/index.jsx b/src/pages/checkout/index.jsx index 3eaf48843..daa47465c 100644 --- a/src/pages/checkout/index.jsx +++ b/src/pages/checkout/index.jsx @@ -113,6 +113,8 @@ function Checkout() { const accessToken = getStorageItem('accessToken'); const tokenExists = accessToken !== null && accessToken !== undefined && accessToken.length > 5; const { coupon: couponQuery } = query; + const { course } = router.query; + const [coupon] = usePersistentBySession('coupon', ''); const couponValue = useMemo(() => { @@ -121,7 +123,6 @@ function Checkout() { return couponString || formatedCouponQuery; }, [coupon, couponQuery]); - const { course } = router.query; const courseChoosed = course; const queryPlanExists = planFormated !== undefined && planFormated?.length > 0; From b46523fad79d1ca5a8d2ec78e7cbe6bbca58ba8f Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Sun, 22 Sep 2024 23:17:15 -0400 Subject: [PATCH 12/67] small change --- src/pages/mentorship/schedule.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 9cd560e58..8ca8c19f4 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -104,7 +104,7 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', - syllabus: allSyllabus?.join(','), + syllabus: allSyllabus?.join(',') || undefined, academy: academy.id, }).getMentor(); From 7a798554c9fc82bbd1b45b99729bc1912bb86a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20M=C3=A1rquez?= <47191295+gustavomm19@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:16:04 +0000 Subject: [PATCH 13/67] new clone modal design --- public/locales/en/common.json | 157 +++++++++++++--- public/locales/es/common.json | 85 ++++++++- public/static/images/apple.png | Bin 0 -> 642 bytes public/static/images/linux.png | Bin 0 -> 2239 bytes public/static/images/windows.png | Bin 0 -> 547 bytes .../components/MarkDownParser/index.jsx | 62 +------ .../syllabus/ModalToCloneProject.jsx | 167 ++++++++++++++---- .../syllabus/SyllabusMarkdownComponent.jsx | 4 +- .../[lesson]/[lessonSlug]/index.jsx | 10 -- 9 files changed, 360 insertions(+), 125 deletions(-) create mode 100644 public/static/images/apple.png create mode 100644 public/static/images/linux.png create mode 100644 public/static/images/windows.png diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 55e34aadb..078850e36 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -60,17 +60,17 @@ "enroll-totally-free": "Enroll totally free", "enroll-for-connector": "Enroll for", "learn-more": "Learn more", - "more-technologies":"More technologies", - "continue-learning":"Continue learning about:", + "more-technologies": "More technologies", + "continue-learning": "Continue learning about:", "technologies-and-more": "{{technologies}} and more.", "user-id": "User ID", "day": "Day", "days": "Days", "short-seconds": "Sec", "interactive": "Interactive", - "beginner": "Beginner", - "easy": "Easy", - "intermediate": "Intermediate", + "beginner": "Beginner", + "easy": "Easy", + "intermediate": "Intermediate", "hard": "Hard", "taken-by": "Taken by", "attended": "Attended", @@ -113,7 +113,7 @@ "clear-all": "Clear all", "english": "English", "spanish": "Spanish", - "remaining":"Remaining", + "remaining": "Remaining", "show-more": "Show more", "show-less": "Show less", "seach-technology": "Search technology", @@ -149,13 +149,96 @@ "type": "dropdown" }, "clone-title": "How to clone a project?", - "cloneInstructions": "This exercise can be downloaded and run locally if you have node.js installed (installation steps).\n\n Once you have node.js, it's time to install learnpack and clone this project into your computer by typing the following command on your terminal:\n\n``` bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNote: This will create a new folder \"{{repoName}}\" in your computer.\n\nIf you want to use VSCode: Make sure you have the LearnPack extension installed, open the folder in VSCode and type `learnpack start` on your vscode terminal.\n\nTo run without VSCode: Use your computer terminal to get inside your recently created folder and start learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nRead the README.md file and follow the rest of the instructions." + "cloneInstructions": "This exercise can be downloaded and run locally if you have node.js installed (installation steps).\n\n Once you have node.js, it's time to install learnpack and clone this project into your computer by typing the following command on your terminal:\n\n``` bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNote: This will create a new folder \"{{repoName}}\" in your computer.\n\nIf you want to use VSCode: Make sure you have the LearnPack extension installed, open the folder in VSCode and type `learnpack start` on your vscode terminal.\n\nTo run without VSCode: Use your computer terminal to get inside your recently created folder and start learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nRead the README.md file and follow the rest of the instructions.", + "clone-modal": { + "title": "Open locally on your computer", + "need-help": "Need help? Schedule a mentoring session", + "description": "This exercise can be downloaded and run locally on your computer by following these steps:", + "select-os": "1. Select your operating system", + "os-list": [ + { + "name": "macos", + "label": "macOS", + "logo": "/static/images/apple.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + }, + { + "name": "linux", + "label": "Linux", + "logo": "/static/images/linux.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + }, + { + "name": "windows", + "label": "Windows", + "logo": "/static/images/windows.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + } + ] + } }, "upgrade-plan": { "title": "We are sorry, you don't have enough privileges to access this block of content, please signup or upgrade your plan to access it.", "button": "Upgrade plan" }, - "asset-type-pronouns" : { + "asset-type-pronouns": { "lesson": "This lesson", "exercise": "This exercise", "project": "This project", @@ -251,15 +334,17 @@ "sub-description": "Join a cohort in a live classroom environment and unlimited support until you find a job.", "service_items": { "title": "Everything on Live & with Feedback plus...", - "items": [{ - "label": "Unlimited live workshops " - }, - { - "label": "Unlimited one-on-one mentoring sessions" - }, - { - "label": "Unlimited group mentoring sessions" - }] + "items": [ + { + "label": "Unlimited live workshops " + }, + { + "label": "Unlimited one-on-one mentoring sessions" + }, + { + "label": "Unlimited group mentoring sessions" + } + ] }, "featured_info": [ { @@ -268,7 +353,11 @@ "title": "Live classes", "icon": "onlinePeople" }, - "features": [{ "description": "Take part of a group of students learning together with a teacher on weekly live classes." }] + "features": [ + { + "description": "Take part of a group of students learning together with a teacher on weekly live classes." + } + ] }, { "service": { @@ -276,7 +365,11 @@ "title": "Career support", "icon": "support" }, - "features": [{ "description": "Get feedback on your resume, LinkedIn and other online profiles, and access our vast hiring partner network, internship opportunities and more." }] + "features": [ + { + "description": "Get feedback on your resume, LinkedIn and other online profiles, and access our vast hiring partner network, internship opportunities and more." + } + ] }, { "service": { @@ -284,7 +377,11 @@ "title": "Job guarantee*", "icon": "briefcase" }, - "features": [{ "description": "Get a job within 6 months upon graduation or get money back; conditions apply." }] + "features": [ + { + "description": "Get a job within 6 months upon graduation or get money back; conditions apply." + } + ] }, { "service": { @@ -292,7 +389,11 @@ "title": "Certified by U.S. FL Dep. of Education", "icon": "certificate" }, - "features": [{ "description": "Our bootcamp certificates have a badge certified by the US Florida Dep. of Education." }] + "features": [ + { + "description": "Our bootcamp certificates have a badge certified by the US Florida Dep. of Education." + } + ] }, { "service": { @@ -300,7 +401,11 @@ "title": "Access to premium community", "icon": "crown" }, - "features": [{ "description": "Our premium community gives you access to office space worldwide, events inside partner companies and employers, job fairs, networking events, an exclusive slack channel, and more perks." }] + "features": [ + { + "description": "Our premium community gives you access to office space worldwide, events inside partner companies and employers, job fairs, networking events, an exclusive slack channel, and more perks." + } + ] }, { "service": { @@ -308,9 +413,13 @@ "title": "Office space world-wide", "icon": "office" }, - "features": [{ "description": "Get access to office space world-wide." }] + "features": [ + { + "description": "Get access to office space world-wide." + } + ] } ] }, "hrs-average": "{{number}}hrs average" -} +} \ No newline at end of file diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 7c9df9e63..f253beda2 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -148,7 +148,90 @@ "type": "dropdown" }, "clone-title": "¿Cómo clonar un proyecto?", - "cloneInstructions": "Este ejercicio se puede descargar y ejecutar localmente si tienes node.js instalado (pasos de instalación).\n\n Si ya tienes node, toca instalar learnpack y clonar el proyecto en tu computadora escribiendo el siguiente comando en tu terminal:\n\n```bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNota: Esto creará una nueva carpeta \"{{repoName}}\" en tu computadora con el código del proyecto dentro.\n\nSi quieres usar VSCode: asegúrate de tener el LearnPack extension instalado, abre la carpeta en VSCode y escribe `learnpack start` en tu terminal de vscode.\n\nPara realizar los ejercicios sin VSCode: abre tu terminal en la carpeta recién creada y comienza el programa learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nLee el archivo README.md y sigue el resto de las instrucciones." + "cloneInstructions": "Este ejercicio se puede descargar y ejecutar localmente si tienes node.js instalado (pasos de instalación).\n\n Si ya tienes node, toca instalar learnpack y clonar el proyecto en tu computadora escribiendo el siguiente comando en tu terminal:\n\n```bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNota: Esto creará una nueva carpeta \"{{repoName}}\" en tu computadora con el código del proyecto dentro.\n\nSi quieres usar VSCode: asegúrate de tener el LearnPack extension instalado, abre la carpeta en VSCode y escribe `learnpack start` en tu terminal de vscode.\n\nPara realizar los ejercicios sin VSCode: abre tu terminal en la carpeta recién creada y comienza el programa learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nLee el archivo README.md y sigue el resto de las instrucciones.", + "clone-modal": { + "title": "Abrir localmente en tu computadora", + "need-help": "¿Necesitas ayuda? Programa una sesión de tutoría", + "description": "Este ejercicio se puede descargar y ejecutar localmente en su computadora siguiendo estos pasos:", + "select-os": "1. Seleccione su sistema operativo", + "os-list": [ + { + "name": "macos", + "label": "macOS", + "logo": "/static/images/apple.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + }, + { + "name": "linux", + "label": "Linux", + "logo": "/static/images/linux.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + }, + { + "name": "windows", + "label": "Windows", + "logo": "/static/images/windows.png", + "steps": [ + { + "label": "Instal node.js on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Instal Learnpack on your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + }, + { + "label": "Clone project into your computer", + "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", + "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", + "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" + } + ] + } + ] + } }, "upgrade-plan": { "title": "Lo sentimos, no tiene suficientes privilegios para acceder a este bloque de contenido, regístrese o actualice su plan para acceder al contenido.", diff --git a/public/static/images/apple.png b/public/static/images/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..165f27ad0cbe909083c7541d0e29676f539b509e GIT binary patch literal 642 zcmV-|0)737P)Nv?7JpUTDy}9&~{#uJSXrZ+x%~KM4XbU!g`1O<{M-NFMWdo{s2zxe1mu3)IM~8fIs2}#hUTpwnM-x z@M(qsynug$Q;Ba-zz^Wmi*HcCy=5n&5_ozHGq`Bw8~e2$aLI z*aCy}68l_88WknDKiWh}XiP#1 zYH10TKoKOx{6cLM+3lY;RaKwUl9&_`oK|g9Bzy+d4GK7Cll#DTx6_$f-`*Z~J`U>0 zG`>fgyV;r9clVxo=biU`=VaVGJb?71B*|@*5*45U))qh>a|z`INm8h_xLgI4wZmN+L>;g-A&>tFo=hZXD6b%ojF560=JYIC zvIOnz_oKMDNVHT`REX;>TQ-}cD20kVG9pK!flZrQ(b(9C7hl{hu6ONv4h;k4%tr8>V<_YpZ*X961WLvK03;6r!xW0?$6b2cb|1Znq1gqkcAyGOeZu zkw+Rxt^?PvZ>DW)3s7f&2L%qmRp7#JheJ@;bmG8kzr*yxHoVPg~}T!D~Mp+7?n32vmj?1{qYnHT%53r`qjw@&J3Nyl}~i| z|Kq{LRfn+C9tQ4jBabxXtkI*FD6TE(#D$BmqBwK~!Jr+UyB|c|1~0yj6X@^%wIKW5 zcZYW}@1qa5ignvMn4vdThAjCp{F*Q0IL+%pQ0g$RW!;D2KEJ51{kAvRZjsUKRS}mo zv#&rOfUhqwYrD8-yN0-~iaH?yy!ZM*+Orew31oPFjzrsf8XH%7wzPL*-2Ww9yZm?D zRk;DROYcTiRTY*kTZR)Seu?4XVfIWkQL1P_%!gY6Av;;oL|w-a`_wXyi#R=yV#mzu zIJ((qpQuYxvR!9+7|mN_5T~eY4JZ2|b{}}2hLdc)C(&-)?`3^mMhEq?4`UM6Z|_QZ zw>8^aahgpiC)p|sq+7faZj2^HAqU=k^H*50Vg;&~u15X6kK&$WznI^+XK}f`siXeR$KH4XmZy-)(p?Cjdk3E9w z>T1~Sc1CtRmM>opm&?TnO*$=YZTBU`6T{p{DP z#wGEC$h#ArS&3$3^>fl5dgwvK<8jdzjYdUX*L6`3hrfmRP^b&U25;qWHvD?^fOoo9hM9Upcr$Z!(Dl03Q*)fLNY&Iy0BCdIzYqrDT5X2uk z^m8$WlRg8sWVyYbMF%4`B-xG6u2k@c~9M*AzARXfkTnIf*)b0j4Qs z?SOgDW(ZYBbwP(jemPQ#iCe|i$-&BFN8;oyv*D= ziCjU!mM6_pV3B2MPg(vc;|*|EOXemdLm$Vf*L#?Z?lcdAlSo;FO5|44U2A^a%Q{Ld zat@k(?ieGMj#&D-GWzhxe&FaC>2uSUzgtQVA3jXu@u*qC;UJx!j-<4u^iRA`KYZ^H zEm>roA(oWfLEGE6@-sy*{-2=rc+mW$D#~2rWi*gx3_qNy%uICb)^G_u1&c_r?)9kd0yGlIs?A0{!PU~m?E0V2rfD>{+b_NJ zGS;qb7JW+>OEAP3H&UG<5{_cxQV8eIO=0vxXn&LAxV-7Hu0>prCZ62(3BEV`CT0=ua&rVq&;GCX$TkbPQ9UOykl=VN6Ww zzGw`)@89#O54R8?ejcE-J{LUu&VKs?p%Cq`JMU93JeO2) z2ZrAM;IIDV?NoRujNlfx1DKkOvZJo6EPYB?o;r??C!z?3rSG-x4ZeZ}jx=BG z9OUlAuAxdARt}v^KNA&N~)U|9EZ--MP)~_Qo$c z#$Q^mFXd*JlPEvv(Am%M%rWhKg@mnqmeK{)(xwx45A%LH_U_B+Y_Xh0bN_@D7_hiA z{ZH%LepRODuH)te;YS*d)2gkFEtcGR*cah3Q)s`_aSoRGTQrwk*Ew03neN5 { const cleanedSubTasks = subTasks.filter((task) => task.id !== taskProps.id); @@ -206,21 +200,8 @@ function MarkDownParser({ } }, [subTasksLoaded, subTasks, newSubTasks]); - const { - token, assetSlug, gitpod, interactive, - } = callToActionProps; const assetType = currentData?.asset_type; - const provisioningLinks = [{ - title: t('learnpack.new-exercise'), - link: `${BREATHECODE_HOST}/v1/provisioning/me/container/new?token=${token}&cohort=${cohortSession?.id}&repo=${currentData?.url}`, - isExternalLink: true, - }, - { - title: t('learnpack.continue-exercise'), - link: `${BREATHECODE_HOST}/v1/provisioning/me/workspaces?token=${token}&cohort=${cohortSession?.id}&repo=${currentData?.url}`, - isExternalLink: true, - }]; // const newLineBeforeCloseTag = /<\//gm; // const formatedContent = content.replace(newLineBeforeCloseTag, '\n$&'); @@ -238,26 +219,6 @@ function MarkDownParser({ anchors.add('.markdown-body p'); anchors.add('.markdown-body pre'); }, [content]); - useEffect(() => { - const openInLearnpackAction = t('learnpack.open-in-learnpack-button', {}, { returnObjects: true }); - const localhostAction = { - text: `${t('learnpack.open-locally')}${cohortSession?.available_as_saas ? ` (${t('learnpack.recommended')})` : ''}`, - type: 'button', - onClick: () => { - setShowCloneModal(true); - }, - }; - const cloudActions = { - ...openInLearnpackAction, - text: `${openInLearnpackAction.text}${cohortSession?.available_as_saas === false ? ` (${t('learnpack.recommended')})` : ''}`, - links: provisioningLinks, - }; - if (cohortSession?.id) { - if (!gitpod) setLearnpackActions([localhostAction]); - else if (cohortSession.available_as_saas) setLearnpackActions([localhostAction, cloudActions]); - else setLearnpackActions([cloudActions, localhostAction]); - } - }, [token, assetSlug, lang, cohortSession?.id, currentData?.url]); const preParsedContent = useMemo(() => { //This regex is to remove the runable empty codeblocks @@ -316,19 +277,8 @@ function MarkDownParser({ + callToAction={currentData?.interactive && ( + )} content={frontMatter} currentData={currentData} @@ -387,7 +337,6 @@ function MarkDownParser({ MarkDownParser.propTypes = { content: PropTypes.string, - callToActionProps: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.object, PropTypes.string, PropTypes.array])), withToc: PropTypes.bool, frontMatter: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.object, PropTypes.string, PropTypes.array])), titleRightSide: PropTypes.node, @@ -403,7 +352,6 @@ MarkDownParser.propTypes = { }; MarkDownParser.defaultProps = { content: '', - callToActionProps: {}, withToc: false, frontMatter: {}, titleRightSide: null, diff --git a/src/js_modules/syllabus/ModalToCloneProject.jsx b/src/js_modules/syllabus/ModalToCloneProject.jsx index 1c303d436..666f03fd4 100644 --- a/src/js_modules/syllabus/ModalToCloneProject.jsx +++ b/src/js_modules/syllabus/ModalToCloneProject.jsx @@ -1,51 +1,158 @@ +import { useState } from 'react'; +import { + Box, + Button, + Modal, + ModalCloseButton, + ModalContent, + ModalOverlay, + Image, + Accordion, + AccordionItem, + AccordionButton, + AccordionPanel, + AccordionIcon, + Checkbox, +} from '@chakra-ui/react'; import useTranslation from 'next-translate/useTranslation'; import PropTypes from 'prop-types'; -import MarkDownParser from '../../common/components/MarkDownParser'; -import SimpleModal from '../../common/components/SimpleModal'; +import Heading from '../../common/components/Heading'; +import Text from '../../common/components/Text'; +import NextChakraLink from '../../common/components/NextChakraLink'; +import ReactPlayerV2 from '../../common/components/ReactPlayerV2'; +import useStyle from '../../common/hooks/useStyle'; +import useCohortHandler from '../../common/hooks/useCohortHandler'; -function ModalToCloneProject({ isOpen, onClose, currentAsset }) { +function ModalToCloneProject({ isOpen, onClose }) { const { t } = useTranslation('syllabus'); + const { state } = useCohortHandler(); + const { cohortSession } = state; + const [selectedOs, setSelectedOs] = useState(null); + const [expanded, setExpanded] = useState(0); + const { featuredLight, hexColor, borderColor } = useStyle(); - const urlToClone = currentAsset?.url || currentAsset?.readme_url?.split('/blob')?.[0]; - const repoName = urlToClone?.split('/')?.pop(); + const osList = t('common:learnpack.clone-modal.os-list', {}, { returnObjects: true }); + + const resetSelector = () => { + setSelectedOs(null); + setExpanded(0); + }; return ( - - - + + + + + + + + + {t('common:learnpack.clone-modal.title')} + + + {t('common:learnpack.clone-modal.description')} + + {!selectedOs && ( + + + {t('common:learnpack.clone-modal.select-os')} + + + {osList.map((os) => ( + setSelectedOs(os)} + _active={{ + background: hexColor.featuredColor, + border: '1px solid', + borderColor: hexColor.blueDefault, + }} + > + {os.label} + + ))} + + + )} + {selectedOs && ( + + + setExpanded(val)} allowToggle display="flex" flexDirection="column" gap="10px"> + {selectedOs.steps.map((step, i) => ( + + + + + + {`${i + 2}.`} + {' '} + {step.label} + + + + + + + {step.description} + + {step.source && ( + + {t('common:learn-more')} + + )} + + + ))} + + + )} + + {cohortSession?.available_as_saas && ( + + {t('common:learnpack.clone-modal.need-help')} + {' '} + → + + )} + + + {selectedOs && ( + <> + + + )} + + + + ); } ModalToCloneProject.propTypes = { isOpen: PropTypes.bool, onClose: PropTypes.func, - currentAsset: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.any])), }; ModalToCloneProject.defaultProps = { isOpen: false, onClose: () => {}, - currentAsset: null, }; export default ModalToCloneProject; diff --git a/src/js_modules/syllabus/SyllabusMarkdownComponent.jsx b/src/js_modules/syllabus/SyllabusMarkdownComponent.jsx index ddc40ac45..a77c5f3ba 100644 --- a/src/js_modules/syllabus/SyllabusMarkdownComponent.jsx +++ b/src/js_modules/syllabus/SyllabusMarkdownComponent.jsx @@ -4,7 +4,7 @@ import MarkDownParser from '../../common/components/MarkDownParser'; import { MDSkeleton } from '../../common/components/Skeleton'; function SyllabusMarkdownComponent({ - ipynbHtmlUrl, readme, currentBlankProps, callToActionProps, currentData, lesson, + ipynbHtmlUrl, readme, currentBlankProps, currentData, lesson, quizSlug, lessonSlug, currentTask, alerMessage, isGuidedExperience, }) { const { t } = useTranslation('syllabus'); @@ -14,7 +14,6 @@ function SyllabusMarkdownComponent({ return ( Date: Tue, 24 Sep 2024 12:07:57 +0000 Subject: [PATCH 14/67] remove old clone modal --- .../components/MarkDownParser/index.jsx | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/src/common/components/MarkDownParser/index.jsx b/src/common/components/MarkDownParser/index.jsx index 142a5c9ca..2c27febe9 100644 --- a/src/common/components/MarkDownParser/index.jsx +++ b/src/common/components/MarkDownParser/index.jsx @@ -8,7 +8,6 @@ import remarkGemoji from 'remark-gemoji'; import PropTypes from 'prop-types'; import rehypeRaw from 'rehype-raw'; import { Img } from '@chakra-ui/react'; -import useTranslation from 'next-translate/useTranslation'; import AnchorJS from 'anchor-js'; import bc from '../../services/breathecode'; import OpenWithLearnpackCTA from '../../../js_modules/syllabus/OpenWithLearnpackCTA'; @@ -24,7 +23,6 @@ import ContentHeading from './ContentHeading'; import CodeViewer, { languagesLabels, languagesNames } from '../CodeViewer'; import SubTasks from './SubTasks'; import DynamicCallToAction from '../DynamicCallToAction'; -import SimpleModal from '../SimpleModal'; function MarkdownH2Heading({ children }) { return ( @@ -140,13 +138,11 @@ function MarkDownParser({ content, withToc, frontMatter, titleRightSide, currentTask, isPublic, currentData, showLineNumbers, showInlineLineNumbers, assetData, alerMessage, isGuidedExperience, showContentHeading, }) { - const { t } = useTranslation('common'); const [subTasksLoaded, setSubTasksLoaded] = useState(false); const [newSubTasks, setNewSubTasks] = useState([]); const [fileContext, setFileContext] = useState(''); const { subTasks, setSubTasks } = useModuleHandler(); const [profile] = usePersistent('profile', {}); - const [showCloneModal, setShowCloneModal] = useState(false); const updateSubTask = async (taskProps) => { const cleanedSubTasks = subTasks.filter((task) => task.id !== taskProps.id); @@ -243,36 +239,8 @@ function MarkDownParser({ return contentReplace; }, [content]); - const urlToClone = currentData?.url || currentData?.readme_url?.split('/blob')?.[0]; - const repoName = urlToClone?.split('/')?.pop(); - return ( <> - { - setShowCloneModal(false); - }} - headerStyles={{ - textAlign: 'center', - textTransform: 'uppercase', - }} - bodyStyles={{ - className: 'markdown-body', - padding: { base: '10px 30px' }, - }} - > - - {showContentHeading && ( Date: Tue, 24 Sep 2024 10:51:06 -0400 Subject: [PATCH 15/67] Update dashboard.json --- public/locales/en/dashboard.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 071ce11ca..2bcf572ac 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -109,7 +109,7 @@ "mentor-sessions-available": "mentor sessions available", "action": "Schedule session", "no-available": "No services available", - "no-available-for-teacher": "No serives available for" + "no-available-for-teacher": "No services available for" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", @@ -204,4 +204,4 @@ "title": "Welcome to 4Geeks!", "description": "Watch this short video that explains how to get the most out of 4Geeks and enhance your learning experience" } -} \ No newline at end of file +} From bbc1b6bb1f7725d7e65a5f01b782471ce07aee8e Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:58:42 -0400 Subject: [PATCH 16/67] Update common.json --- public/locales/en/common.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 078850e36..0e53eed7d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -162,13 +162,13 @@ "logo": "/static/images/apple.png", "steps": [ { - "label": "Instal node.js on your computer", + "label": "Install node.js on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" }, { - "label": "Instal Learnpack on your computer", + "label": "Install Learnpack on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" @@ -187,13 +187,13 @@ "logo": "/static/images/linux.png", "steps": [ { - "label": "Instal node.js on your computer", + "label": "Install node.js on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" }, { - "label": "Instal Learnpack on your computer", + "label": "Install Learnpack on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" @@ -212,13 +212,13 @@ "logo": "/static/images/windows.png", "steps": [ { - "label": "Instal node.js on your computer", + "label": "Install node.js on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" }, { - "label": "Instal Learnpack on your computer", + "label": "Install Learnpack on your computer", "description": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec qu", "video": "https://www.youtube.com/watch?v=1I6IiRVYHEo", "source": "https://4geeks.com/how-to/install-node-nvm-mac-osx" @@ -422,4 +422,4 @@ ] }, "hrs-average": "{{number}}hrs average" -} \ No newline at end of file +} From 7a06d819ec84b33148a4795ac18789b77ea55c42 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:33:39 -0400 Subject: [PATCH 17/67] Update dashboard.json --- public/locales/en/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 2bcf572ac..f244b1e22 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -106,7 +106,7 @@ "you-have": "You have", "available-sessions": "available sessions", "tooltip": "Mentorships reload every week", - "mentor-sessions-available": "mentor sessions available", + "mentor-sessions-available": "mentoring sessions available", "action": "Schedule session", "no-available": "No services available", "no-available-for-teacher": "No services available for" From 4c5780eadeeab8b420bf390d66189ca16e29e91d Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:34:39 -0400 Subject: [PATCH 18/67] Update dashboard.json --- public/locales/es/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 9556e3c10..d855f7cf7 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -4,7 +4,7 @@ }, "title": "Tus noticias", "moduleMap": "Mapa de módulos", - "back-to-dashboard": "Volver a dashboard", + "back-to-dashboard": "Volver al dashboard", "backToChooseProgram": "Volver a elegir programa", "progressText": "Progreso en el programa", "whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.", From 5d833d8efa3783bdec31f358d2ed55f25becf10f Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 24 Sep 2024 15:57:34 -0400 Subject: [PATCH 19/67] small chagnes --- src/common/components/MktShowPrices.jsx | 5 ----- src/common/components/PricingCard.jsx | 2 -- src/js_modules/checkout/ChooseYourClass.jsx | 1 + src/pages/bootcamp/[course_slug].jsx | 5 +---- src/pages/checkout/index.jsx | 19 +++++++++++++++++++ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/common/components/MktShowPrices.jsx b/src/common/components/MktShowPrices.jsx index a880f3b49..c32270ef3 100644 --- a/src/common/components/MktShowPrices.jsx +++ b/src/common/components/MktShowPrices.jsx @@ -161,14 +161,9 @@ function MktShowPrices({ id, externalPlanProps, cohortId, title, gridColumn1, gr firstSectionTitle={t('subscription.upgrade-modal.subscription')} secondSectionTitle={t('subscription.upgrade-modal.finance')} handleUpgrade={(item) => { - const period = item?.period; - const querys = parseQuerys({ plan: item?.plan_slug, plan_id: item?.plan_id, - price: item?.price, - period, - cohort: cohortId, coupon: queryCoupon || coupon, }); router.push(`/checkout${querys}`); diff --git a/src/common/components/PricingCard.jsx b/src/common/components/PricingCard.jsx index 543e3da0b..2be53c333 100644 --- a/src/common/components/PricingCard.jsx +++ b/src/common/components/PricingCard.jsx @@ -110,8 +110,6 @@ export default function PricingCard({ item, courseData, isFetching, relatedSubsc const qs = parseQuerys({ plan: selectedFinancing?.plan_slug || item?.plan_slug, plan_id: selectedFinancing?.plan_id || item?.plan_id, - price: selectedFinancing?.price || item?.price, - period: selectedFinancing?.period || item?.period, coupon: coupon || queryCoupon, cohort: courseData?.cohort?.id, }); diff --git a/src/js_modules/checkout/ChooseYourClass.jsx b/src/js_modules/checkout/ChooseYourClass.jsx index 6e9028903..5fe8fbaa9 100644 --- a/src/js_modules/checkout/ChooseYourClass.jsx +++ b/src/js_modules/checkout/ChooseYourClass.jsx @@ -87,6 +87,7 @@ function ChooseYourClass({ }; }); + //SI NO AÑADO COHORTS ENTONCES TENGO QUE SABER LA IMPORTANCIA const filteredCohorts = Array.isArray(formatedData) ? formatedData.filter((item) => item?.never_ends === false) : null; setCohorts({ cohorts: filteredCohorts, diff --git a/src/pages/bootcamp/[course_slug].jsx b/src/pages/bootcamp/[course_slug].jsx index 19ed60506..cf7befc04 100644 --- a/src/pages/bootcamp/[course_slug].jsx +++ b/src/pages/bootcamp/[course_slug].jsx @@ -207,10 +207,7 @@ function Page({ data }) { plan: featuredPlanToEnroll?.plan_slug, plan_id: featuredPlanToEnroll?.plan_id, has_available_cohorts: planData?.has_available_cohorts, - price: featuredPlanToEnroll?.price, - period: featuredPlanToEnroll?.period, - cohort: cohortId, - }) : `?plan=${data?.plan_slug}&cohort=${cohortId}`; + }) : `?plan=${data?.plan_slug}`; const getPlanPrice = () => { if (featuredPlanToEnroll?.plan_slug) { diff --git a/src/pages/checkout/index.jsx b/src/pages/checkout/index.jsx index daa47465c..c586bf1a4 100644 --- a/src/pages/checkout/index.jsx +++ b/src/pages/checkout/index.jsx @@ -100,6 +100,8 @@ function Checkout() { const cohorts = cohortsData?.cohorts; + console.log(checkoutData); + axiosInstance.defaults.headers.common['Accept-Language'] = router.locale; const { user, isAuthenticated, isLoading } = useAuth(); const { userSession } = useSession(); @@ -162,6 +164,23 @@ function Checkout() { }; const handleCoupon = (coupons, actions) => { + // Verificar si el cupón ingresado ya está aplicado + const alreadyAppliedCoupon = selfAppliedCoupon?.slug === discountCode || selfAppliedCoupon?.slug === couponValue; + + if (alreadyAppliedCoupon) { + toast({ + position: 'top', + title: t('alert-message:coupon-already-applied'), + status: 'info', + duration: 4000, + isClosable: true, + }); + if (actions) { + actions.setSubmitting(false); + } + return; + } + bc.payment({ coupons: [coupons || discountCode], plan: planFormated, From e9c1d094680038dfc6314c6e7360c23cf4ace2cc Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 24 Sep 2024 16:42:40 -0400 Subject: [PATCH 20/67] small change --- src/common/components/MktShowPrices.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/components/MktShowPrices.jsx b/src/common/components/MktShowPrices.jsx index c32270ef3..400b6e044 100644 --- a/src/common/components/MktShowPrices.jsx +++ b/src/common/components/MktShowPrices.jsx @@ -164,6 +164,7 @@ function MktShowPrices({ id, externalPlanProps, cohortId, title, gridColumn1, gr const querys = parseQuerys({ plan: item?.plan_slug, plan_id: item?.plan_id, + cohort: cohortId, coupon: queryCoupon || coupon, }); router.push(`/checkout${querys}`); From d2edf949a252c5cfab7eab353edd33cde1e47215 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Thu, 5 Sep 2024 17:07:34 -0400 Subject: [PATCH 21/67] :sparkles: new url for students to schedule mentorships --- i18n.js | 1 + public/locales/en/dashboard.json | 17 +- public/locales/es/dashboard.json | 17 +- src/pages/mentorship/schedule.jsx | 706 ++++++++++++++++++++++++++++++ 4 files changed, 731 insertions(+), 10 deletions(-) create mode 100644 src/pages/mentorship/schedule.jsx diff --git a/i18n.js b/i18n.js index c81e33453..f85d909ff 100644 --- a/i18n.js +++ b/i18n.js @@ -25,6 +25,7 @@ module.exports = { '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'], '/survey/[surveyId]': ['survey'], '/mentorship': ['mentorship'], + '/mentorship/schedule': ['mentorship', 'dashboard'], '/how-to': ['how-to'], '/pricing': ['pricing', 'signup'], '/how-to/[slug]': ['how-to'], diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 8b8a5453d..49691c178 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -15,9 +15,9 @@ "cta-description": "Your current plan does not include access to this cohort, please upgrade to access the content.", "cta-cohort-not-found": "This cohort does not exist or you don't have access to join.", "cta-button": "Review plan", - "join-next-cohort":"Join next cohort", - "start-course":"Start this course", - "join-more":"Join more than 100 people taking this course right now", + "join-next-cohort": "Join next cohort", + "start-course": "Start this course", + "join-more": "Join more than 100 people taking this course right now", "preview-description": "You are reviewing this cohort dashboard on \"preview mode\", in order to start learning and interact with the materials please join the cohort." }, "already-have-this-cohort": "You are already a member of this cohort", @@ -105,7 +105,14 @@ "get-unlimited-mentorship": "and get unlimited mentorship", "you-have": "You have", "available-sessions": "available sessions", - "tooltip": "Mentorships reload every week" + "tooltip": "Mentorships reload every week", + "mentor-sessions-available": "mentor sessions available", + "action": "Schedule session" + }, + "schedule-steps": { + "select-mentorship": "First select a type of mentorship", + "select-mentor": "Now search for a mentor", + "schedule": "Schedule the session" }, "deliverProject": { "title": "Deliver assignment", @@ -195,4 +202,4 @@ "title": "Welcome to 4Geeks!", "description": "Watch this short video that explains how to get the most out of 4Geeks and enhance your learning experience" } -} +} \ No newline at end of file diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index d113d546e..6ec4e466a 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -16,9 +16,9 @@ "cta-description": "Tu plan actual no incluye acceso a esta cohorte, por favor actualiza para acceder al contenido.", "cta-cohort-not-found": "Esta cohorte no existe o no tienes acceso para unirte.", "cta-button": "Revisar plan", - "join-next-cohort":"Únete a la cohorte", - "start-course":"Empezar este curso", - "join-more":"Únete a más de 100 personas que toman este curso ahora mismo", + "join-next-cohort": "Únete a la cohorte", + "start-course": "Empezar este curso", + "join-more": "Únete a más de 100 personas que toman este curso ahora mismo", "preview-description": "Estás revisando esta cohorte en \"modo de vista previa\". Para comenzar a aprender e interactuar con el material, únete a la cohorte." }, "modules": { @@ -105,7 +105,14 @@ "get-unlimited-mentorship": "y obtén tutoría ilimitada", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana" + "tooltip": "Las tutorías se recargan cada semana", + "mentor-sessions-available": "sesiones de mentoria disponibles", + "action": "Reservar mentoria" + }, + "schedule-steps": { + "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentor": "Ahora busca un mentor", + "schedule": "Agenda la sesión" }, "deliverProject": { "title": "Entregar tarea", @@ -195,4 +202,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} +} \ No newline at end of file diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx new file mode 100644 index 000000000..32b2578dd --- /dev/null +++ b/src/pages/mentorship/schedule.jsx @@ -0,0 +1,706 @@ +import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; + +import { useRouter } from 'next/router'; +import useTranslation from 'next-translate/useTranslation'; +import { getStorageItem } from '../../utils'; +import { reportDatalayer } from '../../utils/requests'; +import useStyle from '../../common/hooks/useStyle'; +import useAuth from '../../common/hooks/useAuth'; +import Icon from '../../common/components/Icon'; +import Link from '../../common/components/NextChakraLink'; +import Heading from '../../common/components/Heading'; +import Text from '../../common/components/Text'; +import Image from '../../common/components/Image'; +import bc from '../../common/services/breathecode'; +import modifyEnv from '../../../modifyEnv'; + +function MentorshipSchedule() { + let isTabletOrPhone = false; + if (typeof window !== 'undefined') { + isTabletOrPhone = window.innerWidth < 780; + } + const { t } = useTranslation('dashboard'); + const router = useRouter(); + const { service, mentor } = router.query; + const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); + const accessToken = getStorageItem('accessToken'); + const toast = useToast(); + const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); + const { borderColor } = useStyle(); + + const { isLoading, user, isAuthenticated } = useAuth(); + const [mentorshipServices, setMentorshipServices] = useState({ + isLoading: true, + data: [], + }); + const [searchProps, setSearchProps] = useState({ + serviceSearch: '', + mentorSearch: '', + }); + const [admissionsData, setAdmissionsData] = useState(); + const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); + const [consumableOfService, setConsumableOfService] = useState({}); + const [consumables, setConsumables] = useState([]); + const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); + const [mentorsByService, setMentorsByService] = useState([]); + const [mentoryProps, setMentoryProps] = useState({}); + + const calculateExistenceOfConsumable = () => { + if (consumableOfService.available_as_saas === false) return true; + if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; + return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; + }; + const availableForConsume = calculateExistenceOfConsumable(); + + const getAllSyllabus = () => { + const syllabus = []; + const allCohorts = admissionsData?.cohorts || []; + + allCohorts.forEach(({ cohort }) => { + if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); + }); + return syllabus; + }; + + const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); + + const getServices = async (userRoles) => { + if (userRoles?.length > 0) { + const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + .then((resp) => { + const data = resp?.data; + if (data !== undefined && data.length > 0) { + return data.map((serv) => ({ + ...serv, + academy: { + id: role?.academy.id, + available_as_saas: role?.academy?.available_as_saas, + }, + })); + } + return []; + })); + const mentorshipResults = await Promise.all(mentorshipPromises); + const recopilatedServices = mentorshipResults.flat(); + + setMentorshipServices({ + isLoading: false, + data: recopilatedServices, + }); + } + }; + + const getAdmissionsData = async () => { + try { + const response = await bc.admissions().me(); + const admissionsFromDB = response.data; + setAdmissionsData(admissionsFromDB); + getServices(admissionsData.roles); + } catch (error) { + console.error('Error fetching admissions data:', error); + } + }; + + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + + const academies = {}; + + mentorshipServices.data.forEach((serv) => { + const { academy, ...restOfService } = serv; + if (!academies[academy.id]) { + academies[academy.id] = { services: [] }; + } + academies[academy.id].services.push(restOfService); + }); + + const academyData = Object.entries(academies).map(([academy, values]) => ({ + id: Number(academy), + services: values.services, + })); + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentors = academyData.map((academy) => bc.mentorship({ + services: academy.services.map((s) => s.slug).join(','), + syllabus: allSyllabus?.join(','), + status: 'ACTIVE', + academy: academy.id, + }).getMentor() + .then((res) => { + const allMentors = res?.data; + return allMentors; + })); + const mentorsList = (await Promise.all(mentors)).flat(); + return mentorsList; + } + + return []; + }; + + const getMentorsAndConsumables = async () => { + const mentors = await getAllMentorsAvailable(); + const reqConsumables = await bc.payment().service().consumable() + .then((res) => res?.data?.mentorship_service_sets.map((mentorshipServiceSet) => bc.mentorship() + .getServiceSet(mentorshipServiceSet?.id) + .then((rs) => ({ + ...rs?.data, + ...mentorshipServiceSet, + })))); + + const allConsumables = await Promise.all(reqConsumables); + + setConsumables(allConsumables); + setAllMentorsAvailable(mentors); + }; + + const manageMentorsData = (serv) => { + const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); + + const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + + // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); + if (relatedConsumables) { + reportDatalayer({ + dataLayer: { + event: 'select_mentorship_service', + path: router.pathname, + consumables_amount: relatedConsumables.balance?.unit, + mentorship_service: serv?.slug, + }, + }); + } + setConsumableOfService({ + ...relatedConsumables, + balance: { + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + }, + available_as_saas: serv?.academy?.available_as_saas, + }); + setMentoryProps({ ...mentoryProps, serv }); + }; + + const handleService = async (serv) => { + try { + if (allMentorsAvailable.length > 0) { + const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); + setMentorsByService(mentorsByServices); + manageMentorsData(serv, mentorsByServices); + if (openSearchService) setOpenSearchService(false); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } else { + manageMentorsData(serv); + if (openSearchService) setOpenSearchService(false); + } + } catch (e) { + toast({ + position: 'top', + title: 'Error', + description: t('alert-message:error-finding-mentors'), + status: 'error', + duration: 7000, + isClosable: true, + }); + } + }; + + const handleServicesByMentorsAvailability = () => { + const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + + if (service) { + const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); + handleService(mentorshipFound[0]); + if (mentorshipFound.length > 0) { + setMentoryProps((prev) => ({ + ...prev, + service: mentorshipFound[0], + })); + } + } + + if (mentor) { + const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); + const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + if (servicesOfMentor.length > 0) { + setOpenSearchMentor(false); + setMentoryProps((prev) => ({ + ...prev, + mentor: isolateMentor[0], + })); + } + } + + return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; + }; + + const handleMentorSelection = (ment) => { + if (mentoryProps.service.slug) { + setMentoryProps({ ...mentoryProps, ment }); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } + if (openSearchMentor) setOpenSearchMentor(false); + }; + + useEffect(() => { + if (mentorshipServices.data && allMentorsAvailable) { + const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); + setServicesByMentorsAvailable(servicesWithMentorsAvailable); + } + }, [mentorshipServices.data, allMentorsAvailable]); + + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + useEffect(() => { + if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { + getMentorsAndConsumables(); + } + }, [mentorshipServices]); + + const handleOpenCloseService = () => { + if (!openSearchService) { + if (!openSearchMentor) setOpenSearchMentor(true); + setMentoryProps({}); + setOpenSearchService(true); + } + }; + + const handleOpenCloseMentor = () => { + setMentoryProps((prevState) => { + const { mentor: mentorToRemove, ...remainingProps } = prevState; + return remainingProps; + }); + setOpenSearchMentor(true); + }; + + const reportBookMentor = () => { + reportDatalayer({ + dataLayer: { + event: 'book_mentorship_session', + path: router.pathname, + consumables_amount: consumableOfService.balance.unit, + mentorship_service: mentoryProps?.service?.slug, + mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, + mentor_id: mentoryProps.mentor.slug, + mentor_booking_url: mentoryProps.mentor.booking_url, + }, + }); + }; + + const handleTitleStep = () => { + if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); + if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + return t('schedule-steps.schedule'); + }; + + // console.log(isTabletOrPhone) + // console.log("available for consume", availableForConsume) + // console.log(servicesByMentorAvailable); + // console.log("Soy admissions Data", admissionsData) + // console.log("All sylabus", allSyllabus) + // console.log("mentorship services", mentorshipServices.data) + // console.log("consumibles", consumables) + // console.log("mentores disponibles", allMentorsAvailable) + // console.log("consumable of service", consumableOfService) + // console.log("consumable of service balance", consumableOfService?.balance?.unit) + console.log('mentoryProps', mentoryProps); + // console.log("available for consume", availableForConsume) + // console.log("access token", accessToken) + // console.log("opensearch service",openSearchService) + // console.log("opensearch mentor",openSearchMentor) + + return !isLoading && user && !mentorshipServices.isLoading && ( + + + {`← ${t('back-to-dash')}`} + + + + {t('supportSideBar.schedule-button')} + + + {t('mentorship.you-have')} + {' '} + {servicesByMentorAvailable?.length} + {' '} + {t('mentorship.mentor-sessions-available')} + + + {handleTitleStep()} + + + {openSearchService + && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.select-type')} + /> + )} + + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading + ? ( + <> + {mentoryProps.service + ? ( + + + {mentoryProps?.service && !openSearchService + && ( + + )} + {mentoryProps.service?.name} + + {mentoryProps?.service && !openSearchService + && ( + + )} + + ) + : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)} + > + + {mentoryProps.serv ? mentoryProps.serv.name : serv.name} + + + ))} + + ) + : ( + + No service + + )} + + + {mentoryProps?.service && ( + <> + {openSearchMentor && !mentoryProps.mentor + && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.search-mentor')} + /> + + )} + + {mentorsByService.length > 0 + ? ( + <> + {(mentoryProps.service && mentoryProps.mentor) + ? ( + + + + {`${mentoryProps.mentor?.user?.first_name} + + + + + {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + + + + + + + + ) + : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( + + {i !== 0 && ( + + )} + handleMentorSelection(ment)} + > + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + + + + + + ))} + + ) + : ( + + {t('supportSideBar.no-mentors')} + + )} + + + )} + {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume + && ( + <> + {mentoryProps.mentor.booking_url + ? ( + reportBookMentor()} + href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} + target="_blank" + rel="noopener noreferrer" + background="#0196d1" + display="flex" + marginTop="12px" + padding="10px" + borderRadius="5px" + alignItems="center" + > + + {t('mentorship.action')} + + + + ) + : ( + + {t('supportSideBar.no-mentor-link')} + + )} + + )} + {mentoryProps.service && mentoryProps.mentor && !availableForConsume + && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + + + + ); +} + +export default MentorshipSchedule; From 10cdedf9d83c316ecc7fa56989d04d9fe26f0b67 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Fri, 13 Sep 2024 17:44:14 -0400 Subject: [PATCH 22/67] :recycle: refactor of the jsx --- src/pages/mentorship/schedule.jsx | 561 +++++++++--------------------- 1 file changed, 167 insertions(+), 394 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 32b2578dd..d0cb3d1c5 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useEffect, useState, useMemo } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; import { useRouter } from 'next/router'; @@ -55,21 +55,9 @@ function MentorshipSchedule() { }; const availableForConsume = calculateExistenceOfConsumable(); - const getAllSyllabus = () => { - const syllabus = []; - const allCohorts = admissionsData?.cohorts || []; - - allCohorts.forEach(({ cohort }) => { - if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); - }); - return syllabus; - }; - - const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); - const getServices = async (userRoles) => { if (userRoles?.length > 0) { - const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + const mentorshipPromises = await userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() .then((resp) => { const data = resp?.data; if (data !== undefined && data.length > 0) { @@ -98,42 +86,48 @@ function MentorshipSchedule() { const response = await bc.admissions().me(); const admissionsFromDB = response.data; setAdmissionsData(admissionsFromDB); - getServices(admissionsData.roles); + getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const getAllMentorsAvailable = async () => { - const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + const allSyllabus = useMemo(() => { + const allCohorts = admissionsData?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissionsData]); - const academies = {}; + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); - mentorshipServices.data.forEach((serv) => { - const { academy, ...restOfService } = serv; - if (!academies[academy.id]) { - academies[academy.id] = { services: [] }; + const academies = mentorshipServices.data.reduce((acc, { academy, ...restOfService }) => { + if (!acc[academy.id]) { + acc[academy.id] = { services: [] }; } - academies[academy.id].services.push(restOfService); - }); + acc[academy.id].services.push(restOfService); + return acc; + }, {}); - const academyData = Object.entries(academies).map(([academy, values]) => ({ - id: Number(academy), - services: values.services, + const academyData = Object.entries(academies).map(([id, { services }]) => ({ + id: Number(id), + services, })); - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { - const mentors = academyData.map((academy) => bc.mentorship({ + const getMentorsForAcademy = async (academy) => { + const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, - }).getMentor() - .then((res) => { - const allMentors = res?.data; - return allMentors; - })); - const mentorsList = (await Promise.all(mentors)).flat(); + }).getMentor(); + + return res?.data || []; + }; + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentorsPromises = academyData.map(getMentorsForAcademy); + const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; } @@ -153,6 +147,7 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); + console.log(allConsumables); setAllMentorsAvailable(mentors); }; @@ -160,10 +155,9 @@ function MentorshipSchedule() { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); if (relatedConsumables) { reportDatalayer({ dataLayer: { @@ -181,9 +175,12 @@ function MentorshipSchedule() { }, available_as_saas: serv?.academy?.available_as_saas, }); - setMentoryProps({ ...mentoryProps, serv }); + + setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; + console.log(consumableOfService); + const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -220,7 +217,7 @@ function MentorshipSchedule() { if (mentorshipFound.length > 0) { setMentoryProps((prev) => ({ ...prev, - service: mentorshipFound[0], + serviceSelected: mentorshipFound[0], })); } } @@ -232,7 +229,7 @@ function MentorshipSchedule() { setOpenSearchMentor(false); setMentoryProps((prev) => ({ ...prev, - mentor: isolateMentor[0], + mentorSelected: isolateMentor[0], })); } } @@ -241,8 +238,8 @@ function MentorshipSchedule() { }; const handleMentorSelection = (ment) => { - if (mentoryProps.service.slug) { - setMentoryProps({ ...mentoryProps, ment }); + if (mentoryProps.serviceSelected.slug) { + setMentoryProps({ ...mentoryProps, mentorSelected: ment }); setSearchProps({ serviceSearch: '', mentorSearch: '', @@ -286,7 +283,7 @@ function MentorshipSchedule() { const handleOpenCloseMentor = () => { setMentoryProps((prevState) => { - const { mentor: mentorToRemove, ...remainingProps } = prevState; + const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; return remainingProps; }); setOpenSearchMentor(true); @@ -298,17 +295,17 @@ function MentorshipSchedule() { event: 'book_mentorship_session', path: router.pathname, consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.service?.slug, - mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, - mentor_id: mentoryProps.mentor.slug, - mentor_booking_url: mentoryProps.mentor.booking_url, + mentorship_service: mentoryProps?.serviceSelected?.slug, + mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, + mentor_id: mentoryProps.mentorSelected.slug, + mentor_booking_url: mentoryProps.mentorSelected.booking_url, }, }); }; const handleTitleStep = () => { - if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); - if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; @@ -322,382 +319,158 @@ function MentorshipSchedule() { // console.log("mentores disponibles", allMentorsAvailable) // console.log("consumable of service", consumableOfService) // console.log("consumable of service balance", consumableOfService?.balance?.unit) - console.log('mentoryProps', mentoryProps); + // console.log('mentoryProps', mentoryProps); // console.log("available for consume", availableForConsume) // console.log("access token", accessToken) // console.log("opensearch service",openSearchService) // console.log("opensearch mentor",openSearchMentor) return !isLoading && user && !mentorshipServices.isLoading && ( - - + + {`← ${t('back-to-dash')}`} - + {t('supportSideBar.schedule-button')} - - {t('mentorship.you-have')} - {' '} - {servicesByMentorAvailable?.length} - {' '} - {t('mentorship.mentor-sessions-available')} + + {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - + {handleTitleStep()} - - {openSearchService - && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.select-type')} - /> - )} - - - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading - ? ( - <> - {mentoryProps.service - ? ( - - - {mentoryProps?.service && !openSearchService - && ( - - )} - {mentoryProps.service?.name} - - {mentoryProps?.service && !openSearchService - && ( - - )} - - ) - : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)} - > - - {mentoryProps.serv ? mentoryProps.serv.name : serv.name} - + {openSearchService && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> + )} + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( +
+ {mentoryProps.serviceSelected ? ( + + + {mentoryProps?.serviceSelected && !openSearchService && ( + + )} + {mentoryProps.serviceSelected?.name} + + {mentoryProps?.serviceSelected && !openSearchService + && ( + + )} + + ) : ( + servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)}> + + {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - ))} - - ) - : ( - - No service - - )} + + )) + )} +
+ ) : ( + + No service + + )}
- {mentoryProps?.service && ( - <> - {openSearchMentor && !mentoryProps.mentor - && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.search-mentor')} - /> - - )} - - {mentorsByService.length > 0 - ? ( - <> - {(mentoryProps.service && mentoryProps.mentor) - ? ( - + + {mentoryProps?.serviceSelected && ( + + {openSearchMentor && !mentoryProps.mentorSelected && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> + + )} + + {mentorsByService.length > 0 ? ( + + {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( + - - {`${mentoryProps.mentor?.user?.first_name} + + {`${mentoryProps.mentorSelected?.user?.first_name} - - - - {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - ) - : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( - - {i !== 0 && ( - - )} - handleMentorSelection(ment)} - > - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + ) : ( + mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( +
+ {i !== 0 && ( + + )} + handleMentorSelection(ment)}> + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + - - - ))} - - ) - : ( - - {t('supportSideBar.no-mentors')} +
+ )) + )} +
+ ) : ( + + {t('supportSideBar.no-mentors')} + + )} +
+
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( + + {mentoryProps.mentorSelected.booking_url ? ( + reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> + + {t('mentorship.action')} + + + + ) : ( + + {t('supportSideBar.no-mentor-link')} )} - - - )} - {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume - && ( - <> - {mentoryProps.mentor.booking_url - ? ( - reportBookMentor()} - href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} - target="_blank" - rel="noopener noreferrer" - background="#0196d1" - display="flex" - marginTop="12px" - padding="10px" - borderRadius="5px" - alignItems="center" - > - - {t('mentorship.action')} - - - - ) - : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - {mentoryProps.service && mentoryProps.mentor && !availableForConsume - && ( - - {t('supportSideBar.no-mentoring-available')} - - )} - +
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + +
); From 8d88f5bbdebdb41b1711e0c5d5688eac26252d2e Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 14:49:50 -0400 Subject: [PATCH 23/67] :sparkles: page to schedule mentorship --- public/locales/en/dashboard.json | 4 +- public/locales/es/dashboard.json | 4 +- src/pages/mentorship/schedule.jsx | 191 ++++++++++++++++++++---------- 3 files changed, 134 insertions(+), 65 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 49691c178..071ce11ca 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -107,7 +107,9 @@ "available-sessions": "available sessions", "tooltip": "Mentorships reload every week", "mentor-sessions-available": "mentor sessions available", - "action": "Schedule session" + "action": "Schedule session", + "no-available": "No services available", + "no-available-for-teacher": "No serives available for" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 6ec4e466a..5c902cfb3 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -107,7 +107,9 @@ "available-sessions": "sesiones disponibles", "tooltip": "Las tutorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", - "action": "Reservar mentoria" + "action": "Reservar mentoria", + "no-available": "No hay servicios disponibles", + "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { "select-mentorship": "Primero selecciona un tipo de tutoria", diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index d0cb3d1c5..6f442a1ba 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,10 +1,13 @@ -import { useEffect, useState, useMemo } from 'react'; +import { useEffect, useState } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; - import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; +import PropTypes from 'prop-types'; import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; +import { validatePlanExistence } from '../../common/handlers/subscriptions'; +import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; +import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; @@ -15,30 +18,44 @@ import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; import modifyEnv from '../../../modifyEnv'; +function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { + return ( + + {t('supportSideBar.no-mentoring-available')} + + + ); +} + function MentorshipSchedule() { let isTabletOrPhone = false; if (typeof window !== 'undefined') { isTabletOrPhone = window.innerWidth < 780; } - const { t } = useTranslation('dashboard'); const router = useRouter(); + const { t } = useTranslation('dashboard'); + const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; + const { borderColor } = useStyle(); + const { isLoading, user, isAuthenticated } = useAuth(); const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); const accessToken = getStorageItem('accessToken'); const toast = useToast(); const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); - const { borderColor } = useStyle(); - - const { isLoading, user, isAuthenticated } = useAuth(); - const [mentorshipServices, setMentorshipServices] = useState({ - isLoading: true, - data: [], - }); - const [searchProps, setSearchProps] = useState({ - serviceSearch: '', - mentorSearch: '', - }); - const [admissionsData, setAdmissionsData] = useState(); + const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); + const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); const [openSearchService, setOpenSearchService] = useState(true); const [openSearchMentor, setOpenSearchMentor] = useState(true); @@ -47,6 +64,10 @@ function MentorshipSchedule() { const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); + const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); + const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); + const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -85,19 +106,12 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; - setAdmissionsData(admissionsFromDB); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const allSyllabus = useMemo(() => { - const allCohorts = admissionsData?.cohorts || []; - const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; - return syllabus; - }, [admissionsData]); - const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -117,7 +131,6 @@ function MentorshipSchedule() { const getMentorsForAcademy = async (academy) => { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), - syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, }).getMentor(); @@ -125,7 +138,7 @@ function MentorshipSchedule() { return res?.data || []; }; - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + if (servicesSlugs.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -147,31 +160,30 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); - console.log(allConsumables); setAllMentorsAvailable(mentors); }; const manageMentorsData = (serv) => { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - if (relatedConsumables) { + if (relatedConsumable) { reportDatalayer({ dataLayer: { event: 'select_mentorship_service', path: router.pathname, - consumables_amount: relatedConsumables.balance?.unit, + consumables_amount: relatedConsumable.balance?.unit, mentorship_service: serv?.slug, }, }); } setConsumableOfService({ - ...relatedConsumables, + ...relatedConsumable, balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, }, available_as_saas: serv?.academy?.available_as_saas, }); @@ -179,8 +191,6 @@ function MentorshipSchedule() { setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; - console.log(consumableOfService); - const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -232,6 +242,7 @@ function MentorshipSchedule() { mentorSelected: isolateMentor[0], })); } + return servicesOfMentor; } return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; @@ -304,31 +315,69 @@ function MentorshipSchedule() { }; const handleTitleStep = () => { - if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; - // console.log(isTabletOrPhone) - // console.log("available for consume", availableForConsume) - // console.log(servicesByMentorAvailable); - // console.log("Soy admissions Data", admissionsData) - // console.log("All sylabus", allSyllabus) - // console.log("mentorship services", mentorshipServices.data) - // console.log("consumibles", consumables) - // console.log("mentores disponibles", allMentorsAvailable) - // console.log("consumable of service", consumableOfService) - // console.log("consumable of service balance", consumableOfService?.balance?.unit) - // console.log('mentoryProps', mentoryProps); - // console.log("available for consume", availableForConsume) - // console.log("access token", accessToken) - // console.log("opensearch service",openSearchService) - // console.log("opensearch mentor",openSearchMentor) + const handleDescription = () => { + if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; + return t('mentorship.no-available'); + }; + + useEffect(() => { + if (mentoryProps.serviceSelected) { + fetchSubscriptions() + .then((data) => { + setSubscriptionData(data); + reportDatalayer({ + dataLayer: { + event: 'subscriptions_load', + method: 'native', + plan_financings: data?.plan_financings?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + subscriptions: data?.subscriptions?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + }, + }); + }); + } + }, [mentoryProps.serviceSelected]); + + const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { + const paidAtDate = new Date(invoice.paid_at); + return paidAtDate > latest ? paidAtDate : latest; + }, new Date(0)); + + const sortByMostRecentInvoice = (a, b) => { + const latestA = getMostRecentPaidAt(a.invoices); + const latestB = getMostRecentPaidAt(b.invoices); + return latestB - latestA; + }; + + const handleGetMoreMentorships = async () => { + setIsFetchingDataForModal(true); + const academyService = mentoryProps?.serviceSelected; + const allSubscriptions = subscriptionData.subscriptions; + const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); + const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; + + validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { + setDataToGetAccessModal({ + ...data, + event: '', + academyService, + }); + setIsModalToGetAccessOpen(true); + }) + .finally(() => setIsFetchingDataForModal(false)); + }; return !isLoading && user && !mentorshipServices.isLoading && ( - + useColorModeValue('#f9f9f9', '#171f2a')}> - {`← ${t('back-to-dash')}`} + + + {`${t('back-to-dash')}`} + @@ -347,16 +396,16 @@ function MentorshipSchedule() { {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? (
{mentoryProps.serviceSelected ? ( - + {mentoryProps?.serviceSelected && !openSearchService && ( - + )} {mentoryProps.serviceSelected?.name} {mentoryProps?.serviceSelected && !openSearchService && ( - )} @@ -373,7 +422,7 @@ function MentorshipSchedule() {
) : ( - No service + {handleDescription()} )} @@ -390,17 +439,17 @@ function MentorshipSchedule() { {mentorsByService.length > 0 ? ( {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - + - + {`${mentoryProps.mentorSelected?.user?.first_name} - - + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} @@ -426,7 +475,7 @@ function MentorshipSchedule() { {`${ment.user.first_name} ${ment.user.last_name}`} - + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} @@ -465,15 +514,31 @@ function MentorshipSchedule() { {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - {t('supportSideBar.no-mentoring-available')} - + )} + { + setIsModalToGetAccessOpen(false); + }} + />
); } +NoConsumablesCard.defaultProps = { + isLoading: false, // Valor por defecto cuando no se pasa esta prop +}; + +NoConsumablesCard.propTypes = { + t: PropTypes.func.isRequired, + handleGetMoreMentorships: PropTypes.func.isRequired, + isLoading: PropTypes.bool, +}; + export default MentorshipSchedule; From be69a9c464143530f90a53b78585aa6d5545528e Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 15:58:10 -0400 Subject: [PATCH 24/67] small change --- src/pages/mentorship/schedule.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 6f442a1ba..75b0ea9ad 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -57,17 +57,17 @@ function MentorshipSchedule() { const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); const [consumableOfService, setConsumableOfService] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [subscriptionData, setSubscriptionData] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -393,7 +393,7 @@ function MentorshipSchedule() { setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> )} - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( + {servicesByMentorAvailable?.length > 0 ? (
{mentoryProps.serviceSelected ? ( From 326f48f924d45fad85997ce2106dd540584c99f7 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:42:31 -0400 Subject: [PATCH 25/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 460 +++++------------------------- 1 file changed, 70 insertions(+), 390 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 75b0ea9ad..89308803a 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,43 +1,14 @@ -import { useEffect, useState } from 'react'; -import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; +import { useEffect, useState, useMemo } from 'react'; +import { useColorModeValue, Container, Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; -import PropTypes from 'prop-types'; -import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; -import { validatePlanExistence } from '../../common/handlers/subscriptions'; -import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; -import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; import Link from '../../common/components/NextChakraLink'; -import Heading from '../../common/components/Heading'; -import Text from '../../common/components/Text'; -import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; -import modifyEnv from '../../../modifyEnv'; - -function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { - return ( - - {t('supportSideBar.no-mentoring-available')} - - - ); -} +import MentoringConsumables from '../../common/components/SupportSidebar/MentoringConsumables'; function MentorshipSchedule() { let isTabletOrPhone = false; @@ -48,33 +19,15 @@ function MentorshipSchedule() { const { t } = useTranslation('dashboard'); const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; - const { borderColor } = useStyle(); const { isLoading, user, isAuthenticated } = useAuth(); - const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); - const accessToken = getStorageItem('accessToken'); - const toast = useToast(); - const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); - const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [consumableOfService, setConsumableOfService] = useState({}); + const [mentoryProps, setMentoryProps] = useState({}); + const [admissions, setAdmissions] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); - const [mentoryProps, setMentoryProps] = useState({}); - const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); const [subscriptionData, setSubscriptionData] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); - const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); - const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - - const calculateExistenceOfConsumable = () => { - if (consumableOfService.available_as_saas === false) return true; - if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; - return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; - }; - const availableForConsume = calculateExistenceOfConsumable(); const getServices = async (userRoles) => { if (userRoles?.length > 0) { @@ -106,12 +59,31 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; + setAdmissions(response.data); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + const allSyllabus = useMemo(() => { + const allCohorts = admissions?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissions]); + const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -132,13 +104,14 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', + syllabus: allSyllabus?.join(','), academy: academy.id, }).getMentor(); return res?.data || []; }; - if (servicesSlugs.length > 0) { + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -163,167 +136,37 @@ function MentorshipSchedule() { setAllMentorsAvailable(mentors); }; - const manageMentorsData = (serv) => { - const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - - const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - - if (relatedConsumable) { - reportDatalayer({ - dataLayer: { - event: 'select_mentorship_service', - path: router.pathname, - consumables_amount: relatedConsumable.balance?.unit, - mentorship_service: serv?.slug, - }, - }); - } - setConsumableOfService({ - ...relatedConsumable, - balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, - }, - available_as_saas: serv?.academy?.available_as_saas, - }); - - setMentoryProps({ ...mentoryProps, serviceSelected: serv }); - }; - - const handleService = async (serv) => { - try { - if (allMentorsAvailable.length > 0) { - const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); - setMentorsByService(mentorsByServices); - manageMentorsData(serv, mentorsByServices); - if (openSearchService) setOpenSearchService(false); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } else { - manageMentorsData(serv); - if (openSearchService) setOpenSearchService(false); - } - } catch (e) { - toast({ - position: 'top', - title: 'Error', - description: t('alert-message:error-finding-mentors'), - status: 'error', - duration: 7000, - isClosable: true, - }); - } - }; - - const handleServicesByMentorsAvailability = () => { - const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - - if (service) { - const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); - handleService(mentorshipFound[0]); - if (mentorshipFound.length > 0) { - setMentoryProps((prev) => ({ - ...prev, - serviceSelected: mentorshipFound[0], - })); - } - } - - if (mentor) { - const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); - const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - if (servicesOfMentor.length > 0) { - setOpenSearchMentor(false); - setMentoryProps((prev) => ({ - ...prev, - mentorSelected: isolateMentor[0], - })); - } - return servicesOfMentor; - } - - return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; - }; - - const handleMentorSelection = (ment) => { - if (mentoryProps.serviceSelected.slug) { - setMentoryProps({ ...mentoryProps, mentorSelected: ment }); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } - if (openSearchMentor) setOpenSearchMentor(false); - }; - - useEffect(() => { - if (mentorshipServices.data && allMentorsAvailable) { - const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); - setServicesByMentorsAvailable(servicesWithMentorsAvailable); - } - }, [mentorshipServices.data, allMentorsAvailable]); - - useEffect(() => { - const checkAuthAndFetchData = async () => { - if (!isLoading && !isAuthenticated) { - router.push('/login'); - } else if (isAuthenticated) { - await getAdmissionsData(); - } - }; - - checkAuthAndFetchData(); - }, [isLoading, isAuthenticated]); - useEffect(() => { if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { getMentorsAndConsumables(); } }, [mentorshipServices]); - const handleOpenCloseService = () => { - if (!openSearchService) { - if (!openSearchMentor) setOpenSearchMentor(true); - setMentoryProps({}); - setOpenSearchService(true); - } - }; - - const handleOpenCloseMentor = () => { - setMentoryProps((prevState) => { - const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; - return remainingProps; - }); - setOpenSearchMentor(true); - }; - - const reportBookMentor = () => { - reportDatalayer({ - dataLayer: { - event: 'book_mentorship_session', - path: router.pathname, - consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.serviceSelected?.slug, - mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, - mentor_id: mentoryProps.mentorSelected.slug, - mentor_booking_url: mentoryProps.mentorSelected.booking_url, - }, - }); - }; + const mentorsFiltered = mentorsByService.filter( + (ment) => { + const fullName = `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase(); + return ( + fullName.includes(searchProps.mentorSearch) + && ment.services.some((sv) => sv.status === 'ACTIVE' && sv.slug === mentoryProps?.service?.slug) + ); + }, + ); - const handleTitleStep = () => { - if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); - if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); - return t('schedule-steps.schedule'); - }; + const filterServices = () => { + if (subscriptionData?.selected_mentorship_service_set?.mentorship_services?.length > 0) { + return subscriptionData?.selected_mentorship_service_set?.mentorship_services?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } + if (mentorshipServices.data.length > 0) { + return mentorshipServices.data?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } - const handleDescription = () => { - if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; - return t('mentorship.no-available'); + return []; }; + const suscriptionServicesFiltered = filterServices(); useEffect(() => { if (mentoryProps.serviceSelected) { @@ -342,203 +185,40 @@ function MentorshipSchedule() { } }, [mentoryProps.serviceSelected]); - const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { - const paidAtDate = new Date(invoice.paid_at); - return paidAtDate > latest ? paidAtDate : latest; - }, new Date(0)); - - const sortByMostRecentInvoice = (a, b) => { - const latestA = getMostRecentPaidAt(a.invoices); - const latestB = getMostRecentPaidAt(b.invoices); - return latestB - latestA; - }; - - const handleGetMoreMentorships = async () => { - setIsFetchingDataForModal(true); - const academyService = mentoryProps?.serviceSelected; - const allSubscriptions = subscriptionData.subscriptions; - const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); - const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; - - validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { - setDataToGetAccessModal({ - ...data, - event: '', - academyService, - }); - setIsModalToGetAccessOpen(true); - }) - .finally(() => setIsFetchingDataForModal(false)); - }; - return !isLoading && user && !mentorshipServices.isLoading && ( - useColorModeValue('#f9f9f9', '#171f2a')}> + useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> {`${t('back-to-dash')}`} - - - {t('supportSideBar.schedule-button')} - - - {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - - - {handleTitleStep()} - - {openSearchService && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> - )} - - {servicesByMentorAvailable?.length > 0 ? ( -
- {mentoryProps.serviceSelected ? ( - - - {mentoryProps?.serviceSelected && !openSearchService && ( - - )} - {mentoryProps.serviceSelected?.name} - - {mentoryProps?.serviceSelected && !openSearchService - && ( - - )} - - ) : ( - servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)}> - - {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - - - )) - )} -
- ) : ( - - {handleDescription()} - - )} -
-
- - {mentoryProps?.serviceSelected && ( - - {openSearchMentor && !mentoryProps.mentorSelected && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> - - )} - - {mentorsByService.length > 0 ? ( - - {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - - - - {`${mentoryProps.mentorSelected?.user?.first_name} - - - - - {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - - - {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - - - - - - ) : ( - mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( -
- {i !== 0 && ( - - )} - handleMentorSelection(ment)}> - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} - - - - -
- )) - )} -
- ) : ( - - {t('supportSideBar.no-mentors')} - - )} -
-
- )} -
- - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( - - {mentoryProps.mentorSelected.booking_url ? ( - reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> - - {t('mentorship.action')} - - - - ) : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - - - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - )} - - { - setIsModalToGetAccessOpen(false); + + + - +
); } -NoConsumablesCard.defaultProps = { - isLoading: false, // Valor por defecto cuando no se pasa esta prop -}; - -NoConsumablesCard.propTypes = { - t: PropTypes.func.isRequired, - handleGetMoreMentorships: PropTypes.func.isRequired, - isLoading: PropTypes.bool, -}; - export default MentorshipSchedule; From 7f50a3ec7712c8ca9918e94a5c29295be9358747 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:57:50 -0400 Subject: [PATCH 26/67] Update dashboard.json --- public/locales/es/dashboard.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 5c902cfb3..9556e3c10 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -73,17 +73,17 @@ "no-mentors": "No hay mentores disponibles", "no-mentor-link": "No hay enlace para crear sesión con este mentor", "search-mentor": "Buscar mentor por nombre", - "start-mentorship": "Para comenzar, seleccione un tipo de tutoría", + "start-mentorship": "Para comenzar, seleccione un tipo de mentoría", "mentoring-label": "Mentoría", - "mentoring": "Programe una sesión de tutoría 1-1.", - "select-type": "Seleccione un tipo de tutoría", - "mentoring-available": "Tutorías disponibles", - "no-mentoring-available": "No tienes tutorías disponibles.", + "mentoring": "Programe una sesión de mentoría 1-1.", + "select-type": "Seleccione un tipo de mentoría", + "mentoring-available": "Mentorías disponibles", + "no-mentoring-available": "No tienes mentorías disponibles.", "create-session-text": "Agendar ahora", "learn-more": "Aprender mas.", "learn-more-link": "https://4geeks.com/es/docs/knowledge-base-4geeks/sesiones-de-tutoria", "get-more-mentorships": "Consigue más mentorías", - "schedule-button": "Programar una sesión de tutoría", + "schedule-button": "Programar una sesión de mentoría", "mentors-available": "+{{count}} Mentores disponibles", "actionButtons": [ { @@ -101,18 +101,18 @@ ] }, "mentorship": { - "no-mentorship": "Se ha quedado sin tutorías, pero no se preocupe, puede obtener más con unos pocos clics.", - "get-unlimited-mentorship": "y obtén tutoría ilimitada", + "no-mentorship": "Se ha quedado sin mentorías, pero no se preocupe, puede obtener más con unos pocos clics.", + "get-unlimited-mentorship": "y obtén mentorías ilimitadas", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana", + "tooltip": "Las mentorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", "action": "Reservar mentoria", "no-available": "No hay servicios disponibles", "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { - "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentorship": "Primero selecciona un tipo de mentoría", "select-mentor": "Ahora busca un mentor", "schedule": "Agenda la sesión" }, @@ -204,4 +204,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} \ No newline at end of file +} From f2c728475ed989cba204ab5c30fa47102e45517f Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:50:12 -0400 Subject: [PATCH 27/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 89308803a..9cd560e58 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -190,7 +190,7 @@ function MentorshipSchedule() { - {`${t('back-to-dash')}`} + {`${t('back-to-dashboard')}`} From e9bd61ce4ebec5de6a43ab61b62d4ae054514992 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Sun, 22 Sep 2024 23:17:15 -0400 Subject: [PATCH 28/67] small change --- src/pages/mentorship/schedule.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 9cd560e58..8ca8c19f4 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -104,7 +104,7 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', - syllabus: allSyllabus?.join(','), + syllabus: allSyllabus?.join(',') || undefined, academy: academy.id, }).getMentor(); From 7d41b216ef797f7296ca6ed8cb0d3d9643716a77 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Thu, 5 Sep 2024 17:07:34 -0400 Subject: [PATCH 29/67] :sparkles: new url for students to schedule mentorships --- i18n.js | 1 + public/locales/en/dashboard.json | 17 +- public/locales/es/dashboard.json | 17 +- src/pages/mentorship/schedule.jsx | 706 ++++++++++++++++++++++++++++++ 4 files changed, 731 insertions(+), 10 deletions(-) create mode 100644 src/pages/mentorship/schedule.jsx diff --git a/i18n.js b/i18n.js index c81e33453..f85d909ff 100644 --- a/i18n.js +++ b/i18n.js @@ -25,6 +25,7 @@ module.exports = { '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'], '/survey/[surveyId]': ['survey'], '/mentorship': ['mentorship'], + '/mentorship/schedule': ['mentorship', 'dashboard'], '/how-to': ['how-to'], '/pricing': ['pricing', 'signup'], '/how-to/[slug]': ['how-to'], diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 11643ac06..76d9acebe 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -15,9 +15,9 @@ "cta-description": "Your current plan does not include access to this cohort, please upgrade to access the content.", "cta-cohort-not-found": "This cohort does not exist or you don't have access to join.", "cta-button": "Review plan", - "join-next-cohort":"Join next cohort", - "start-course":"Start this course", - "join-more":"Join more than 100 people taking this course right now", + "join-next-cohort": "Join next cohort", + "start-course": "Start this course", + "join-more": "Join more than 100 people taking this course right now", "preview-description": "You are reviewing this cohort dashboard on \"preview mode\", in order to start learning and interact with the materials please join the cohort." }, "already-have-this-cohort": "You are already a member of this cohort", @@ -108,7 +108,14 @@ "get-unlimited-mentorship": "and get unlimited mentorship", "you-have": "You have", "available-sessions": "available sessions", - "tooltip": "Mentorships reload every week" + "tooltip": "Mentorships reload every week", + "mentor-sessions-available": "mentor sessions available", + "action": "Schedule session" + }, + "schedule-steps": { + "select-mentorship": "First select a type of mentorship", + "select-mentor": "Now search for a mentor", + "schedule": "Schedule the session" }, "deliverProject": { "title": "Deliver assignment", @@ -198,4 +205,4 @@ "title": "Welcome to 4Geeks!", "description": "Watch this short video that explains how to get the most out of 4Geeks and enhance your learning experience" } -} +} \ No newline at end of file diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index f5ccaafca..1a2ed84d9 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -16,9 +16,9 @@ "cta-description": "Tu plan actual no incluye acceso a esta cohorte, por favor actualiza para acceder al contenido.", "cta-cohort-not-found": "Esta cohorte no existe o no tienes acceso para unirte.", "cta-button": "Revisar plan", - "join-next-cohort":"Únete a la cohorte", - "start-course":"Empezar este curso", - "join-more":"Únete a más de 100 personas que toman este curso ahora mismo", + "join-next-cohort": "Únete a la cohorte", + "start-course": "Empezar este curso", + "join-more": "Únete a más de 100 personas que toman este curso ahora mismo", "preview-description": "Estás revisando esta cohorte en \"modo de vista previa\". Para comenzar a aprender e interactuar con el material, únete a la cohorte." }, "modules": { @@ -108,7 +108,14 @@ "get-unlimited-mentorship": "y obtén tutoría ilimitada", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana" + "tooltip": "Las tutorías se recargan cada semana", + "mentor-sessions-available": "sesiones de mentoria disponibles", + "action": "Reservar mentoria" + }, + "schedule-steps": { + "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentor": "Ahora busca un mentor", + "schedule": "Agenda la sesión" }, "deliverProject": { "title": "Entregar tarea", @@ -198,4 +205,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} +} \ No newline at end of file diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx new file mode 100644 index 000000000..32b2578dd --- /dev/null +++ b/src/pages/mentorship/schedule.jsx @@ -0,0 +1,706 @@ +import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; + +import { useRouter } from 'next/router'; +import useTranslation from 'next-translate/useTranslation'; +import { getStorageItem } from '../../utils'; +import { reportDatalayer } from '../../utils/requests'; +import useStyle from '../../common/hooks/useStyle'; +import useAuth from '../../common/hooks/useAuth'; +import Icon from '../../common/components/Icon'; +import Link from '../../common/components/NextChakraLink'; +import Heading from '../../common/components/Heading'; +import Text from '../../common/components/Text'; +import Image from '../../common/components/Image'; +import bc from '../../common/services/breathecode'; +import modifyEnv from '../../../modifyEnv'; + +function MentorshipSchedule() { + let isTabletOrPhone = false; + if (typeof window !== 'undefined') { + isTabletOrPhone = window.innerWidth < 780; + } + const { t } = useTranslation('dashboard'); + const router = useRouter(); + const { service, mentor } = router.query; + const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); + const accessToken = getStorageItem('accessToken'); + const toast = useToast(); + const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); + const { borderColor } = useStyle(); + + const { isLoading, user, isAuthenticated } = useAuth(); + const [mentorshipServices, setMentorshipServices] = useState({ + isLoading: true, + data: [], + }); + const [searchProps, setSearchProps] = useState({ + serviceSearch: '', + mentorSearch: '', + }); + const [admissionsData, setAdmissionsData] = useState(); + const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); + const [consumableOfService, setConsumableOfService] = useState({}); + const [consumables, setConsumables] = useState([]); + const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); + const [mentorsByService, setMentorsByService] = useState([]); + const [mentoryProps, setMentoryProps] = useState({}); + + const calculateExistenceOfConsumable = () => { + if (consumableOfService.available_as_saas === false) return true; + if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; + return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; + }; + const availableForConsume = calculateExistenceOfConsumable(); + + const getAllSyllabus = () => { + const syllabus = []; + const allCohorts = admissionsData?.cohorts || []; + + allCohorts.forEach(({ cohort }) => { + if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); + }); + return syllabus; + }; + + const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); + + const getServices = async (userRoles) => { + if (userRoles?.length > 0) { + const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + .then((resp) => { + const data = resp?.data; + if (data !== undefined && data.length > 0) { + return data.map((serv) => ({ + ...serv, + academy: { + id: role?.academy.id, + available_as_saas: role?.academy?.available_as_saas, + }, + })); + } + return []; + })); + const mentorshipResults = await Promise.all(mentorshipPromises); + const recopilatedServices = mentorshipResults.flat(); + + setMentorshipServices({ + isLoading: false, + data: recopilatedServices, + }); + } + }; + + const getAdmissionsData = async () => { + try { + const response = await bc.admissions().me(); + const admissionsFromDB = response.data; + setAdmissionsData(admissionsFromDB); + getServices(admissionsData.roles); + } catch (error) { + console.error('Error fetching admissions data:', error); + } + }; + + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + + const academies = {}; + + mentorshipServices.data.forEach((serv) => { + const { academy, ...restOfService } = serv; + if (!academies[academy.id]) { + academies[academy.id] = { services: [] }; + } + academies[academy.id].services.push(restOfService); + }); + + const academyData = Object.entries(academies).map(([academy, values]) => ({ + id: Number(academy), + services: values.services, + })); + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentors = academyData.map((academy) => bc.mentorship({ + services: academy.services.map((s) => s.slug).join(','), + syllabus: allSyllabus?.join(','), + status: 'ACTIVE', + academy: academy.id, + }).getMentor() + .then((res) => { + const allMentors = res?.data; + return allMentors; + })); + const mentorsList = (await Promise.all(mentors)).flat(); + return mentorsList; + } + + return []; + }; + + const getMentorsAndConsumables = async () => { + const mentors = await getAllMentorsAvailable(); + const reqConsumables = await bc.payment().service().consumable() + .then((res) => res?.data?.mentorship_service_sets.map((mentorshipServiceSet) => bc.mentorship() + .getServiceSet(mentorshipServiceSet?.id) + .then((rs) => ({ + ...rs?.data, + ...mentorshipServiceSet, + })))); + + const allConsumables = await Promise.all(reqConsumables); + + setConsumables(allConsumables); + setAllMentorsAvailable(mentors); + }; + + const manageMentorsData = (serv) => { + const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); + + const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + + // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); + if (relatedConsumables) { + reportDatalayer({ + dataLayer: { + event: 'select_mentorship_service', + path: router.pathname, + consumables_amount: relatedConsumables.balance?.unit, + mentorship_service: serv?.slug, + }, + }); + } + setConsumableOfService({ + ...relatedConsumables, + balance: { + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + }, + available_as_saas: serv?.academy?.available_as_saas, + }); + setMentoryProps({ ...mentoryProps, serv }); + }; + + const handleService = async (serv) => { + try { + if (allMentorsAvailable.length > 0) { + const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); + setMentorsByService(mentorsByServices); + manageMentorsData(serv, mentorsByServices); + if (openSearchService) setOpenSearchService(false); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } else { + manageMentorsData(serv); + if (openSearchService) setOpenSearchService(false); + } + } catch (e) { + toast({ + position: 'top', + title: 'Error', + description: t('alert-message:error-finding-mentors'), + status: 'error', + duration: 7000, + isClosable: true, + }); + } + }; + + const handleServicesByMentorsAvailability = () => { + const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + + if (service) { + const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); + handleService(mentorshipFound[0]); + if (mentorshipFound.length > 0) { + setMentoryProps((prev) => ({ + ...prev, + service: mentorshipFound[0], + })); + } + } + + if (mentor) { + const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); + const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); + if (servicesOfMentor.length > 0) { + setOpenSearchMentor(false); + setMentoryProps((prev) => ({ + ...prev, + mentor: isolateMentor[0], + })); + } + } + + return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; + }; + + const handleMentorSelection = (ment) => { + if (mentoryProps.service.slug) { + setMentoryProps({ ...mentoryProps, ment }); + setSearchProps({ + serviceSearch: '', + mentorSearch: '', + }); + } + if (openSearchMentor) setOpenSearchMentor(false); + }; + + useEffect(() => { + if (mentorshipServices.data && allMentorsAvailable) { + const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); + setServicesByMentorsAvailable(servicesWithMentorsAvailable); + } + }, [mentorshipServices.data, allMentorsAvailable]); + + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + useEffect(() => { + if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { + getMentorsAndConsumables(); + } + }, [mentorshipServices]); + + const handleOpenCloseService = () => { + if (!openSearchService) { + if (!openSearchMentor) setOpenSearchMentor(true); + setMentoryProps({}); + setOpenSearchService(true); + } + }; + + const handleOpenCloseMentor = () => { + setMentoryProps((prevState) => { + const { mentor: mentorToRemove, ...remainingProps } = prevState; + return remainingProps; + }); + setOpenSearchMentor(true); + }; + + const reportBookMentor = () => { + reportDatalayer({ + dataLayer: { + event: 'book_mentorship_session', + path: router.pathname, + consumables_amount: consumableOfService.balance.unit, + mentorship_service: mentoryProps?.service?.slug, + mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, + mentor_id: mentoryProps.mentor.slug, + mentor_booking_url: mentoryProps.mentor.booking_url, + }, + }); + }; + + const handleTitleStep = () => { + if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); + if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + return t('schedule-steps.schedule'); + }; + + // console.log(isTabletOrPhone) + // console.log("available for consume", availableForConsume) + // console.log(servicesByMentorAvailable); + // console.log("Soy admissions Data", admissionsData) + // console.log("All sylabus", allSyllabus) + // console.log("mentorship services", mentorshipServices.data) + // console.log("consumibles", consumables) + // console.log("mentores disponibles", allMentorsAvailable) + // console.log("consumable of service", consumableOfService) + // console.log("consumable of service balance", consumableOfService?.balance?.unit) + console.log('mentoryProps', mentoryProps); + // console.log("available for consume", availableForConsume) + // console.log("access token", accessToken) + // console.log("opensearch service",openSearchService) + // console.log("opensearch mentor",openSearchMentor) + + return !isLoading && user && !mentorshipServices.isLoading && ( + + + {`← ${t('back-to-dash')}`} + + + + {t('supportSideBar.schedule-button')} + + + {t('mentorship.you-have')} + {' '} + {servicesByMentorAvailable?.length} + {' '} + {t('mentorship.mentor-sessions-available')} + + + {handleTitleStep()} + + + {openSearchService + && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.select-type')} + /> + )} + + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading + ? ( + <> + {mentoryProps.service + ? ( + + + {mentoryProps?.service && !openSearchService + && ( + + )} + {mentoryProps.service?.name} + + {mentoryProps?.service && !openSearchService + && ( + + )} + + ) + : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)} + > + + {mentoryProps.serv ? mentoryProps.serv.name : serv.name} + + + ))} + + ) + : ( + + No service + + )} + + + {mentoryProps?.service && ( + <> + {openSearchMentor && !mentoryProps.mentor + && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} + borderBottomRadius="0" + border="0" + padding="0 1px" + placeholder={t('supportSideBar.search-mentor')} + /> + + )} + + {mentorsByService.length > 0 + ? ( + <> + {(mentoryProps.service && mentoryProps.mentor) + ? ( + + + + {`${mentoryProps.mentor?.user?.first_name} + + + + + {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + + + + + + + + ) + : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( + + {i !== 0 && ( + + )} + handleMentorSelection(ment)} + > + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + + + + + + ))} + + ) + : ( + + {t('supportSideBar.no-mentors')} + + )} + + + )} + {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume + && ( + <> + {mentoryProps.mentor.booking_url + ? ( + reportBookMentor()} + href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} + target="_blank" + rel="noopener noreferrer" + background="#0196d1" + display="flex" + marginTop="12px" + padding="10px" + borderRadius="5px" + alignItems="center" + > + + {t('mentorship.action')} + + + + ) + : ( + + {t('supportSideBar.no-mentor-link')} + + )} + + )} + {mentoryProps.service && mentoryProps.mentor && !availableForConsume + && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + + + + ); +} + +export default MentorshipSchedule; From ded7667f299bed4ca0f57fbf40171fdb1e20a5ce Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Fri, 13 Sep 2024 17:44:14 -0400 Subject: [PATCH 30/67] :recycle: refactor of the jsx --- src/pages/mentorship/schedule.jsx | 561 +++++++++--------------------- 1 file changed, 167 insertions(+), 394 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 32b2578dd..d0cb3d1c5 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useMemo, Fragment } from 'react'; +import { useEffect, useState, useMemo } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; import { useRouter } from 'next/router'; @@ -55,21 +55,9 @@ function MentorshipSchedule() { }; const availableForConsume = calculateExistenceOfConsumable(); - const getAllSyllabus = () => { - const syllabus = []; - const allCohorts = admissionsData?.cohorts || []; - - allCohorts.forEach(({ cohort }) => { - if (!syllabus.includes(cohort.syllabus_version.slug)) syllabus.push(cohort.syllabus_version.slug); - }); - return syllabus; - }; - - const allSyllabus = useMemo(getAllSyllabus, [admissionsData]); - const getServices = async (userRoles) => { if (userRoles?.length > 0) { - const mentorshipPromises = userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() + const mentorshipPromises = await userRoles.map((role) => bc.mentorship({ academy: role?.academy?.id }, true).getService() .then((resp) => { const data = resp?.data; if (data !== undefined && data.length > 0) { @@ -98,42 +86,48 @@ function MentorshipSchedule() { const response = await bc.admissions().me(); const admissionsFromDB = response.data; setAdmissionsData(admissionsFromDB); - getServices(admissionsData.roles); + getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const getAllMentorsAvailable = async () => { - const servicesSlugs = mentorshipServices.data.map((serv) => serv?.slug); + const allSyllabus = useMemo(() => { + const allCohorts = admissionsData?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissionsData]); - const academies = {}; + const getAllMentorsAvailable = async () => { + const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); - mentorshipServices.data.forEach((serv) => { - const { academy, ...restOfService } = serv; - if (!academies[academy.id]) { - academies[academy.id] = { services: [] }; + const academies = mentorshipServices.data.reduce((acc, { academy, ...restOfService }) => { + if (!acc[academy.id]) { + acc[academy.id] = { services: [] }; } - academies[academy.id].services.push(restOfService); - }); + acc[academy.id].services.push(restOfService); + return acc; + }, {}); - const academyData = Object.entries(academies).map(([academy, values]) => ({ - id: Number(academy), - services: values.services, + const academyData = Object.entries(academies).map(([id, { services }]) => ({ + id: Number(id), + services, })); - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { - const mentors = academyData.map((academy) => bc.mentorship({ + const getMentorsForAcademy = async (academy) => { + const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, - }).getMentor() - .then((res) => { - const allMentors = res?.data; - return allMentors; - })); - const mentorsList = (await Promise.all(mentors)).flat(); + }).getMentor(); + + return res?.data || []; + }; + + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + const mentorsPromises = academyData.map(getMentorsForAcademy); + const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; } @@ -153,6 +147,7 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); + console.log(allConsumables); setAllMentorsAvailable(mentors); }; @@ -160,10 +155,9 @@ function MentorshipSchedule() { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); + || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) + || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - // const relatedConsumables = consumables.find((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === service?.slug)); if (relatedConsumables) { reportDatalayer({ dataLayer: { @@ -181,9 +175,12 @@ function MentorshipSchedule() { }, available_as_saas: serv?.academy?.available_as_saas, }); - setMentoryProps({ ...mentoryProps, serv }); + + setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; + console.log(consumableOfService); + const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -220,7 +217,7 @@ function MentorshipSchedule() { if (mentorshipFound.length > 0) { setMentoryProps((prev) => ({ ...prev, - service: mentorshipFound[0], + serviceSelected: mentorshipFound[0], })); } } @@ -232,7 +229,7 @@ function MentorshipSchedule() { setOpenSearchMentor(false); setMentoryProps((prev) => ({ ...prev, - mentor: isolateMentor[0], + mentorSelected: isolateMentor[0], })); } } @@ -241,8 +238,8 @@ function MentorshipSchedule() { }; const handleMentorSelection = (ment) => { - if (mentoryProps.service.slug) { - setMentoryProps({ ...mentoryProps, ment }); + if (mentoryProps.serviceSelected.slug) { + setMentoryProps({ ...mentoryProps, mentorSelected: ment }); setSearchProps({ serviceSearch: '', mentorSearch: '', @@ -286,7 +283,7 @@ function MentorshipSchedule() { const handleOpenCloseMentor = () => { setMentoryProps((prevState) => { - const { mentor: mentorToRemove, ...remainingProps } = prevState; + const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; return remainingProps; }); setOpenSearchMentor(true); @@ -298,17 +295,17 @@ function MentorshipSchedule() { event: 'book_mentorship_session', path: router.pathname, consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.service?.slug, - mentor_name: `${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`, - mentor_id: mentoryProps.mentor.slug, - mentor_booking_url: mentoryProps.mentor.booking_url, + mentorship_service: mentoryProps?.serviceSelected?.slug, + mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, + mentor_id: mentoryProps.mentorSelected.slug, + mentor_booking_url: mentoryProps.mentorSelected.booking_url, }, }); }; const handleTitleStep = () => { - if (!mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentorship'); - if (mentoryProps.service && !mentoryProps.mentor) return t('schedule-steps.select-mentor'); + if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; @@ -322,382 +319,158 @@ function MentorshipSchedule() { // console.log("mentores disponibles", allMentorsAvailable) // console.log("consumable of service", consumableOfService) // console.log("consumable of service balance", consumableOfService?.balance?.unit) - console.log('mentoryProps', mentoryProps); + // console.log('mentoryProps', mentoryProps); // console.log("available for consume", availableForConsume) // console.log("access token", accessToken) // console.log("opensearch service",openSearchService) // console.log("opensearch mentor",openSearchMentor) return !isLoading && user && !mentorshipServices.isLoading && ( - - + + {`← ${t('back-to-dash')}`} - + {t('supportSideBar.schedule-button')} - - {t('mentorship.you-have')} - {' '} - {servicesByMentorAvailable?.length} - {' '} - {t('mentorship.mentor-sessions-available')} + + {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - + {handleTitleStep()} - - {openSearchService - && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.select-type')} - /> - )} - - - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading - ? ( - <> - {mentoryProps.service - ? ( - - - {mentoryProps?.service && !openSearchService - && ( - - )} - {mentoryProps.service?.name} - - {mentoryProps?.service && !openSearchService - && ( - - )} - - ) - : servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)} - > - - {mentoryProps.serv ? mentoryProps.serv.name : serv.name} - + {openSearchService && ( + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> + )} + + {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( +
+ {mentoryProps.serviceSelected ? ( + + + {mentoryProps?.serviceSelected && !openSearchService && ( + + )} + {mentoryProps.serviceSelected?.name} + + {mentoryProps?.serviceSelected && !openSearchService + && ( + + )} + + ) : ( + servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( + handleService(serv)}> + + {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - ))} - - ) - : ( - - No service - - )} + + )) + )} +
+ ) : ( + + No service + + )}
- {mentoryProps?.service && ( - <> - {openSearchMentor && !mentoryProps.mentor - && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} - borderBottomRadius="0" - border="0" - padding="0 1px" - placeholder={t('supportSideBar.search-mentor')} - /> - - )} - - {mentorsByService.length > 0 - ? ( - <> - {(mentoryProps.service && mentoryProps.mentor) - ? ( - + + {mentoryProps?.serviceSelected && ( + + {openSearchMentor && !mentoryProps.mentorSelected && ( + + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> + + )} + + {mentorsByService.length > 0 ? ( + + {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( + - - {`${mentoryProps.mentor?.user?.first_name} + + {`${mentoryProps.mentorSelected?.user?.first_name} - - - - {`${mentoryProps.mentor.user.first_name} ${mentoryProps.mentor.user.last_name}`} + + + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - {(mentoryProps.mentor.one_line_bio && mentoryProps.mentor.one_line_bio !== '') ? `${mentoryProps.mentor.one_line_bio} ` : ''} + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - ) - : mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( - - {i !== 0 && ( - - )} - handleMentorSelection(ment)} - > - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + ) : ( + mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( +
+ {i !== 0 && ( + + )} + handleMentorSelection(ment)}> + + {`${ment?.user?.first_name} + + + + + {`${ment.user.first_name} ${ment.user.last_name}`} + + + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} + - - - ))} - - ) - : ( - - {t('supportSideBar.no-mentors')} +
+ )) + )} +
+ ) : ( + + {t('supportSideBar.no-mentors')} + + )} +
+
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( + + {mentoryProps.mentorSelected.booking_url ? ( + reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> + + {t('mentorship.action')} + + + + ) : ( + + {t('supportSideBar.no-mentor-link')} )} - - - )} - {mentoryProps.service && mentoryProps.mentor && !openSearchMentor && !openSearchService && availableForConsume - && ( - <> - {mentoryProps.mentor.booking_url - ? ( - reportBookMentor()} - href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentor?.slug}?utm_campaign=${mentoryProps?.service?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} - target="_blank" - rel="noopener noreferrer" - background="#0196d1" - display="flex" - marginTop="12px" - padding="10px" - borderRadius="5px" - alignItems="center" - > - - {t('mentorship.action')} - - - - ) - : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - {mentoryProps.service && mentoryProps.mentor && !availableForConsume - && ( - - {t('supportSideBar.no-mentoring-available')} - - )} - +
+ )} +
+ + {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( + + {t('supportSideBar.no-mentoring-available')} + + )} + +
); From b4c5b206f5adaccdd0a7387205e27f23eb31791f Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 14:49:50 -0400 Subject: [PATCH 31/67] :sparkles: page to schedule mentorship --- public/locales/en/dashboard.json | 4 +- public/locales/es/dashboard.json | 4 +- src/pages/mentorship/schedule.jsx | 191 ++++++++++++++++++++---------- 3 files changed, 134 insertions(+), 65 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 76d9acebe..61d247661 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -110,7 +110,9 @@ "available-sessions": "available sessions", "tooltip": "Mentorships reload every week", "mentor-sessions-available": "mentor sessions available", - "action": "Schedule session" + "action": "Schedule session", + "no-available": "No services available", + "no-available-for-teacher": "No serives available for" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 1a2ed84d9..dc65a7c56 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -110,7 +110,9 @@ "available-sessions": "sesiones disponibles", "tooltip": "Las tutorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", - "action": "Reservar mentoria" + "action": "Reservar mentoria", + "no-available": "No hay servicios disponibles", + "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { "select-mentorship": "Primero selecciona un tipo de tutoria", diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index d0cb3d1c5..6f442a1ba 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,10 +1,13 @@ -import { useEffect, useState, useMemo } from 'react'; +import { useEffect, useState } from 'react'; import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; - import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; +import PropTypes from 'prop-types'; import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; +import { validatePlanExistence } from '../../common/handlers/subscriptions'; +import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; +import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; @@ -15,30 +18,44 @@ import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; import modifyEnv from '../../../modifyEnv'; +function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { + return ( + + {t('supportSideBar.no-mentoring-available')} + + + ); +} + function MentorshipSchedule() { let isTabletOrPhone = false; if (typeof window !== 'undefined') { isTabletOrPhone = window.innerWidth < 780; } - const { t } = useTranslation('dashboard'); const router = useRouter(); + const { t } = useTranslation('dashboard'); + const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; + const { borderColor } = useStyle(); + const { isLoading, user, isAuthenticated } = useAuth(); const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); const accessToken = getStorageItem('accessToken'); const toast = useToast(); const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); - const { borderColor } = useStyle(); - - const { isLoading, user, isAuthenticated } = useAuth(); - const [mentorshipServices, setMentorshipServices] = useState({ - isLoading: true, - data: [], - }); - const [searchProps, setSearchProps] = useState({ - serviceSearch: '', - mentorSearch: '', - }); - const [admissionsData, setAdmissionsData] = useState(); + const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); + const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); const [openSearchService, setOpenSearchService] = useState(true); const [openSearchMentor, setOpenSearchMentor] = useState(true); @@ -47,6 +64,10 @@ function MentorshipSchedule() { const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); + const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); + const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); + const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -85,19 +106,12 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; - setAdmissionsData(admissionsFromDB); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; - const allSyllabus = useMemo(() => { - const allCohorts = admissionsData?.cohorts || []; - const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; - return syllabus; - }, [admissionsData]); - const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -117,7 +131,6 @@ function MentorshipSchedule() { const getMentorsForAcademy = async (academy) => { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), - syllabus: allSyllabus?.join(','), status: 'ACTIVE', academy: academy.id, }).getMentor(); @@ -125,7 +138,7 @@ function MentorshipSchedule() { return res?.data || []; }; - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + if (servicesSlugs.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -147,31 +160,30 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); - console.log(allConsumables); setAllMentorsAvailable(mentors); }; const manageMentorsData = (serv) => { const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - const relatedConsumables = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) + const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - if (relatedConsumables) { + if (relatedConsumable) { reportDatalayer({ dataLayer: { event: 'select_mentorship_service', path: router.pathname, - consumables_amount: relatedConsumables.balance?.unit, + consumables_amount: relatedConsumable.balance?.unit, mentorship_service: serv?.slug, }, }); } setConsumableOfService({ - ...relatedConsumables, + ...relatedConsumable, balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumables?.balance?.unit, + unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, }, available_as_saas: serv?.academy?.available_as_saas, }); @@ -179,8 +191,6 @@ function MentorshipSchedule() { setMentoryProps({ ...mentoryProps, serviceSelected: serv }); }; - console.log(consumableOfService); - const handleService = async (serv) => { try { if (allMentorsAvailable.length > 0) { @@ -232,6 +242,7 @@ function MentorshipSchedule() { mentorSelected: isolateMentor[0], })); } + return servicesOfMentor; } return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; @@ -304,31 +315,69 @@ function MentorshipSchedule() { }; const handleTitleStep = () => { - if (!mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentorship'); + if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); return t('schedule-steps.schedule'); }; - // console.log(isTabletOrPhone) - // console.log("available for consume", availableForConsume) - // console.log(servicesByMentorAvailable); - // console.log("Soy admissions Data", admissionsData) - // console.log("All sylabus", allSyllabus) - // console.log("mentorship services", mentorshipServices.data) - // console.log("consumibles", consumables) - // console.log("mentores disponibles", allMentorsAvailable) - // console.log("consumable of service", consumableOfService) - // console.log("consumable of service balance", consumableOfService?.balance?.unit) - // console.log('mentoryProps', mentoryProps); - // console.log("available for consume", availableForConsume) - // console.log("access token", accessToken) - // console.log("opensearch service",openSearchService) - // console.log("opensearch mentor",openSearchMentor) + const handleDescription = () => { + if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; + return t('mentorship.no-available'); + }; + + useEffect(() => { + if (mentoryProps.serviceSelected) { + fetchSubscriptions() + .then((data) => { + setSubscriptionData(data); + reportDatalayer({ + dataLayer: { + event: 'subscriptions_load', + method: 'native', + plan_financings: data?.plan_financings?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + subscriptions: data?.subscriptions?.filter((s) => s.status === 'ACTIVE').map((s) => s.plans.filter((p) => p.status === 'ACTIVE').map((p) => p.slug).join(',')).join(','), + }, + }); + }); + } + }, [mentoryProps.serviceSelected]); + + const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { + const paidAtDate = new Date(invoice.paid_at); + return paidAtDate > latest ? paidAtDate : latest; + }, new Date(0)); + + const sortByMostRecentInvoice = (a, b) => { + const latestA = getMostRecentPaidAt(a.invoices); + const latestB = getMostRecentPaidAt(b.invoices); + return latestB - latestA; + }; + + const handleGetMoreMentorships = async () => { + setIsFetchingDataForModal(true); + const academyService = mentoryProps?.serviceSelected; + const allSubscriptions = subscriptionData.subscriptions; + const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); + const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; + + validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { + setDataToGetAccessModal({ + ...data, + event: '', + academyService, + }); + setIsModalToGetAccessOpen(true); + }) + .finally(() => setIsFetchingDataForModal(false)); + }; return !isLoading && user && !mentorshipServices.isLoading && ( - + useColorModeValue('#f9f9f9', '#171f2a')}> - {`← ${t('back-to-dash')}`} + + + {`${t('back-to-dash')}`} + @@ -347,16 +396,16 @@ function MentorshipSchedule() { {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? (
{mentoryProps.serviceSelected ? ( - + {mentoryProps?.serviceSelected && !openSearchService && ( - + )} {mentoryProps.serviceSelected?.name} {mentoryProps?.serviceSelected && !openSearchService && ( - )} @@ -373,7 +422,7 @@ function MentorshipSchedule() {
) : ( - No service + {handleDescription()} )} @@ -390,17 +439,17 @@ function MentorshipSchedule() { {mentorsByService.length > 0 ? ( {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - + - + {`${mentoryProps.mentorSelected?.user?.first_name} - - + + {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - + {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} @@ -426,7 +475,7 @@ function MentorshipSchedule() { {`${ment.user.first_name} ${ment.user.last_name}`} - + {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} @@ -465,15 +514,31 @@ function MentorshipSchedule() { {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - {t('supportSideBar.no-mentoring-available')} - + )} + { + setIsModalToGetAccessOpen(false); + }} + />
); } +NoConsumablesCard.defaultProps = { + isLoading: false, // Valor por defecto cuando no se pasa esta prop +}; + +NoConsumablesCard.propTypes = { + t: PropTypes.func.isRequired, + handleGetMoreMentorships: PropTypes.func.isRequired, + isLoading: PropTypes.bool, +}; + export default MentorshipSchedule; From 677c55dea564e07f3af52dd7bf8764a248ae902a Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 15:58:10 -0400 Subject: [PATCH 32/67] small change --- src/pages/mentorship/schedule.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 6f442a1ba..75b0ea9ad 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -57,17 +57,17 @@ function MentorshipSchedule() { const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); const [consumableOfService, setConsumableOfService] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); const [mentoryProps, setMentoryProps] = useState({}); const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); + const [subscriptionData, setSubscriptionData] = useState([]); + const [openSearchService, setOpenSearchService] = useState(true); + const [openSearchMentor, setOpenSearchMentor] = useState(true); const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - const [subscriptionData, setSubscriptionData] = useState([]); const calculateExistenceOfConsumable = () => { if (consumableOfService.available_as_saas === false) return true; @@ -393,7 +393,7 @@ function MentorshipSchedule() { setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> )} - {servicesByMentorAvailable?.length > 0 && !mentorshipServices.isLoading ? ( + {servicesByMentorAvailable?.length > 0 ? (
{mentoryProps.serviceSelected ? ( From cd8e313717327737a632fedb704f9a38bf3970bf Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:42:31 -0400 Subject: [PATCH 33/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 460 +++++------------------------- 1 file changed, 70 insertions(+), 390 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 75b0ea9ad..89308803a 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -1,43 +1,14 @@ -import { useEffect, useState } from 'react'; -import { useColorModeValue, useToast, Container, Box, InputGroup, Input, Button } from '@chakra-ui/react'; +import { useEffect, useState, useMemo } from 'react'; +import { useColorModeValue, Container, Box } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import useTranslation from 'next-translate/useTranslation'; -import PropTypes from 'prop-types'; -import { getStorageItem } from '../../utils'; import { reportDatalayer } from '../../utils/requests'; -import { validatePlanExistence } from '../../common/handlers/subscriptions'; -import ModalToGetAccess, { stageType } from '../../common/components/ModalToGetAccess'; import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; -import useStyle from '../../common/hooks/useStyle'; import useAuth from '../../common/hooks/useAuth'; import Icon from '../../common/components/Icon'; import Link from '../../common/components/NextChakraLink'; -import Heading from '../../common/components/Heading'; -import Text from '../../common/components/Text'; -import Image from '../../common/components/Image'; import bc from '../../common/services/breathecode'; -import modifyEnv from '../../../modifyEnv'; - -function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { - return ( - - {t('supportSideBar.no-mentoring-available')} - - - ); -} +import MentoringConsumables from '../../common/components/SupportSidebar/MentoringConsumables'; function MentorshipSchedule() { let isTabletOrPhone = false; @@ -48,33 +19,15 @@ function MentorshipSchedule() { const { t } = useTranslation('dashboard'); const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; - const { borderColor } = useStyle(); const { isLoading, user, isAuthenticated } = useAuth(); - const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); - const accessToken = getStorageItem('accessToken'); - const toast = useToast(); - const commonBackground = useColorModeValue('white', 'rgba(255, 255, 255, 0.1)'); const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); - const [servicesByMentorAvailable, setServicesByMentorsAvailable] = useState([]); - const [consumableOfService, setConsumableOfService] = useState({}); + const [mentoryProps, setMentoryProps] = useState({}); + const [admissions, setAdmissions] = useState({}); const [consumables, setConsumables] = useState([]); const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); const [mentorsByService, setMentorsByService] = useState([]); - const [mentoryProps, setMentoryProps] = useState({}); - const [dataToGetAccessModal, setDataToGetAccessModal] = useState({}); const [subscriptionData, setSubscriptionData] = useState([]); - const [openSearchService, setOpenSearchService] = useState(true); - const [openSearchMentor, setOpenSearchMentor] = useState(true); - const [isFetchingDataForModal, setIsFetchingDataForModal] = useState(false); - const [isModalToGetAccessOpen, setIsModalToGetAccessOpen] = useState(false); - - const calculateExistenceOfConsumable = () => { - if (consumableOfService.available_as_saas === false) return true; - if (consumableOfService?.balance) return consumableOfService?.balance?.unit > 0 || consumableOfService?.balance?.unit === -1; - return consumables?.mentorship_service_sets?.length > 0 && Object.values(mentorshipServices.data).length > 0; - }; - const availableForConsume = calculateExistenceOfConsumable(); const getServices = async (userRoles) => { if (userRoles?.length > 0) { @@ -106,12 +59,31 @@ function MentorshipSchedule() { try { const response = await bc.admissions().me(); const admissionsFromDB = response.data; + setAdmissions(response.data); getServices(admissionsFromDB.roles); } catch (error) { console.error('Error fetching admissions data:', error); } }; + useEffect(() => { + const checkAuthAndFetchData = async () => { + if (!isLoading && !isAuthenticated) { + router.push('/login'); + } else if (isAuthenticated) { + await getAdmissionsData(); + } + }; + + checkAuthAndFetchData(); + }, [isLoading, isAuthenticated]); + + const allSyllabus = useMemo(() => { + const allCohorts = admissions?.cohorts || []; + const syllabus = [...new Set(allCohorts.map(({ cohort }) => cohort.syllabus_version.slug))]; + return syllabus; + }, [admissions]); + const getAllMentorsAvailable = async () => { const servicesSlugs = mentorshipServices.data.map(({ slug }) => slug); @@ -132,13 +104,14 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', + syllabus: allSyllabus?.join(','), academy: academy.id, }).getMentor(); return res?.data || []; }; - if (servicesSlugs.length > 0) { + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -163,167 +136,37 @@ function MentorshipSchedule() { setAllMentorsAvailable(mentors); }; - const manageMentorsData = (serv) => { - const filteredConsumables = consumables.filter((consumable) => consumable?.mentorship_services?.some((c) => c?.slug === serv?.slug)); - - const relatedConsumable = filteredConsumables.find((consumable) => consumable?.balance?.unit === -1) - || filteredConsumables.find((consumable) => consumable?.balance?.unit > 0) - || filteredConsumables.find((consumable) => consumable?.balance?.unit === 0); - - if (relatedConsumable) { - reportDatalayer({ - dataLayer: { - event: 'select_mentorship_service', - path: router.pathname, - consumables_amount: relatedConsumable.balance?.unit, - mentorship_service: serv?.slug, - }, - }); - } - setConsumableOfService({ - ...relatedConsumable, - balance: { - unit: serv?.academy?.available_as_saas === false ? -1 : relatedConsumable?.balance?.unit, - }, - available_as_saas: serv?.academy?.available_as_saas, - }); - - setMentoryProps({ ...mentoryProps, serviceSelected: serv }); - }; - - const handleService = async (serv) => { - try { - if (allMentorsAvailable.length > 0) { - const mentorsByServices = allMentorsAvailable.filter((ment) => ment.services.some((s) => s.slug === serv.slug)); - setMentorsByService(mentorsByServices); - manageMentorsData(serv, mentorsByServices); - if (openSearchService) setOpenSearchService(false); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } else { - manageMentorsData(serv); - if (openSearchService) setOpenSearchService(false); - } - } catch (e) { - toast({ - position: 'top', - title: 'Error', - description: t('alert-message:error-finding-mentors'), - status: 'error', - duration: 7000, - isClosable: true, - }); - } - }; - - const handleServicesByMentorsAvailability = () => { - const servicesWithMentors = mentorshipServices.data.filter((serv) => allMentorsAvailable.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - - if (service) { - const mentorshipFound = servicesWithMentors.filter((serv) => serv.slug === service); - handleService(mentorshipFound[0]); - if (mentorshipFound.length > 0) { - setMentoryProps((prev) => ({ - ...prev, - serviceSelected: mentorshipFound[0], - })); - } - } - - if (mentor) { - const isolateMentor = allMentorsAvailable.filter((ment) => ment.slug === mentor); - const servicesOfMentor = mentorshipServices.data.filter((serv) => isolateMentor.some((ment) => ment.services.some((mentorService) => serv.slug === mentorService.slug))); - if (servicesOfMentor.length > 0) { - setOpenSearchMentor(false); - setMentoryProps((prev) => ({ - ...prev, - mentorSelected: isolateMentor[0], - })); - } - return servicesOfMentor; - } - - return servicesWithMentors.length > 0 ? servicesWithMentors : allMentorsAvailable; - }; - - const handleMentorSelection = (ment) => { - if (mentoryProps.serviceSelected.slug) { - setMentoryProps({ ...mentoryProps, mentorSelected: ment }); - setSearchProps({ - serviceSearch: '', - mentorSearch: '', - }); - } - if (openSearchMentor) setOpenSearchMentor(false); - }; - - useEffect(() => { - if (mentorshipServices.data && allMentorsAvailable) { - const servicesWithMentorsAvailable = handleServicesByMentorsAvailability(); - setServicesByMentorsAvailable(servicesWithMentorsAvailable); - } - }, [mentorshipServices.data, allMentorsAvailable]); - - useEffect(() => { - const checkAuthAndFetchData = async () => { - if (!isLoading && !isAuthenticated) { - router.push('/login'); - } else if (isAuthenticated) { - await getAdmissionsData(); - } - }; - - checkAuthAndFetchData(); - }, [isLoading, isAuthenticated]); - useEffect(() => { if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { getMentorsAndConsumables(); } }, [mentorshipServices]); - const handleOpenCloseService = () => { - if (!openSearchService) { - if (!openSearchMentor) setOpenSearchMentor(true); - setMentoryProps({}); - setOpenSearchService(true); - } - }; - - const handleOpenCloseMentor = () => { - setMentoryProps((prevState) => { - const { mentorSelected: mentorToRemove, ...remainingProps } = prevState; - return remainingProps; - }); - setOpenSearchMentor(true); - }; - - const reportBookMentor = () => { - reportDatalayer({ - dataLayer: { - event: 'book_mentorship_session', - path: router.pathname, - consumables_amount: consumableOfService.balance.unit, - mentorship_service: mentoryProps?.serviceSelected?.slug, - mentor_name: `${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`, - mentor_id: mentoryProps.mentorSelected.slug, - mentor_booking_url: mentoryProps.mentorSelected.booking_url, - }, - }); - }; + const mentorsFiltered = mentorsByService.filter( + (ment) => { + const fullName = `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase(); + return ( + fullName.includes(searchProps.mentorSearch) + && ment.services.some((sv) => sv.status === 'ACTIVE' && sv.slug === mentoryProps?.service?.slug) + ); + }, + ); - const handleTitleStep = () => { - if (!mentoryProps.serviceSelected) return t('schedule-steps.select-mentorship'); - if (mentoryProps.serviceSelected && !mentoryProps.mentorSelected) return t('schedule-steps.select-mentor'); - return t('schedule-steps.schedule'); - }; + const filterServices = () => { + if (subscriptionData?.selected_mentorship_service_set?.mentorship_services?.length > 0) { + return subscriptionData?.selected_mentorship_service_set?.mentorship_services?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } + if (mentorshipServices.data.length > 0) { + return mentorshipServices.data?.filter( + (l) => l.name.toLowerCase().includes(searchProps.serviceSearch), + ); + } - const handleDescription = () => { - if (mentor) return `${t('mentorship.no-available-for-teacher')} ${mentor}`; - return t('mentorship.no-available'); + return []; }; + const suscriptionServicesFiltered = filterServices(); useEffect(() => { if (mentoryProps.serviceSelected) { @@ -342,203 +185,40 @@ function MentorshipSchedule() { } }, [mentoryProps.serviceSelected]); - const getMostRecentPaidAt = (invoices) => invoices.reduce((latest, invoice) => { - const paidAtDate = new Date(invoice.paid_at); - return paidAtDate > latest ? paidAtDate : latest; - }, new Date(0)); - - const sortByMostRecentInvoice = (a, b) => { - const latestA = getMostRecentPaidAt(a.invoices); - const latestB = getMostRecentPaidAt(b.invoices); - return latestB - latestA; - }; - - const handleGetMoreMentorships = async () => { - setIsFetchingDataForModal(true); - const academyService = mentoryProps?.serviceSelected; - const allSubscriptions = subscriptionData.subscriptions; - const currentServiceSubscription = Array.isArray(allSubscriptions) && allSubscriptions.sort(sortByMostRecentInvoice).find((subscription) => subscription.selected_mentorship_service_set.mentorship_services.some((serv) => serv.slug === mentoryProps?.serviceSelected?.slug)); - const currentSubscription = currentServiceSubscription || allSubscriptions?.[0]; - - validatePlanExistence(allSubscriptions, currentSubscription?.plans?.[0]?.slug).then((data) => { - setDataToGetAccessModal({ - ...data, - event: '', - academyService, - }); - setIsModalToGetAccessOpen(true); - }) - .finally(() => setIsFetchingDataForModal(false)); - }; - return !isLoading && user && !mentorshipServices.isLoading && ( - useColorModeValue('#f9f9f9', '#171f2a')}> + useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> {`${t('back-to-dash')}`} - - - {t('supportSideBar.schedule-button')} - - - {`${t('mentorship.you-have')} ${servicesByMentorAvailable?.length} ${t('mentorship.mentor-sessions-available')}`} - - - {handleTitleStep()} - - {openSearchService && ( - setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.select-type')} /> - )} - - {servicesByMentorAvailable?.length > 0 ? ( -
- {mentoryProps.serviceSelected ? ( - - - {mentoryProps?.serviceSelected && !openSearchService && ( - - )} - {mentoryProps.serviceSelected?.name} - - {mentoryProps?.serviceSelected && !openSearchService - && ( - - )} - - ) : ( - servicesByMentorAvailable.filter((serv) => serv.name.toLowerCase().includes(searchProps.serviceSearch || '')).map((serv) => ( - handleService(serv)}> - - {mentoryProps.serviceSelected ? mentoryProps.serviceSelected.name : serv.name} - - - )) - )} -
- ) : ( - - {handleDescription()} - - )} -
-
- - {mentoryProps?.serviceSelected && ( - - {openSearchMentor && !mentoryProps.mentorSelected && ( - - setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} borderBottomRadius="0" border="0" padding="0 1px" placeholder={t('supportSideBar.search-mentor')} /> - - )} - - {mentorsByService.length > 0 ? ( - - {(mentoryProps.serviceSelected && mentoryProps.mentorSelected) ? ( - - - - {`${mentoryProps.mentorSelected?.user?.first_name} - - - - - {`${mentoryProps.mentorSelected.user.first_name} ${mentoryProps.mentorSelected.user.last_name}`} - - - {(mentoryProps.mentorSelected.one_line_bio && mentoryProps.mentorSelected.one_line_bio !== '') ? `${mentoryProps.mentorSelected.one_line_bio} ` : ''} - - - - - - - - ) : ( - mentorsByService.filter((ment) => `${ment.user.first_name} ${ment.user.last_name}`.toLowerCase().includes(searchProps.mentorSearch || '')).map((ment, i) => ( -
- {i !== 0 && ( - - )} - handleMentorSelection(ment)}> - - {`${ment?.user?.first_name} - - - - - {`${ment.user.first_name} ${ment.user.last_name}`} - - - {(ment.one_line_bio && ment.one_line_bio !== '') ? `${ment.one_line_bio} ` : ''} - - - - -
- )) - )} -
- ) : ( - - {t('supportSideBar.no-mentors')} - - )} -
-
- )} -
- - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !openSearchMentor && !openSearchService && availableForConsume && ( - - {mentoryProps.mentorSelected.booking_url ? ( - reportBookMentor()} href={`${BREATHECODE_HOST}/mentor/${mentoryProps.mentorSelected?.slug}?utm_campaign=${mentoryProps?.serviceSelected?.slug}&utm_source=4geeks&salesforce_uuid=${user?.id}&token=${accessToken}`} target="_blank" rel="noopener noreferrer" background="#0196d1" display="flex" marginTop="12px" padding="10px" borderRadius="5px" alignItems="center"> - - {t('mentorship.action')} - - - - ) : ( - - {t('supportSideBar.no-mentor-link')} - - )} - - )} - - - {mentoryProps.serviceSelected && mentoryProps.mentorSelected && !availableForConsume && ( - - )} - - { - setIsModalToGetAccessOpen(false); + + + - +
); } -NoConsumablesCard.defaultProps = { - isLoading: false, // Valor por defecto cuando no se pasa esta prop -}; - -NoConsumablesCard.propTypes = { - t: PropTypes.func.isRequired, - handleGetMoreMentorships: PropTypes.func.isRequired, - isLoading: PropTypes.bool, -}; - export default MentorshipSchedule; From 07b45e30dc7648db27caa1f6bb6e034daf4fdeee Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:57:50 -0400 Subject: [PATCH 34/67] Update dashboard.json --- public/locales/es/dashboard.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index dc65a7c56..9198387c0 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -73,17 +73,17 @@ "no-mentors": "No hay mentores disponibles", "no-mentor-link": "No hay enlace para crear sesión con este mentor", "search-mentor": "Buscar mentor por nombre", - "start-mentorship": "Para comenzar, seleccione un tipo de tutoría", + "start-mentorship": "Para comenzar, seleccione un tipo de mentoría", "mentoring-label": "Mentoría", - "mentoring": "Programe una sesión de tutoría 1-1.", - "select-type": "Seleccione un tipo de tutoría", - "mentoring-available": "Tutorías disponibles", - "no-mentoring-available": "No tienes tutorías disponibles.", + "mentoring": "Programe una sesión de mentoría 1-1.", + "select-type": "Seleccione un tipo de mentoría", + "mentoring-available": "Mentorías disponibles", + "no-mentoring-available": "No tienes mentorías disponibles.", "create-session-text": "Agendar ahora", "learn-more": "Aprender mas.", "learn-more-link": "https://4geeks.com/es/docs/knowledge-base-4geeks/sesiones-de-tutoria", "get-more-mentorships": "Consigue más mentorías", - "schedule-button": "Programar una sesión de tutoría", + "schedule-button": "Programar una sesión de mentoría", "mentors-available": "+{{count}} Mentores disponibles", "for": "para", "service-not-found": "no se ha encontrado servicio", @@ -104,18 +104,18 @@ ] }, "mentorship": { - "no-mentorship": "Se ha quedado sin tutorías, pero no se preocupe, puede obtener más con unos pocos clics.", - "get-unlimited-mentorship": "y obtén tutoría ilimitada", + "no-mentorship": "Se ha quedado sin mentorías, pero no se preocupe, puede obtener más con unos pocos clics.", + "get-unlimited-mentorship": "y obtén mentorías ilimitadas", "you-have": "Tienes", "available-sessions": "sesiones disponibles", - "tooltip": "Las tutorías se recargan cada semana", + "tooltip": "Las mentorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", "action": "Reservar mentoria", "no-available": "No hay servicios disponibles", "no-available-for-teacher": "No hay servicios disponibles para" }, "schedule-steps": { - "select-mentorship": "Primero selecciona un tipo de tutoria", + "select-mentorship": "Primero selecciona un tipo de mentoría", "select-mentor": "Ahora busca un mentor", "schedule": "Agenda la sesión" }, @@ -207,4 +207,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} \ No newline at end of file +} From 893ecd910f21d079874593a88138081d02467617 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:50:12 -0400 Subject: [PATCH 35/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 89308803a..9cd560e58 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -190,7 +190,7 @@ function MentorshipSchedule() { - {`${t('back-to-dash')}`} + {`${t('back-to-dashboard')}`} From f397594083ae2452ae09f8c128a72ec5eb9c7bfb Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Sun, 22 Sep 2024 23:17:15 -0400 Subject: [PATCH 36/67] small change --- src/pages/mentorship/schedule.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 9cd560e58..8ca8c19f4 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -104,7 +104,7 @@ function MentorshipSchedule() { const res = await bc.mentorship({ services: academy.services.map((s) => s.slug).join(','), status: 'ACTIVE', - syllabus: allSyllabus?.join(','), + syllabus: allSyllabus?.join(',') || undefined, academy: academy.id, }).getMentor(); From 6f50e0b56c099fa737ac6d3a52cdfa9a3f52bc2f Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 24 Sep 2024 17:00:06 -0400 Subject: [PATCH 37/67] merge --- public/locales/es/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 9198387c0..c1861921d 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -207,4 +207,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} +} \ No newline at end of file From b285ff66264f3f61e0d9d63f50eed313ce7d3409 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Fri, 13 Sep 2024 17:44:14 -0400 Subject: [PATCH 38/67] :recycle: refactor of the jsx --- src/pages/mentorship/schedule.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 8ca8c19f4..25c4c60e0 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -133,6 +133,7 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); + console.log(allConsumables); setAllMentorsAvailable(mentors); }; From 90cbce5610921aa55ffd06b8ba009e0076461488 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 16 Sep 2024 14:49:50 -0400 Subject: [PATCH 39/67] :sparkles: page to schedule mentorship --- src/pages/mentorship/schedule.jsx | 34 +++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 25c4c60e0..a39759d66 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -10,6 +10,27 @@ import Link from '../../common/components/NextChakraLink'; import bc from '../../common/services/breathecode'; import MentoringConsumables from '../../common/components/SupportSidebar/MentoringConsumables'; +function NoConsumablesCard({ t, handleGetMoreMentorships, isLoading }) { + return ( + + {t('supportSideBar.no-mentoring-available')} + + + ); +} + function MentorshipSchedule() { let isTabletOrPhone = false; if (typeof window !== 'undefined') { @@ -111,7 +132,7 @@ function MentorshipSchedule() { return res?.data || []; }; - if (servicesSlugs.length > 0 || allSyllabus.length > 0) { + if (servicesSlugs.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -133,7 +154,6 @@ function MentorshipSchedule() { const allConsumables = await Promise.all(reqConsumables); setConsumables(allConsumables); - console.log(allConsumables); setAllMentorsAvailable(mentors); }; @@ -222,4 +242,14 @@ function MentorshipSchedule() { ); } +NoConsumablesCard.defaultProps = { + isLoading: false, // Valor por defecto cuando no se pasa esta prop +}; + +NoConsumablesCard.propTypes = { + t: PropTypes.func.isRequired, + handleGetMoreMentorships: PropTypes.func.isRequired, + isLoading: PropTypes.bool, +}; + export default MentorshipSchedule; From 1b4805cbc53d5bd8ad1091c8ce5d84811fb29d83 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 18 Sep 2024 14:42:31 -0400 Subject: [PATCH 40/67] :recycle: using Mentoring consumables component --- src/pages/mentorship/schedule.jsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index a39759d66..78bb6b129 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -132,7 +132,7 @@ function MentorshipSchedule() { return res?.data || []; }; - if (servicesSlugs.length > 0) { + if (servicesSlugs.length > 0 || allSyllabus.length > 0) { const mentorsPromises = academyData.map(getMentorsForAcademy); const mentorsList = (await Promise.all(mentorsPromises)).flat(); return mentorsList; @@ -242,14 +242,4 @@ function MentorshipSchedule() { ); } -NoConsumablesCard.defaultProps = { - isLoading: false, // Valor por defecto cuando no se pasa esta prop -}; - -NoConsumablesCard.propTypes = { - t: PropTypes.func.isRequired, - handleGetMoreMentorships: PropTypes.func.isRequired, - isLoading: PropTypes.bool, -}; - export default MentorshipSchedule; From d8c6346c1b5862f58ac4f22e2a2b7b5023de6402 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:57:50 -0400 Subject: [PATCH 41/67] Update dashboard.json --- public/locales/es/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index c1861921d..9198387c0 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -207,4 +207,4 @@ "title": "Bienvenido a 4Geeks!", "description": "Mira este breve video que explica cómo aprovechar al máximo 4Geeks y mejorar tu experiencia de aprendizaje" } -} \ No newline at end of file +} From 17354770b1147fc9e6bdd41cc662789851f823f4 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:51:06 -0400 Subject: [PATCH 42/67] Update dashboard.json --- public/locales/en/dashboard.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 61d247661..332b175b3 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -112,7 +112,7 @@ "mentor-sessions-available": "mentor sessions available", "action": "Schedule session", "no-available": "No services available", - "no-available-for-teacher": "No serives available for" + "no-available-for-teacher": "No services available for" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", @@ -207,4 +207,4 @@ "title": "Welcome to 4Geeks!", "description": "Watch this short video that explains how to get the most out of 4Geeks and enhance your learning experience" } -} \ No newline at end of file +} From 07a23b1a75d8949fbc35ce113e162a0b6e8c5c3a Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:33:39 -0400 Subject: [PATCH 43/67] Update dashboard.json --- public/locales/en/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 332b175b3..25d220d1d 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -109,7 +109,7 @@ "you-have": "You have", "available-sessions": "available sessions", "tooltip": "Mentorships reload every week", - "mentor-sessions-available": "mentor sessions available", + "mentor-sessions-available": "mentoring sessions available", "action": "Schedule session", "no-available": "No services available", "no-available-for-teacher": "No services available for" From c2e389468b3c42f27dfd7a27ea2e3a4efb7a45d0 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:34:39 -0400 Subject: [PATCH 44/67] Update dashboard.json --- public/locales/es/dashboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 9198387c0..262a7055f 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -4,7 +4,7 @@ }, "title": "Tus noticias", "moduleMap": "Mapa de módulos", - "back-to-dashboard": "Volver a dashboard", + "back-to-dashboard": "Volver al dashboard", "backToChooseProgram": "Volver a elegir programa", "progressText": "Progreso en el programa", "whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.", From f113aaf51e0ba58d19208c518a9fb421c16a6689 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 24 Sep 2024 19:01:09 -0400 Subject: [PATCH 45/67] :recycle: small refactor --- i18n.js | 2 +- public/locales/en/dashboard.json | 4 +--- public/locales/es/dashboard.json | 1 - src/common/components/FooterTC.jsx | 6 +++++- .../components/SupportSidebar/MentoringConsumables.jsx | 10 +++++----- src/pages/mentorship/schedule.jsx | 10 +++++----- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/i18n.js b/i18n.js index f85d909ff..bb5fb1bab 100644 --- a/i18n.js +++ b/i18n.js @@ -25,7 +25,7 @@ module.exports = { '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]': ['syllabus', 'dashboard', 'projects', 'assignments'], '/survey/[surveyId]': ['survey'], '/mentorship': ['mentorship'], - '/mentorship/schedule': ['mentorship', 'dashboard'], + '/mentorship/schedule': ['mentorship', 'dashboard', 'signup', 'common'], '/how-to': ['how-to'], '/pricing': ['pricing', 'signup'], '/how-to/[slug]': ['how-to'], diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 28f7b2892..9b22f3123 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -3,9 +3,8 @@ "title": "Dashboard" }, "title": "Your News", - "back-to-dashboard": "Back to dashboard", - "backToChooseProgram": "Back to choose program", "moduleMap": "Module map", + "backToChooseProgram": "Back to choose program", "progressText": "progress in the program", "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!", @@ -85,7 +84,6 @@ "get-more-mentorships": "Get more mentorships", "schedule-button": "Schedule a mentoring session", "mentors-available": "+{{count}} Mentors available", - "for": "for", "service-not-found": "couldn't find service", "mentor-not-found": "couldn't find mentor", "actionButtons": [ diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index 262a7055f..f6ca28cc3 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -4,7 +4,6 @@ }, "title": "Tus noticias", "moduleMap": "Mapa de módulos", - "back-to-dashboard": "Volver al dashboard", "backToChooseProgram": "Volver a elegir programa", "progressText": "Progreso en el programa", "whiteLabeledText": "Este curso es traído a ti gracias a nuestra alianza con esta universidad.", diff --git a/src/common/components/FooterTC.jsx b/src/common/components/FooterTC.jsx index 4f785dbf6..c72db4237 100644 --- a/src/common/components/FooterTC.jsx +++ b/src/common/components/FooterTC.jsx @@ -18,7 +18,11 @@ function FooterTC({ pageProps }) { const copyrightName = pageProps?.existsWhiteLabel ? logoData.name : '4Geeks'; const actualYear = new Date().getFullYear(); const router = useRouter(); - const noFooterRoutes = ['/cohort/[cohortSlug]/[slug]/[version]', '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]']; + const noFooterRoutes = [ + '/cohort/[cohortSlug]/[slug]/[version]', + '/syllabus/[cohortSlug]/[lesson]/[lessonSlug]', + '/mentorship/schedule', + ]; if (noFooterRoutes.includes(router.pathname)) { return null; diff --git a/src/common/components/SupportSidebar/MentoringConsumables.jsx b/src/common/components/SupportSidebar/MentoringConsumables.jsx index fddb8b96e..a5775c9c0 100644 --- a/src/common/components/SupportSidebar/MentoringConsumables.jsx +++ b/src/common/components/SupportSidebar/MentoringConsumables.jsx @@ -238,7 +238,7 @@ function MentoringConsumables({ toast({ position: 'top', title: 'Error', - description: `${t('supportSideBar.service-not-found')} "${queryService}" ${queryMentor ? `${t('supportSideBar.for')} "${queryMentor}"` : ''}`, + description: `${t('supportSideBar.service-not-found')} "${queryService}" ${queryMentor ? `${t('common:word-connector.for')} "${queryMentor}"` : ''}`, status: 'error', duration: 7000, isClosable: true, @@ -313,7 +313,7 @@ function MentoringConsumables({ {!mentoryProps?.service && (consumables?.mentorship_service_sets?.length !== 0 || currentBalance !== 0) && ( <> - + {t('supportSideBar.mentoring')}
@@ -373,7 +373,7 @@ function MentoringConsumables({ )} {open && mentoryProps?.service && !mentoryProps?.mentor && existConsumablesOnCurrentService && ( - + {t('mentorship.you-have')} @@ -395,7 +395,7 @@ function MentoringConsumables({ && ( <> {mentoryProps?.service && ( - 0 ? '22px' : '34px'} px="20px" py="15px" textAlign="center" w="100%" borderTopRadius="0.375rem"> + @@ -522,9 +522,9 @@ MentoringConsumables.propTypes = { MentoringConsumables.defaultProps = { queryService: undefined, queryMentor: undefined, + titleSize: undefined, mentoryProps: [], width: '100%', - titleSize: '14px', consumables: {}, programServices: [], setProgramMentors: () => { }, diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 8ca8c19f4..ce5166483 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -16,7 +16,7 @@ function MentorshipSchedule() { isTabletOrPhone = window.innerWidth < 780; } const router = useRouter(); - const { t } = useTranslation('dashboard'); + const { t } = useTranslation('signup'); const { fetchSubscriptions } = useSubscriptionsHandler(); const { service, mentor } = router.query; const { isLoading, user, isAuthenticated } = useAuth(); @@ -186,20 +186,20 @@ function MentorshipSchedule() { }, [mentoryProps.serviceSelected]); return !isLoading && user && !mentorshipServices.isLoading && ( - useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> + useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> - {`${t('back-to-dashboard')}`} + {`${t('consumables.back-to-dashboard')}`} - + useColorModeValue('white', '#27333f')}> Date: Tue, 24 Sep 2024 19:06:16 -0400 Subject: [PATCH 46/67] :recycle: samll refactor --- src/js_modules/checkout/PaymentInfo.jsx | 26 +++++++++++++------------ src/js_modules/checkout/Summary.jsx | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx index bba24265d..53e2fc08d 100644 --- a/src/js_modules/checkout/PaymentInfo.jsx +++ b/src/js_modules/checkout/PaymentInfo.jsx @@ -56,17 +56,19 @@ function PaymentInfo() { const paymentStatusBgColor = isPaymentSuccess ? 'green.light' : '#ffefef'; const redirectTocohort = () => { - if (isPaymentSuccess) { - const langLink = lang !== 'en' ? `/${lang}` : ''; - const syllabusVersion = cohortFound?.syllabus_version; - axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; - const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; - setCohortSession({ - ...cohortFound, - selectedProgramSlug: cohortDashboardLink, - }); - router.push(cohortDashboardLink); - } else setPaymentStatus('idle'); + if (!isPaymentSuccess) { + setPaymentStatus('idle'); + return; + } + const langLink = lang !== 'en' ? `/${lang}` : ''; + const syllabusVersion = cohortFound?.syllabus_version; + axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; + const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; + setCohortSession({ + ...cohortFound, + selectedProgramSlug: cohortDashboardLink, + }); + router.push(cohortDashboardLink); }; const joinCohort = (cohort) => { @@ -379,7 +381,7 @@ function PaymentInfo() { // mt="12px" isDisabled={isPaymentSuccess && !cohortFound} isLoading={isSubmittingPayment} - onClick={() => redirectTocohort()} + onClick={redirectTocohort} > {isPaymentSuccess ? 'Start learning' : 'Try again'} diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index d903a7e91..0c0aeb0d9 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -498,7 +498,7 @@ function Summary() { // mt="12px" isDisabled={isPaymentSuccess && !cohortFound} isLoading={isSubmitting} - onClick={() => redirectTocohort()} + onClick={redirectTocohort} > {isPaymentSuccess ? t('start-free-course') : t('try-again')} From 576e1075078f3edfa476b3db4971903236828944 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 24 Sep 2024 19:14:14 -0400 Subject: [PATCH 47/67] :recycle: samll refactor --- public/locales/en/dashboard.json | 3 +-- public/locales/es/dashboard.json | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 9b22f3123..a5357150a 100644 --- a/public/locales/en/dashboard.json +++ b/public/locales/en/dashboard.json @@ -109,8 +109,7 @@ "tooltip": "Mentorships reload every week", "mentor-sessions-available": "mentoring sessions available", "action": "Schedule session", - "no-available": "No services available", - "no-available-for-teacher": "No services available for" + "no-available": "No services available" }, "schedule-steps": { "select-mentorship": "First select a type of mentorship", diff --git a/public/locales/es/dashboard.json b/public/locales/es/dashboard.json index f6ca28cc3..e7897dc8f 100644 --- a/public/locales/es/dashboard.json +++ b/public/locales/es/dashboard.json @@ -110,8 +110,7 @@ "tooltip": "Las mentorías se recargan cada semana", "mentor-sessions-available": "sesiones de mentoria disponibles", "action": "Reservar mentoria", - "no-available": "No hay servicios disponibles", - "no-available-for-teacher": "No hay servicios disponibles para" + "no-available": "No hay servicios disponibles" }, "schedule-steps": { "select-mentorship": "Primero selecciona un tipo de mentoría", From e8e647651c259fb0acc5cae6350679a6f5d905fa Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 25 Sep 2024 15:09:25 -0400 Subject: [PATCH 48/67] :recycle: small refactor of router.querys and :bug: bug solving realted to usePersistent and coupons --- public/locales/en/signup.json | 3 ++- public/locales/es/signup.json | 3 ++- src/common/hooks/usePersistent.js | 13 +++++++++---- src/js_modules/checkout/ChooseYourClass.jsx | 1 - src/js_modules/checkout/Summary.jsx | 2 -- src/pages/bootcamp/[course_slug].jsx | 3 ++- src/pages/checkout/index.jsx | 8 ++------ 7 files changed, 17 insertions(+), 16 deletions(-) diff --git a/public/locales/en/signup.json b/public/locales/en/signup.json index 0bcbaeecd..60f77b131 100644 --- a/public/locales/en/signup.json +++ b/public/locales/en/signup.json @@ -158,7 +158,8 @@ "validate-email-title": "Verify your email", "validate-email-description": "We sent a confirmation email to: {{email}}, please check your email and click on the confirmation link.", "email-sent-to": "We sent you a confirmation email to {{email}}!", - "email-already-sent": "You have a pending invitation sent less than 1 day ago, check your email" + "email-already-sent": "You have a pending invitation sent less than 1 day ago, check your email", + "coupon-already-applied": "Coupon already applied" }, "alert-message-validate-email": { "title": "Verify email", diff --git a/public/locales/es/signup.json b/public/locales/es/signup.json index 7732374cb..408f651b9 100644 --- a/public/locales/es/signup.json +++ b/public/locales/es/signup.json @@ -158,7 +158,8 @@ "validate-email-title": "Verifique su correo electrónico", "validate-email-description": "Enviamos un correo electrónico de confirmación a: {{email}}, verifica tu correo electrónico y haz clic en el enlace de confirmación.", "email-sent-to": "Enviamos un correo electrónico de confirmación a {{email}}!", - "email-already-sent": "Tienes una invitación pendiente enviada hace menos de 1 día, revisa tu correo" + "email-already-sent": "Tienes una invitación pendiente enviada hace menos de 1 día, revisa tu correo", + "coupon-already-applied": "El cupón ya esta siendo aplicado" }, "alert-message-validate-email": { "title": "Verificar correo electrónico", diff --git a/src/common/hooks/usePersistent.js b/src/common/hooks/usePersistent.js index 43950060c..bb590925e 100644 --- a/src/common/hooks/usePersistent.js +++ b/src/common/hooks/usePersistent.js @@ -23,9 +23,13 @@ const usePersistent = (key, initialValue) => { const usePersistentBySession = (key, initialValue) => { const getStoredValues = useMemo(() => { const item = isWindow ? window.sessionStorage.getItem(key) : null; - const isObject = typeof item === 'object'; - const objectValue = JSON.parse(item) || initialValue; - return isObject ? objectValue : item || initialValue; + if (item === null) return initialValue; + + if (item.startsWith('{') || item.startsWith('[')) { + return JSON.parse(item); + } + + return item; }, [key, initialValue]); const [storedValue, setStoredValue] = useState(getStoredValues); @@ -33,7 +37,8 @@ const usePersistentBySession = (key, initialValue) => { const setValue = (value) => { try { setStoredValue(value); - window.sessionStorage.setItem(key, JSON.stringify(value)); + const valueToStore = typeof value === 'object' ? JSON.stringify(value) : value; + window.sessionStorage.setItem(key, valueToStore); } catch (error) { console.error('usePersistent_error:', error); } diff --git a/src/js_modules/checkout/ChooseYourClass.jsx b/src/js_modules/checkout/ChooseYourClass.jsx index 5fe8fbaa9..6e9028903 100644 --- a/src/js_modules/checkout/ChooseYourClass.jsx +++ b/src/js_modules/checkout/ChooseYourClass.jsx @@ -87,7 +87,6 @@ function ChooseYourClass({ }; }); - //SI NO AÑADO COHORTS ENTONCES TENGO QUE SABER LA IMPORTANCIA const filteredCohorts = Array.isArray(formatedData) ? formatedData.filter((item) => item?.never_ends === false) : null; setCohorts({ cohorts: filteredCohorts, diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index bb99c5fde..4e1a0283a 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -194,8 +194,6 @@ function Summary() { query: { ...router.query, plan_id: selectedPlanCheckoutData?.plan_id, - price: selectedPlanCheckoutData?.price, - period: selectedPlanCheckoutData?.period, }, }); } else { diff --git a/src/pages/bootcamp/[course_slug].jsx b/src/pages/bootcamp/[course_slug].jsx index cf7befc04..e0aa35849 100644 --- a/src/pages/bootcamp/[course_slug].jsx +++ b/src/pages/bootcamp/[course_slug].jsx @@ -207,7 +207,8 @@ function Page({ data }) { plan: featuredPlanToEnroll?.plan_slug, plan_id: featuredPlanToEnroll?.plan_id, has_available_cohorts: planData?.has_available_cohorts, - }) : `?plan=${data?.plan_slug}`; + cohort: cohortId, + }) : `?plan=${data?.plan_slug}&cohort=${cohortId}`; const getPlanPrice = () => { if (featuredPlanToEnroll?.plan_slug) { diff --git a/src/pages/checkout/index.jsx b/src/pages/checkout/index.jsx index c586bf1a4..96d36e9c6 100644 --- a/src/pages/checkout/index.jsx +++ b/src/pages/checkout/index.jsx @@ -100,8 +100,6 @@ function Checkout() { const cohorts = cohortsData?.cohorts; - console.log(checkoutData); - axiosInstance.defaults.headers.common['Accept-Language'] = router.locale; const { user, isAuthenticated, isLoading } = useAuth(); const { userSession } = useSession(); @@ -116,6 +114,7 @@ function Checkout() { const tokenExists = accessToken !== null && accessToken !== undefined && accessToken.length > 5; const { coupon: couponQuery } = query; const { course } = router.query; + const courseChoosed = course; const [coupon] = usePersistentBySession('coupon', ''); @@ -125,8 +124,6 @@ function Checkout() { return couponString || formatedCouponQuery; }, [coupon, couponQuery]); - const courseChoosed = course; - const queryPlanExists = planFormated !== undefined && planFormated?.length > 0; const queryMentorshipServiceSlugExists = mentorshipServiceSetSlug && mentorshipServiceSetSlug?.length > 0; const queryEventTypeSetSlugExists = eventTypeSetSlug && eventTypeSetSlug?.length > 0; @@ -164,13 +161,12 @@ function Checkout() { }; const handleCoupon = (coupons, actions) => { - // Verificar si el cupón ingresado ya está aplicado const alreadyAppliedCoupon = selfAppliedCoupon?.slug === discountCode || selfAppliedCoupon?.slug === couponValue; if (alreadyAppliedCoupon) { toast({ position: 'top', - title: t('alert-message:coupon-already-applied'), + title: t('signup:alert-message.coupon-already-applied'), status: 'info', duration: 4000, isClosable: true, From 0dabc1917b401ef4454b0d6409892814fe9c6ec8 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 25 Sep 2024 16:59:26 -0400 Subject: [PATCH 49/67] Small change --- src/common/hooks/usePersistent.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/hooks/usePersistent.js b/src/common/hooks/usePersistent.js index bb590925e..1c8298385 100644 --- a/src/common/hooks/usePersistent.js +++ b/src/common/hooks/usePersistent.js @@ -22,6 +22,8 @@ const usePersistent = (key, initialValue) => { const usePersistentBySession = (key, initialValue) => { const getStoredValues = useMemo(() => { + if (typeof window === 'undefined') return initialValue; + const item = isWindow ? window.sessionStorage.getItem(key) : null; if (item === null) return initialValue; @@ -34,14 +36,12 @@ const usePersistentBySession = (key, initialValue) => { const [storedValue, setStoredValue] = useState(getStoredValues); + console.log(storedValue); + const setValue = (value) => { - try { - setStoredValue(value); - const valueToStore = typeof value === 'object' ? JSON.stringify(value) : value; - window.sessionStorage.setItem(key, valueToStore); - } catch (error) { - console.error('usePersistent_error:', error); - } + setStoredValue(value); + const valueToStore = typeof value === 'object' ? JSON.stringify(value) : value; + window.sessionStorage.setItem(key, valueToStore); }; return [storedValue, setValue]; }; From 3893d7bac66431efab9a7846b0ea4f6fce3289a3 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 25 Sep 2024 16:59:52 -0400 Subject: [PATCH 50/67] Small change --- src/common/hooks/usePersistent.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common/hooks/usePersistent.js b/src/common/hooks/usePersistent.js index 1c8298385..2a8964b8b 100644 --- a/src/common/hooks/usePersistent.js +++ b/src/common/hooks/usePersistent.js @@ -36,8 +36,6 @@ const usePersistentBySession = (key, initialValue) => { const [storedValue, setStoredValue] = useState(getStoredValues); - console.log(storedValue); - const setValue = (value) => { setStoredValue(value); const valueToStore = typeof value === 'object' ? JSON.stringify(value) : value; From 7be316987097dda2ce723d280521e69a181ec823 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 25 Sep 2024 18:51:18 -0400 Subject: [PATCH 51/67] :recycle: small refactor --- src/common/components/MarkDownParser/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/components/MarkDownParser/index.jsx b/src/common/components/MarkDownParser/index.jsx index 978106962..bd7cf6787 100644 --- a/src/common/components/MarkDownParser/index.jsx +++ b/src/common/components/MarkDownParser/index.jsx @@ -263,10 +263,10 @@ function MarkDownParser({ //This regex is to remove the runable empty codeblocks const emptyCodeRegex = /```([a-zA-Z]+).*runable=("true"|true|'true').*(\n{1,}|\s{1,}\n{1,})?```/gm; //This regex is to wrap all the runable codeblocks inside of a tag - const codeViewerRegex = /(```(?\w+).*runable=("true"|'true'|true).*(?(?:.|\n)*?)```\n?)+/gm; - // const codeViewerRegex = /(```(?[\w.]+).*runable=("true"|'true'|true).*(?(?:.|\n)*?)```\n?)+/gm; + const codeViewerRegex = /(```(?[\w.]+).*runable=("true"|'true'|true).*(?(?:.|\n)*?)```\n?)+/gm; const removedEmptyCodeViewers = content?.length > 0 ? content.replace(emptyCodeRegex, () => '') : ''; + const contentReplace = removedEmptyCodeViewers.replace(codeViewerRegex, (match) => `
\n${match}
\n`); const contextPathRegex = /```([a-zA-Z]+).*(path=[^\s]*).*([\s\S]+?)```/g; From d553ad632406dd1557a4fa08b6c972f8de82c783 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Wed, 25 Sep 2024 18:56:36 -0400 Subject: [PATCH 52/67] small change to usePersistent --- src/common/hooks/usePersistent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/hooks/usePersistent.js b/src/common/hooks/usePersistent.js index 2a8964b8b..efdfa07b9 100644 --- a/src/common/hooks/usePersistent.js +++ b/src/common/hooks/usePersistent.js @@ -41,7 +41,7 @@ const usePersistentBySession = (key, initialValue) => { const valueToStore = typeof value === 'object' ? JSON.stringify(value) : value; window.sessionStorage.setItem(key, valueToStore); }; - return [storedValue, setValue]; + return [storedValue !== undefined ? storedValue : initialValue, setValue]; }; export { From da434fc37241dc8865a9b7c9eda6e194b3b15aba Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Thu, 26 Sep 2024 12:09:45 -0400 Subject: [PATCH 53/67] :bug: fix related to consumables --- src/pages/mentorship/schedule.jsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index ce5166483..3d18652a4 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -120,6 +120,21 @@ function MentorshipSchedule() { return []; }; + const sortByConsumptionAvailability = (allConsumables) => allConsumables.sort((a, b) => { + const balanceA = a?.balance?.unit; + const balanceB = b?.balance?.unit; + + if (balanceA === -1 && balanceB !== -1) return -1; + if (balanceA !== -1 && balanceB === -1) return 1; + + if (balanceA > 0 && balanceB <= 0) return -1; + if (balanceA <= 0 && balanceB > 0) return 1; + + if (balanceA > 0 && balanceB > 0) return balanceB - balanceA; + + return 0; + }); + const getMentorsAndConsumables = async () => { const mentors = await getAllMentorsAvailable(); const reqConsumables = await bc.payment().service().consumable() @@ -131,8 +146,8 @@ function MentorshipSchedule() { })))); const allConsumables = await Promise.all(reqConsumables); - - setConsumables(allConsumables); + const sortedConsumables = sortByConsumptionAvailability(allConsumables); + setConsumables(sortedConsumables); setAllMentorsAvailable(mentors); }; From 9e35948d3f7edde048c3e82d6315f8faa6e1bfa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20M=C3=A1rquez?= <47191295+gustavomm19@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:49:40 +0000 Subject: [PATCH 54/67] apply asset agent on modal to clone proyect --- public/locales/en/common.json | 11 ++++++ public/locales/es/common.json | 11 ++++++ src/js_modules/projects/TabletWithForm.jsx | 31 +-------------- .../syllabus/ModalToCloneProject.jsx | 38 ++++++++++++++----- styles/markdown.css | 7 ++++ 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 0e53eed7d..29dc0ab3a 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -155,6 +155,17 @@ "need-help": "Need help? Schedule a mentoring session", "description": "This exercise can be downloaded and run locally on your computer by following these steps:", "select-os": "1. Select your operating system", + "agent-vs-code": { + "label": "Open the project in VS Code", + "description": "Make sure you have the LearnPack extension installed, open the folder in VSCode and type learnpack start on your vscode terminal.", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo" + }, + "agent-os": { + "label": "Open the project without VSCode", + "description": "Use your computer terminal to get inside your recently created folder and start learnpack:", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "code": "$ cd {{repoName}}\n$ learnpack start\n" + }, "os-list": [ { "name": "macos", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index f253beda2..3c0fe54ae 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -154,6 +154,17 @@ "need-help": "¿Necesitas ayuda? Programa una sesión de tutoría", "description": "Este ejercicio se puede descargar y ejecutar localmente en su computadora siguiendo estos pasos:", "select-os": "1. Seleccione su sistema operativo", + "agent-vs-code": { + "label": "Abrir el proyecto con VSCode", + "description": "Asegúrate de tener la extensión de LearnPack instalada, abre la carpeta en VSCode y escribr learnpack start en la terminal de vscode.", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo" + }, + "agent-os": { + "label": "Abrir el proyecto sin VSCode", + "description": "Usa la terminal de la computadora para entrar en la carpeta creada e inicia Learnpack:", + "video": "https://www.youtube.com/watch?v=tyb4WuO5Pdo", + "code": "$ cd {{repoName}}\n$ learnpack start\n" + }, "os-list": [ { "name": "macos", diff --git a/src/js_modules/projects/TabletWithForm.jsx b/src/js_modules/projects/TabletWithForm.jsx index f684bf140..0c1448545 100644 --- a/src/js_modules/projects/TabletWithForm.jsx +++ b/src/js_modules/projects/TabletWithForm.jsx @@ -15,12 +15,12 @@ import Link from '../../common/components/NextChakraLink'; import Text from '../../common/components/Text'; import Icon from '../../common/components/Icon'; import SimpleTable from './SimpleTable'; +import ModalToCloneProject from '../syllabus/ModalToCloneProject'; import ShowOnSignUp from '../../common/components/ShowOnSignup'; import useStyle from '../../common/hooks/useStyle'; import { ORIGIN_HOST } from '../../utils/variables'; import { reportDatalayer } from '../../utils/requests'; import ReactPlayerV2 from '../../common/components/ReactPlayerV2'; -import MarkDownParser from '../../common/components/MarkDownParser'; import SimpleModal from '../../common/components/SimpleModal'; const TabletWithForm = React.forwardRef(({ @@ -63,9 +63,6 @@ const TabletWithForm = React.forwardRef(({ }); }; - const urlToClone = asset?.url || asset?.readme_url.split('/blob')?.[0]; - const repoName = urlToClone.split('/').pop(); - return ( <> {showSimpleTable && ( @@ -335,31 +332,7 @@ const TabletWithForm = React.forwardRef(({ - { - setShowCloneModal(false); - }} - headerStyles={{ - textAlign: 'center', - textTransform: 'uppercase', - }} - bodyStyles={{ - className: 'markdown-body', - padding: { base: '10px 30px' }, - }} - > - - + { setSelectedOs(null); @@ -89,7 +99,7 @@ function ModalToCloneProject({ isOpen, onClose }) { {t('common:go-back')} setExpanded(val)} allowToggle display="flex" flexDirection="column" gap="10px"> - {selectedOs.steps.map((step, i) => ( + {steps.map((step, i) => ( @@ -103,17 +113,25 @@ function ModalToCloneProject({ isOpen, onClose }) { - - {step.description} - + + {step.code && ( + +
+                                
+                                  {step.code}
+                                
+                              
+
+ )} {step.source && ( - - {t('common:learn-more')} - + + {t('common:learn-more')} + )}
))} +
)} @@ -133,7 +151,7 @@ function ModalToCloneProject({ isOpen, onClose }) { className="react-player-border-radius" containerStyle={{ height: '100%' }} iframeStyle={{ background: 'none', borderRadius: '11px', height: '100% !important' }} - url={selectedOs.steps[expanded]?.video} + url={steps && steps[expanded]?.video} height="100%" /> @@ -148,11 +166,13 @@ function ModalToCloneProject({ isOpen, onClose }) { ModalToCloneProject.propTypes = { isOpen: PropTypes.bool, onClose: PropTypes.func, + currentAsset: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.any])), }; ModalToCloneProject.defaultProps = { isOpen: false, onClose: () => {}, + currentAsset: null, }; export default ModalToCloneProject; diff --git a/styles/markdown.css b/styles/markdown.css index cffa18998..e31eaba6a 100644 --- a/styles/markdown.css +++ b/styles/markdown.css @@ -488,6 +488,13 @@ blockquote > :last-child { padding: 0 0 0 16px; } +.highlight-command { + background: #2d2d2d; + padding: 2px 5px; + border-radius: 5px; + color: #ccc; +} + /* .highlight { font-size: .85em; line-height: 2.0; From 7af120526685da935614511069302adf60d1d229 Mon Sep 17 00:00:00 2001 From: Jenniffer Guzman Date: Sun, 29 Sep 2024 20:44:30 -0500 Subject: [PATCH 55/67] Fixed icon size --- src/common/components/ProgramCard.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/common/components/ProgramCard.jsx b/src/common/components/ProgramCard.jsx index 80595931e..c8c20fe2b 100644 --- a/src/common/components/ProgramCard.jsx +++ b/src/common/components/ProgramCard.jsx @@ -160,12 +160,14 @@ function ProgramCard({
) : ( - - + + + + )} From 5fa2a63c7c0a9e94825d80e3d94c82030d83db4e Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 30 Sep 2024 12:45:27 -0400 Subject: [PATCH 56/67] :bug: small bug fix related to the icon --- src/common/components/MktSideRecommendations.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/common/components/MktSideRecommendations.jsx b/src/common/components/MktSideRecommendations.jsx index 6b2d075e5..0fd811a8d 100644 --- a/src/common/components/MktSideRecommendations.jsx +++ b/src/common/components/MktSideRecommendations.jsx @@ -128,6 +128,7 @@ function MktSideRecommendations({ title, endpoint, technologies, containerPaddin const getMainTechIcon = () => { const techWithURL = technologies.find((tech) => tech.icon_url !== null); + if (!techWithURL) return undefined; return techWithURL.icon_url; }; @@ -152,8 +153,9 @@ function MktSideRecommendations({ title, endpoint, technologies, containerPaddin return ( <> - - + {(recom.icon_url || getMainTechIcon()) + && } + {recom?.course_translation?.title || recom.title} @@ -210,9 +212,9 @@ function MktSideRecommendations({ title, endpoint, technologies, containerPaddin - - - + {(recom.icon_url !== undefined || getMainTechIcon() !== undefined) + && } + {recom?.course_translation?.title || recom.title} From 7ab45f36f798d9e1bcfe26156a67e95d5a24c3bc Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 30 Sep 2024 15:23:15 -0400 Subject: [PATCH 57/67] small refactor --- src/common/components/MktSideRecommendations.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/components/MktSideRecommendations.jsx b/src/common/components/MktSideRecommendations.jsx index 0fd811a8d..7d8e772e1 100644 --- a/src/common/components/MktSideRecommendations.jsx +++ b/src/common/components/MktSideRecommendations.jsx @@ -212,7 +212,7 @@ function MktSideRecommendations({ title, endpoint, technologies, containerPaddin - {(recom.icon_url !== undefined || getMainTechIcon() !== undefined) + {(recom.icon_url || getMainTechIcon()) && } {recom?.course_translation?.title || recom.title} From 7db334146a77e2c8d23a0a3bc01c0afa73a865ee Mon Sep 17 00:00:00 2001 From: Jenniffer Guzman Date: Mon, 30 Sep 2024 14:58:53 -0500 Subject: [PATCH 58/67] ordered support material --- src/common/components/SupplementaryMaterial.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/common/components/SupplementaryMaterial.jsx b/src/common/components/SupplementaryMaterial.jsx index 7a08d9668..670f83123 100644 --- a/src/common/components/SupplementaryMaterial.jsx +++ b/src/common/components/SupplementaryMaterial.jsx @@ -24,6 +24,14 @@ function SupplementaryMaterial({ assets, ...rest }) { if (assets.length === 0) return null; + const order = ['LESSON', 'ARTICLE', 'EXERCISE', 'PROJECT']; + + const orderAssets = () => { + assets.sort((a, b) => order.indexOf(a.asset_type) - order.indexOf(b.asset_type)); + }; + + orderAssets(); + return ( Date: Mon, 30 Sep 2024 19:32:24 -0400 Subject: [PATCH 59/67] small refactor --- src/js_modules/checkout/PaymentInfo.jsx | 7 +++---- src/js_modules/checkout/Summary.jsx | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx index 53e2fc08d..a3f7063a6 100644 --- a/src/js_modules/checkout/PaymentInfo.jsx +++ b/src/js_modules/checkout/PaymentInfo.jsx @@ -166,10 +166,9 @@ function PaymentInfo() { }, [readyToRefetch, timeElapsed]); useEffect(() => { - if (isPaymentSuccess) { - setIsSubmittingPayment(true); - setReadyToRefetch(true); - } + if (!isPaymentSuccess) return; + setIsSubmittingPayment(true); + setReadyToRefetch(true); }, [isPaymentSuccess]); useEffect(() => { diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index 0c0aeb0d9..14c522a02 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -188,10 +188,9 @@ function Summary() { }, [readyToRefetch, timeElapsed]); useEffect(() => { - if (isPaymentSuccess) { - setIsSubmitting(true); - setReadyToRefetch(true); - } + if (isPaymentSuccess) return; + setIsSubmitting(true); + setReadyToRefetch(true); }, [isPaymentSuccess]); const handleSubmit = () => { From 144f39a92aa413d24b96fac88ba9d2ca59a6a354 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Mon, 30 Sep 2024 20:36:14 -0400 Subject: [PATCH 60/67] Update SupplementaryMaterial.jsx --- src/common/components/SupplementaryMaterial.jsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/common/components/SupplementaryMaterial.jsx b/src/common/components/SupplementaryMaterial.jsx index 670f83123..c392b4a02 100644 --- a/src/common/components/SupplementaryMaterial.jsx +++ b/src/common/components/SupplementaryMaterial.jsx @@ -25,12 +25,7 @@ function SupplementaryMaterial({ assets, ...rest }) { if (assets.length === 0) return null; const order = ['LESSON', 'ARTICLE', 'EXERCISE', 'PROJECT']; - - const orderAssets = () => { - assets.sort((a, b) => order.indexOf(a.asset_type) - order.indexOf(b.asset_type)); - }; - - orderAssets(); + assets.sort((a, b) => order.indexOf(a.asset_type) - order.indexOf(b.asset_type)); return ( Date: Mon, 30 Sep 2024 21:57:33 -0400 Subject: [PATCH 61/67] adding max-w and when searching for mentor it will be the only option --- .../SupportSidebar/MentoringConsumables.jsx | 5 +- src/pages/mentorship/schedule.jsx | 60 ++++++++++--------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/common/components/SupportSidebar/MentoringConsumables.jsx b/src/common/components/SupportSidebar/MentoringConsumables.jsx index a5775c9c0..b9c2997c4 100644 --- a/src/common/components/SupportSidebar/MentoringConsumables.jsx +++ b/src/common/components/SupportSidebar/MentoringConsumables.jsx @@ -220,7 +220,10 @@ function MentoringConsumables({ showErrorToast(); setNotifyError(false); } - if (mentorFound) servWithMentorsAvailable = getServicesWithMentor(mentorFound); + if (mentorFound) { + servWithMentorsAvailable = getServicesWithMentor(mentorFound); + setProgramMentors([mentorFound]); + } } setServicesWithMentorsAvailable(servWithMentorsAvailable); diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx index 3d18652a4..d2fc0a6d5 100644 --- a/src/pages/mentorship/schedule.jsx +++ b/src/pages/mentorship/schedule.jsx @@ -202,35 +202,37 @@ function MentorshipSchedule() { return !isLoading && user && !mentorshipServices.isLoading && ( useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> - - - - {`${t('consumables.back-to-dashboard')}`} - - - - useColorModeValue('white', '#27333f')}> - - + + + + + {`${t('consumables.back-to-dashboard')}`} + + + + useColorModeValue('white', '#27333f')}> + + + ); From 8d8f81c899b2fc8c440d5b030ce7477c52e725e6 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Mon, 30 Sep 2024 22:10:28 -0400 Subject: [PATCH 62/67] small change --- src/js_modules/checkout/Summary.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js_modules/checkout/Summary.jsx b/src/js_modules/checkout/Summary.jsx index 14c522a02..fca075967 100644 --- a/src/js_modules/checkout/Summary.jsx +++ b/src/js_modules/checkout/Summary.jsx @@ -188,7 +188,7 @@ function Summary() { }, [readyToRefetch, timeElapsed]); useEffect(() => { - if (isPaymentSuccess) return; + if (!isPaymentSuccess) return; setIsSubmitting(true); setReadyToRefetch(true); }, [isPaymentSuccess]); From 088238fbb00862c5df066fcc295ea24a7b289519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20M=C3=A1rquez?= <47191295+gustavomm19@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:47:50 +0000 Subject: [PATCH 63/67] remove unsued previous clone instructions --- public/locales/en/common.json | 1 - public/locales/es/common.json | 1 - src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx | 1 - 3 files changed, 3 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 29dc0ab3a..c54335276 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -149,7 +149,6 @@ "type": "dropdown" }, "clone-title": "How to clone a project?", - "cloneInstructions": "This exercise can be downloaded and run locally if you have node.js installed (installation steps).\n\n Once you have node.js, it's time to install learnpack and clone this project into your computer by typing the following command on your terminal:\n\n``` bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNote: This will create a new folder \"{{repoName}}\" in your computer.\n\nIf you want to use VSCode: Make sure you have the LearnPack extension installed, open the folder in VSCode and type `learnpack start` on your vscode terminal.\n\nTo run without VSCode: Use your computer terminal to get inside your recently created folder and start learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nRead the README.md file and follow the rest of the instructions.", "clone-modal": { "title": "Open locally on your computer", "need-help": "Need help? Schedule a mentoring session", diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 3c0fe54ae..660094623 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -148,7 +148,6 @@ "type": "dropdown" }, "clone-title": "¿Cómo clonar un proyecto?", - "cloneInstructions": "Este ejercicio se puede descargar y ejecutar localmente si tienes node.js instalado (pasos de instalación).\n\n Si ya tienes node, toca instalar learnpack y clonar el proyecto en tu computadora escribiendo el siguiente comando en tu terminal:\n\n```bash\n$ npm i @learnpack/learnpack -g\n$ git clone {{urlToClone}}\n```\nNota: Esto creará una nueva carpeta \"{{repoName}}\" en tu computadora con el código del proyecto dentro.\n\nSi quieres usar VSCode: asegúrate de tener el LearnPack extension instalado, abre la carpeta en VSCode y escribe `learnpack start` en tu terminal de vscode.\n\nPara realizar los ejercicios sin VSCode: abre tu terminal en la carpeta recién creada y comienza el programa learnpack:\n\n```bash\n$ cd {{repoName}}\n$ learnpack start\n```\nLee el archivo README.md y sigue el resto de las instrucciones.", "clone-modal": { "title": "Abrir localmente en tu computadora", "need-help": "¿Necesitas ayuda? Programa una sesión de tutoría", diff --git a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx index 1dad38769..5dadcc1ca 100644 --- a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx +++ b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx @@ -121,7 +121,6 @@ function SyllabusContent() { ? section.filteredModulesByPending : section.filteredModules)); - useEffect(() => console.log(currentAsset), [currentAsset]); const currentModuleIndex = filteredCurrentAssignments.findIndex((s) => s?.some((l) => l.slug === lessonSlug || l.translations?.[language]?.slug === lessonSlug || (currentAsset?.id && l.translations?.[language]?.slug === currentAsset.slug))); const currentModule = sortedAssignments[currentModuleIndex]; From 23ed75f49fc32e9fc7d9d8975d49bd99794c44e5 Mon Sep 17 00:00:00 2001 From: lumi-tip Date: Tue, 1 Oct 2024 10:50:01 -0400 Subject: [PATCH 64/67] changes in spacing --- .../components/SupportSidebar/MentoringConsumables.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/components/SupportSidebar/MentoringConsumables.jsx b/src/common/components/SupportSidebar/MentoringConsumables.jsx index b9c2997c4..86667d45b 100644 --- a/src/common/components/SupportSidebar/MentoringConsumables.jsx +++ b/src/common/components/SupportSidebar/MentoringConsumables.jsx @@ -398,7 +398,7 @@ function MentoringConsumables({ && ( <> {mentoryProps?.service && ( - + @@ -410,7 +410,7 @@ function MentoringConsumables({ {!mentoryProps?.service && programServices.length > 0 && ( <> - + setSearchProps({ ...searchProps, serviceSearch: e.target.value?.toLocaleLowerCase() })} background={commonBackground} borderBottomRadius="0" border="0" placeholder={t('supportSideBar.select-type')} /> @@ -432,7 +432,7 @@ function MentoringConsumables({ {mentoryProps?.service && !mentoryProps?.mentor && ( <> - + setSearchProps({ ...searchProps, mentorSearch: e.target.value?.toLowerCase() })} background={commonBackground} borderBottomRadius="0" border="0" placeholder={t('supportSideBar.search-mentor')} /> From 8d14123d72578831c187fd9cf090ea0bb1d1f842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20M=C3=A1rquez?= <47191295+gustavomm19@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:59:24 +0000 Subject: [PATCH 65/67] fix modal width --- src/js_modules/syllabus/ModalToCloneProject.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js_modules/syllabus/ModalToCloneProject.jsx b/src/js_modules/syllabus/ModalToCloneProject.jsx index 1c280b7c8..9f686ab02 100644 --- a/src/js_modules/syllabus/ModalToCloneProject.jsx +++ b/src/js_modules/syllabus/ModalToCloneProject.jsx @@ -49,7 +49,7 @@ function ModalToCloneProject({ isOpen, onClose, currentAsset }) { }; return ( - + From 9cadff8cae0fdee8fa6855f2a38d12133e9429a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20M=C3=A1rquez?= <47191295+gustavomm19@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:08:59 +0000 Subject: [PATCH 66/67] remove 80% height --- src/js_modules/syllabus/ModalToCloneProject.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js_modules/syllabus/ModalToCloneProject.jsx b/src/js_modules/syllabus/ModalToCloneProject.jsx index 9f686ab02..9e0d50350 100644 --- a/src/js_modules/syllabus/ModalToCloneProject.jsx +++ b/src/js_modules/syllabus/ModalToCloneProject.jsx @@ -51,7 +51,7 @@ function ModalToCloneProject({ isOpen, onClose, currentAsset }) { return ( - + From c28d4ab964c734c705ddbf7779ca9569732b3771 Mon Sep 17 00:00:00 2001 From: Tomas Gonzalez <56565994+tommygonzaleza@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:30:48 -0400 Subject: [PATCH 67/67] Update PricingCard.jsx --- src/common/components/PricingCard.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/components/PricingCard.jsx b/src/common/components/PricingCard.jsx index 2be53c333..437f06df1 100644 --- a/src/common/components/PricingCard.jsx +++ b/src/common/components/PricingCard.jsx @@ -105,7 +105,6 @@ export default function PricingCard({ item, courseData, isFetching, relatedSubsc const alreadyHaveIt = relatedSubscription?.plans?.[0]?.slug === item?.plan_slug; const handlePlan = () => { - console.log('EJECUTE'); const langPath = lang === 'en' ? '' : `/${lang}`; const qs = parseQuerys({ plan: selectedFinancing?.plan_slug || item?.plan_slug,