diff --git a/i18n.js b/i18n.js index c81e33453..bb5fb1bab 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', 'signup', 'common'], '/how-to': ['how-to'], '/pricing': ['pricing', 'signup'], '/how-to/[slug]': ['how-to'], diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 89b5547b3..4cd880cdd 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,106 @@ "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", + "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", + "label": "macOS", + "logo": "/static/images/apple.png", + "steps": [ + { + "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": "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" + }, + { + "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": "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": "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" + }, + { + "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": "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": "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" + }, + { + "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", @@ -253,15 +346,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": [ { @@ -270,7 +365,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": { @@ -278,7 +377,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": { @@ -286,7 +389,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": { @@ -294,7 +401,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": { @@ -302,7 +413,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": { @@ -310,7 +425,11 @@ "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." + } + ] } ] }, diff --git a/public/locales/en/dashboard.json b/public/locales/en/dashboard.json index 11643ac06..a5357150a 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!", @@ -15,9 +14,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", @@ -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": [ @@ -108,7 +106,15 @@ "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": "mentoring sessions available", + "action": "Schedule session", + "no-available": "No services available" + }, + "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 +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 diff --git a/public/locales/en/signup.json b/public/locales/en/signup.json index b5b074e0b..2b71525b5 100644 --- a/public/locales/en/signup.json +++ b/public/locales/en/signup.json @@ -159,7 +159,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/common.json b/public/locales/es/common.json index 25615d4c6..643f17fa7 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -148,7 +148,100 @@ "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", + "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", + "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/locales/es/dashboard.json b/public/locales/es/dashboard.json index f5ccaafca..e7897dc8f 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 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.", @@ -16,9 +15,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": { @@ -73,17 +72,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,11 +103,19 @@ ] }, "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" + }, + "schedule-steps": { + "select-mentorship": "Primero selecciona un tipo de mentoría", + "select-mentor": "Ahora busca un mentor", + "schedule": "Agenda la sesión" }, "deliverProject": { "title": "Entregar tarea", diff --git a/public/locales/es/signup.json b/public/locales/es/signup.json index d60bee094..f0a9bc37c 100644 --- a/public/locales/es/signup.json +++ b/public/locales/es/signup.json @@ -159,7 +159,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/public/static/images/apple.png b/public/static/images/apple.png new file mode 100644 index 000000000..165f27ad0 Binary files /dev/null and b/public/static/images/apple.png differ diff --git a/public/static/images/linux.png b/public/static/images/linux.png new file mode 100644 index 000000000..f730b4a6f Binary files /dev/null and b/public/static/images/linux.png differ diff --git a/public/static/images/windows.png b/public/static/images/windows.png new file mode 100644 index 000000000..8883c0b3c Binary files /dev/null and b/public/static/images/windows.png differ 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/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/MarkDownParser/index.jsx b/src/common/components/MarkDownParser/index.jsx index b32cb24a1..7a1039059 100644 --- a/src/common/components/MarkDownParser/index.jsx +++ b/src/common/components/MarkDownParser/index.jsx @@ -8,25 +8,21 @@ 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'; // import { useRouter } from 'next/router'; import { Wrapper, BeforeAfter, Code, MDCheckbox, MDHeading, MDHr, MDLink, MDText, OnlyForBanner, Quote, } from './MDComponents'; import { usePersistent } from '../../hooks/usePersistent'; -import useCohortHandler from '../../hooks/useCohortHandler'; import useModuleHandler from '../../hooks/useModuleHandler'; import Toc from './toc'; import ContentHeading from './ContentHeading'; -import CallToAction from '../CallToAction'; import CodeViewer, { languagesLabels, languagesNames } from '../CodeViewer'; import SubTasks from './SubTasks'; import DynamicCallToAction from '../DynamicCallToAction'; -import SimpleModal from '../SimpleModal'; -import modifyEnv from '../../../../modifyEnv'; function MarkdownH2Heading({ children }) { return ( @@ -139,20 +135,14 @@ function ListComponent({ subTasksLoaded, newSubTasks, setNewSubTasks, subTasks, } function MarkDownParser({ - content, callToActionProps, withToc, frontMatter, titleRightSide, currentTask, isPublic, currentData, + content, withToc, frontMatter, titleRightSide, currentTask, isPublic, currentData, showLineNumbers, showInlineLineNumbers, assetData, alerMessage, isGuidedExperience, showContentHeading, }) { - const { t, lang } = useTranslation('common'); const [subTasksLoaded, setSubTasksLoaded] = useState(false); const [newSubTasks, setNewSubTasks] = useState([]); - const [learnpackActions, setLearnpackActions] = useState([]); const [fileContext, setFileContext] = useState(''); const { subTasks, setSubTasks } = useModuleHandler(); - const { state } = useCohortHandler(); - const { cohortSession } = state; const [profile] = usePersistent('profile', {}); - const [showCloneModal, setShowCloneModal] = useState(false); - const BREATHECODE_HOST = modifyEnv({ queryString: 'host', env: process.env.BREATHECODE_HOST }); const updateSubTask = async (taskProps) => { const cleanedSubTasks = subTasks.filter((task) => task.id !== taskProps.id); @@ -206,21 +196,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,34 +215,15 @@ 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 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`); const contextPathRegex = /```([a-zA-Z]+).*(path=[^\s]*).*([\s\S]+?)```/g; @@ -282,53 +240,14 @@ 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 && ( + callToAction={currentData?.interactive && ( + )} content={frontMatter} currentData={currentData} @@ -387,7 +306,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 +321,6 @@ MarkDownParser.propTypes = { }; MarkDownParser.defaultProps = { content: '', - callToActionProps: {}, withToc: false, frontMatter: {}, titleRightSide: null, diff --git a/src/common/components/MktShowPrices.jsx b/src/common/components/MktShowPrices.jsx index a880f3b49..400b6e044 100644 --- a/src/common/components/MktShowPrices.jsx +++ b/src/common/components/MktShowPrices.jsx @@ -161,13 +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, }); diff --git a/src/common/components/MktSideRecommendations.jsx b/src/common/components/MktSideRecommendations.jsx index 6b2d075e5..7d8e772e1 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 || getMainTechIcon()) + && } + {recom?.course_translation?.title || recom.title} diff --git a/src/common/components/PricingCard.jsx b/src/common/components/PricingCard.jsx index d95a09bb2..437f06df1 100644 --- a/src/common/components/PricingCard.jsx +++ b/src/common/components/PricingCard.jsx @@ -109,8 +109,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/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({ ) : ( - - + + + + )} diff --git a/src/common/components/SupplementaryMaterial.jsx b/src/common/components/SupplementaryMaterial.jsx index 7a08d9668..c392b4a02 100644 --- a/src/common/components/SupplementaryMaterial.jsx +++ b/src/common/components/SupplementaryMaterial.jsx @@ -24,6 +24,9 @@ function SupplementaryMaterial({ assets, ...rest }) { if (assets.length === 0) return null; + const order = ['LESSON', 'ARTICLE', 'EXERCISE', 'PROJECT']; + assets.sort((a, b) => order.indexOf(a.asset_type) - order.indexOf(b.asset_type)); + return ( {!mentoryProps?.service && (consumables?.mentorship_service_sets?.length !== 0 || currentBalance !== 0) && ( <> - + {t('supportSideBar.mentoring')}
@@ -373,7 +376,7 @@ function MentoringConsumables({ )} {open && mentoryProps?.service && !mentoryProps?.mentor && existConsumablesOnCurrentService && ( - + {t('mentorship.you-have')} @@ -395,7 +398,7 @@ function MentoringConsumables({ && ( <> {mentoryProps?.service && ( - 0 ? '22px' : '34px'} px="20px" py="15px" textAlign="center" w="100%" borderTopRadius="0.375rem"> + @@ -407,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')} /> @@ -429,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')} /> @@ -522,9 +525,9 @@ MentoringConsumables.propTypes = { MentoringConsumables.defaultProps = { queryService: undefined, queryMentor: undefined, + titleSize: undefined, mentoryProps: [], width: '100%', - titleSize: '14px', consumables: {}, programServices: [], setProgramMentors: () => { }, diff --git a/src/common/hooks/usePersistent.js b/src/common/hooks/usePersistent.js index 43950060c..efdfa07b9 100644 --- a/src/common/hooks/usePersistent.js +++ b/src/common/hooks/usePersistent.js @@ -22,23 +22,26 @@ 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; - 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); const setValue = (value) => { - try { - setStoredValue(value); - window.sessionStorage.setItem(key, JSON.stringify(value)); - } 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]; + return [storedValue !== undefined ? storedValue : initialValue, setValue]; }; export { diff --git a/src/js_modules/checkout/PaymentInfo.jsx b/src/js_modules/checkout/PaymentInfo.jsx index 9af2ec223..a3f7063a6 100644 --- a/src/js_modules/checkout/PaymentInfo.jsx +++ b/src/js_modules/checkout/PaymentInfo.jsx @@ -43,27 +43,34 @@ 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'); const redirectedFrom = getStorageItem('redirected-from'); const router = useRouter(); + const { backgroundColor, fontColor, hexColor } = useStyle(); const isPaymentSuccess = paymentStatus === 'success'; const isPaymentIdle = paymentStatus === 'idle'; const paymentStatusBgColor = isPaymentSuccess ? 'green.light' : '#ffefef'; - const redirectTocohort = (cohort) => { + const redirectTocohort = () => { + if (!isPaymentSuccess) { + setPaymentStatus('idle'); + return; + } 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}`; + 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({ - ...cohort, + ...cohortFound, selectedProgramSlug: cohortDashboardLink, }); router.push(cohortDashboardLink); }; + const joinCohort = (cohort) => { reportDatalayer({ dataLayer: { @@ -85,10 +92,11 @@ function PaymentInfo() { setReadyToRefetch(false); } if (dataRequested?.status === 'ACTIVE') { - redirectTocohort(cohort); + setCohortFound(cohort); } }) - .catch(() => { + .catch((error) => { + console.error('Error al unirse a la cohorte:', error); setIsSubmittingPayment(false); setTimeout(() => { setReadyToRefetch(false); @@ -96,8 +104,6 @@ function PaymentInfo() { }); }; - const { backgroundColor, fontColor, hexColor } = useStyle(); - useEffect(() => { reportDatalayer({ dataLayer: { @@ -109,7 +115,7 @@ function PaymentInfo() { useEffect(() => { let interval; - if (readyToRefetch && timeElapsed < 10) { + if (readyToRefetch && timeElapsed < 10 && isPaymentSuccess) { interval = setInterval(() => { getAllMySubscriptions() .then((subscriptions) => { @@ -159,11 +165,17 @@ function PaymentInfo() { return () => clearInterval(interval); }, [readyToRefetch, timeElapsed]); + useEffect(() => { + if (!isPaymentSuccess) return; + setIsSubmittingPayment(true); + setReadyToRefetch(true); + }, [isPaymentSuccess]); + useEffect(() => { 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); @@ -366,15 +378,9 @@ function PaymentInfo() { height="45px" variant="default" // mt="12px" + isDisabled={isPaymentSuccess && !cohortFound} 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..504f6e0e9 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,19 @@ function Summary() { }); }, []); - const redirectTocohort = (cohort) => { + const redirectTocohort = () => { const langLink = lang !== 'en' ? `/${lang}` : ''; - const syllabusVersion = cohort?.syllabus_version; + const syllabusVersion = cohortFound?.syllabus_version; - axiosInstance.defaults.headers.common.Academy = cohort.academy.id; - const cohortDashboardLink = `${langLink}/cohort/${cohort?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; + axiosInstance.defaults.headers.common.Academy = cohortFound.academy.id; + const cohortDashboardLink = `${langLink}/cohort/${cohortFound?.slug}/${syllabusVersion?.slug}/v${syllabusVersion?.version}`; setCohortSession({ - ...cohort, + ...cohortFound, selectedProgramSlug: cohortDashboardLink, }); router.push(cohortDashboardLink); }; + const joinCohort = (cohort) => { reportDatalayer({ dataLayer: { @@ -122,10 +124,11 @@ function Summary() { setReadyToRefetch(false); } if (dataRequested?.id) { - redirectTocohort(cohort); + setCohortFound(cohort); } }) - .catch(() => { + .catch((error) => { + console.log(error); setTimeout(() => { setReadyToRefetch(false); }, 600); @@ -184,6 +187,12 @@ function Summary() { return () => clearInterval(interval); }, [readyToRefetch, timeElapsed]); + useEffect(() => { + if (!isPaymentSuccess) return; + setIsSubmitting(true); + setReadyToRefetch(true); + }, [isPaymentSuccess]); + const handleSubmit = () => { if (!isPaymentIdle || isSubmitting || !selectedPlanCheckoutData?.plan_id) return; setIsSubmitting(true); @@ -194,8 +203,6 @@ function Summary() { query: { ...router.query, plan_id: selectedPlanCheckoutData?.plan_id, - price: selectedPlanCheckoutData?.price, - period: selectedPlanCheckoutData?.period, }, }); } else { @@ -242,11 +249,11 @@ function Summary() { } } if (respPayment.status === 'FULFILLED') { + setPaymentStatus('success'); setSelectedPlanCheckoutData({ ...selectedPlanCheckoutData, payment_success: true, }); - setPaymentStatus('success'); } }) .catch(() => { @@ -261,6 +268,7 @@ function Summary() { }); } }; + useEffect(() => { setHasMounted(true); }, []); @@ -485,15 +493,9 @@ function Summary() { height="45px" variant="default" // mt="12px" + isDisabled={isPaymentSuccess && !cohortFound} isLoading={isSubmitting} - onClick={() => { - if (isPaymentSuccess) { - setIsSubmitting(true); - setReadyToRefetch(true); - } else { - setPaymentStatus('idle'); - } - }} + onClick={redirectTocohort} > {isPaymentSuccess ? t('start-free-course') : t('try-again')} 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); + 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"> + {steps.map((step, i) => ( + + + + + + {`${i + 2}.`} + {' '} + {step.label} + + + + + + + {step.code && ( + +
+                                
+                                  {step.code}
+                                
+                              
+
+ )} + {step.source && ( + + {t('common:learn-more')} + + )} +
+
+ ))} + +
+
+ )} +
+ {cohortSession?.available_as_saas && ( + + {t('common:learnpack.clone-modal.need-help')} + {' '} + → + + )} +
+ + {selectedOs && ( + <> + + + )} + +
+
+
); } 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 ( 5; const { coupon: couponQuery } = query; + const { course } = router.query; + const courseChoosed = course; + const [coupon] = usePersistentBySession('coupon', ''); const couponValue = useMemo(() => { @@ -121,9 +124,6 @@ function Checkout() { return couponString || formatedCouponQuery; }, [coupon, couponQuery]); - const { course } = router.query; - const courseChoosed = course; - const queryPlanExists = planFormated !== undefined && planFormated?.length > 0; const queryMentorshipServiceSlugExists = mentorshipServiceSetSlug && mentorshipServiceSetSlug?.length > 0; const queryEventTypeSetSlugExists = eventTypeSetSlug && eventTypeSetSlug?.length > 0; @@ -161,6 +161,22 @@ function Checkout() { }; const handleCoupon = (coupons, actions) => { + const alreadyAppliedCoupon = selfAppliedCoupon?.slug === discountCode || selfAppliedCoupon?.slug === couponValue; + + if (alreadyAppliedCoupon) { + toast({ + position: 'top', + title: t('signup:alert-message.coupon-already-applied'), + status: 'info', + duration: 4000, + isClosable: true, + }); + if (actions) { + actions.setSubmitting(false); + } + return; + } + bc.payment({ coupons: [coupons || discountCode], plan: planFormated, diff --git a/src/pages/mentorship/schedule.jsx b/src/pages/mentorship/schedule.jsx new file mode 100644 index 000000000..d2fc0a6d5 --- /dev/null +++ b/src/pages/mentorship/schedule.jsx @@ -0,0 +1,241 @@ +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 { reportDatalayer } from '../../utils/requests'; +import useSubscriptionsHandler from '../../common/store/actions/subscriptionAction'; +import useAuth from '../../common/hooks/useAuth'; +import Icon from '../../common/components/Icon'; +import Link from '../../common/components/NextChakraLink'; +import bc from '../../common/services/breathecode'; +import MentoringConsumables from '../../common/components/SupportSidebar/MentoringConsumables'; + +function MentorshipSchedule() { + let isTabletOrPhone = false; + if (typeof window !== 'undefined') { + isTabletOrPhone = window.innerWidth < 780; + } + const router = useRouter(); + const { t } = useTranslation('signup'); + const { fetchSubscriptions } = useSubscriptionsHandler(); + const { service, mentor } = router.query; + const { isLoading, user, isAuthenticated } = useAuth(); + const [mentorshipServices, setMentorshipServices] = useState({ isLoading: true, data: [] }); + const [searchProps, setSearchProps] = useState({ serviceSearch: '', mentorSearch: '' }); + const [mentoryProps, setMentoryProps] = useState({}); + const [admissions, setAdmissions] = useState({}); + const [consumables, setConsumables] = useState([]); + const [allMentorsAvailable, setAllMentorsAvailable] = useState([]); + const [mentorsByService, setMentorsByService] = useState([]); + const [subscriptionData, setSubscriptionData] = useState([]); + + const getServices = async (userRoles) => { + if (userRoles?.length > 0) { + 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) { + 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; + 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); + + const academies = mentorshipServices.data.reduce((acc, { academy, ...restOfService }) => { + if (!acc[academy.id]) { + acc[academy.id] = { services: [] }; + } + acc[academy.id].services.push(restOfService); + return acc; + }, {}); + + const academyData = Object.entries(academies).map(([id, { services }]) => ({ + id: Number(id), + services, + })); + + const getMentorsForAcademy = async (academy) => { + const res = await bc.mentorship({ + services: academy.services.map((s) => s.slug).join(','), + status: 'ACTIVE', + syllabus: allSyllabus?.join(',') || undefined, + academy: academy.id, + }).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; + } + + 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() + .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); + const sortedConsumables = sortByConsumptionAvailability(allConsumables); + setConsumables(sortedConsumables); + setAllMentorsAvailable(mentors); + }; + + useEffect(() => { + if (!mentorshipServices.isLoading && mentorshipServices?.data.length > 0) { + getMentorsAndConsumables(); + } + }, [mentorshipServices]); + + 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 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), + ); + } + + return []; + }; + const suscriptionServicesFiltered = filterServices(); + + 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]); + + return !isLoading && user && !mentorshipServices.isLoading && ( + useColorModeValue('#f9f9f9', '#171f2a')} overflow="hidden"> + + + + + {`${t('consumables.back-to-dashboard')}`} + + + + useColorModeValue('white', '#27333f')}> + + + + + + ); +} + +export default MentorshipSchedule; diff --git a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx index f383a3621..e731e0919 100644 --- a/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx +++ b/src/pages/syllabus/[cohortSlug]/[lesson]/[lessonSlug]/index.jsx @@ -77,7 +77,6 @@ function SyllabusContent() { const [showSolutionVideo, setShowSolutionVideo] = useState(false); const [selectedSyllabus, setSelectedSyllabus] = useState({}); const [defaultSelectedSyllabus, setDefaultSelectedSyllabus] = useState({}); - const [callToActionProps, setCallToActionProps] = useState({}); const [showModal, setShowModal] = useState(false); const [readmeUrlPathname, setReadmeUrlPathname] = useState(null); const [openTargetBlankModal, setOpenTargetBlankModal] = useState(null); @@ -122,7 +121,7 @@ function SyllabusContent() { ? section.filteredModulesByPending : section.filteredModules)); - const currentModuleIndex = filteredCurrentAssignments.findIndex((s) => s?.some((l) => l.slug === lessonSlug || l.translations?.[language]?.slug === lessonSlug || l.translations?.[language]?.slug === currentAsset?.slug)); + 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]; @@ -229,7 +228,6 @@ function SyllabusContent() { setShowModal(false); setCurrentAsset(null); setCurrentSelectedModule(null); - setCallToActionProps({}); setReadme(null); setIpynbHtmlUrl(null); setCurrentBlankProps(null); @@ -272,13 +270,6 @@ function SyllabusContent() { // const translatedExtension = language === 'us' ? '' : `.${language}`; const finalPathname = `${pathnameWithoutExtension}.${extension}`; - setCallToActionProps({ - token: accessToken, - assetSlug: lessonSlug, - interactive: data.interactive, - gitpod: data.gitpod, - assetType: assetTypeValues[lesson], - }); setReadmeUrlPathname(finalPathname); let currentTranslationSlug = data?.lang === language ? data?.slug : data.translations[language]; if (isIpynb) { @@ -854,7 +845,6 @@ function SyllabusContent() { ipynbHtmlUrl={ipynbHtmlUrl} readme={readme} currentBlankProps={currentBlankProps} - callToActionProps={callToActionProps} currentData={currentAsset} lesson={lesson} quizSlug={quizSlug} 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;