diff --git a/astro.config.mjs b/astro.config.mjs index d7ddcba..0a60abc 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -8,6 +8,9 @@ export default defineConfig({ compressHTML: true, site: process.env.ASTRO_SITE, trailingSlash: 'always', + image: { + domains: ['assets.codyops.com','assets.codyops.com.br','assets.codyops.com.es'], + }, integrations: [ markdownIntegration({ remarkPlugins: [], diff --git a/src/assets/img/careers/awsdevops-banner-desktop.webp b/src/assets/img/careers/aws-devops-banner-desktop.webp similarity index 100% rename from src/assets/img/careers/awsdevops-banner-desktop.webp rename to src/assets/img/careers/aws-devops-banner-desktop.webp diff --git a/src/assets/img/careers/cloud-engineer-banner.webp b/src/assets/img/careers/cloudops-banner-desktop.webp similarity index 100% rename from src/assets/img/careers/cloud-engineer-banner.webp rename to src/assets/img/careers/cloudops-banner-desktop.webp diff --git a/src/components/banner/banner.astro b/src/components/banner/banner.astro deleted file mode 100644 index 2f24920..0000000 --- a/src/components/banner/banner.astro +++ /dev/null @@ -1,31 +0,0 @@ ---- -import { Image } from 'astro:assets'; - -type Props = { - as?: HTMLElement; - className?: string; - id?: string; - backgroundImage: any; - backgroundAlt: string; -}; - -const { - as = 'div', - className = '', - id = '', - backgroundImage, - backgroundAlt, -}: Props = Astro.props; - -const Tag: HTMLElement = as; ---- - - - - diff --git a/src/components/course/course-hero.astro b/src/components/course/course-hero.astro index 4cd5c17..046129d 100644 --- a/src/components/course/course-hero.astro +++ b/src/components/course/course-hero.astro @@ -46,11 +46,11 @@ const { course }: Props = Astro.props;

Incluido en Carreras:

- {course.codyops_careers.map((carrer: any) => ( - - {carrer.codyops_careers_id.name} + {course.codyops_careers.map((career: any) => ( + + {career.codyops_careers_id.name} - ))} + ))}
\ No newline at end of file + diff --git a/src/data/aws-devops-career.ts b/src/data/aws-devops-career.ts deleted file mode 100644 index fa0a3bd..0000000 --- a/src/data/aws-devops-career.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { CourseLevel, PriceBox } from '../enums'; -import type { Accordion, CareerPrice } from '../types'; - -export const awsDevopsCareerPrices: CareerPrice[] = [ - { - description: 'Ejemplo 1: Al contado con un 60% de descuento.', - discount: true, - prices: [ - { - title: 'Precio regular', - price: 'USD 300', - type: PriceBox.DiscountPrice, - nextSign: '-', - }, - { - title: 'Dscto. 60%', - price: 'USD 180', - nextSign: '=', - }, - { - title: 'Precio final', - price: 'USD 120', - type: PriceBox.FinalPrice, - }, - ], - }, - { - description: 'Ejemplo 2: En tres cuotas mensuales con 40% de descuento.', - prices: [ - { - title: 'Cuota 1 (mes 1)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 2 (mes 2)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 3 (mes 3)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Precio al finalizar el tercer mes', - price: 'USD 180', - type: PriceBox.FinalPrice, - }, - ], - }, -]; -export const awsDevopsCareerProgram: Accordion[] = [ - { - title: 'Security, Identity & Compliance', - hours: '40 Horas', - children: [ - { - title: 'Amazon Identity and Access Management (AWS IAM)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Certificate Manager', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Secrets Manager', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Key Management Service (AWS KMS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Computing & Networking', - hours: '60 Horas', - children: [ - { - title: 'Amazon Virtual Private Cloud ( AWS VPC)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Compute Cloud (AWS EC2)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Route 53', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CloudFront', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Load Balancing', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon App Runner', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Serverless', - hours: '50 Horas', - children: [ - { - title: 'Amazon Simple Notification Service (AWS SNS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon API Gateway', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon EventBridge (Amazon CloudWatch Events)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Lambda', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Simple Queue Service (AWS SQS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Infrastructure & Orchestration', - hours: '10 Horas', - children: [ - { - title: 'Amazon CloudFormation', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Monitoring & Alerting', - hours: '20 Horas', - children: [ - { - title: 'Amazon CloudWatch', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CloudTrail ', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Continuous Integration', - hours: '50 Horas', - children: [ - { - title: 'Amazon CodeArtifact', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CodeBuild', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CodeDeploy', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CodePipeline', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CodeStar', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Collaboration & Management', - hours: '10 Horas', - children: [ - { - title: 'Amazon CodeCommit', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Containers Orchestration', - hours: '40 Horas', - children: [ - { - title: 'Amazon Elastic Container Registry (AWS ECR)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Fargate', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Container Service (AWS ECS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Kubernetes Service (AWS EKS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Store', - hours: '50 Horas', - children: [ - { - title: 'Amazon Simple Storage Service (AWS S3)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Block Store (AWS EBS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic File System (AWS EFS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Relational Database Service (AWS RDS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon DynamoDB', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, -]; diff --git a/src/data/cloudops-career.ts b/src/data/cloudops-career.ts deleted file mode 100644 index bd9689e..0000000 --- a/src/data/cloudops-career.ts +++ /dev/null @@ -1,309 +0,0 @@ -// eslint-disable-next-line import/extensions -import { CourseLevel, PriceBox } from '../enums'; -import type { Accordion, CareerPrice } from '../types'; - -export const cloudopsCareerPrices: CareerPrice[] = [ - { - description: 'Ejemplo 1: Al contado con un 60% de descuento.', - discount: true, - prices: [ - { - title: 'Precio regular', - price: 'USD 300', - type: PriceBox.DiscountPrice, - nextSign: '-', - }, - { - title: 'Dscto. 60%', - price: 'USD 180', - nextSign: '=', - }, - { - title: 'Precio final', - price: 'USD 120', - type: PriceBox.FinalPrice, - }, - ], - }, - { - description: 'Ejemplo 2: En tres cuotas mensuales con 40% de descuento.', - prices: [ - { - title: 'Cuota 1 (mes 1)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 2 (mes 2)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 3 (mes 3)', - price: 'USD 60', - type: PriceBox.CuotePrice, - nextSign: '=', - }, - { - title: 'Precio al finalizar el tercer mes', - price: 'USD 180', - type: PriceBox.FinalPrice, - }, - ], - }, -]; - -export const cloudopsCareerProgram: Accordion[] = [ - { - title: 'Collaboration and Productivity', - hours: '20 Horas', - children: [ - { - title: 'Google Workspace I (Gmail - Classroom - Calendar - Drive)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Google Workspace II (Chat & Space - Meet - Docs - Sheets)', - courseLevel: CourseLevel.Intermediate, - hours: '10 Horas', - }, - ], - }, - { - title: 'Development & Scripting', - hours: '18 Horas', - children: [ - { - title: 'Bourne Again SHell (Bash)', - courseLevel: CourseLevel.Introduction, - hours: '8 Horas', - }, - { - title: 'Bourne Again SHell (Bash)', - courseLevel: CourseLevel.Intermediate, - hours: '8 Horas', - }, - { - title: 'Google Cloud Shell', - courseLevel: CourseLevel.Introduction, - hours: '2 Horas', - }, - ], - }, - { - title: 'Infrastructure Orchestration', - hours: '54 Horas', - children: [ - { - title: 'Terraform', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Terragrunt ', - courseLevel: CourseLevel.Intermediate, - hours: '20 Horas', - }, - { - title: 'Pulumi ', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - ], - }, - { - title: 'Software Control Managment', - hours: '14 Horas', - children: [ - { - title: 'Git & GitHub', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - ], - }, - { - title: 'Containers Orchestration', - hours: '60 Horas', - children: [ - { - title: 'Docker', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Kubernetes', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Amazon Elastic Container Registry (ECR)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic Kubernetes Service (EKS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Continuous Integration', - hours: '14 Horas', - children: [ - { - title: 'GitHub Actions', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - ], - }, - { - title: 'Serverless', - hours: '10 Horas', - children: [ - { - title: 'Amazon Simple Notification Service (SNS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Deployment Automation', - hours: '10 Horas', - children: [ - { - title: 'Helm Charts', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Public Cloud Services', - hours: '20 Horas', - children: [ - { - title: 'Amazon Web Services', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - ], - }, - { - title: 'Compute', - hours: '10 Horas', - children: [ - { - title: 'Amazon Elastic Compute Cloud (EC2)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Administration & Government', - hours: '30 Horas', - children: [ - { - title: 'Amazon CloudWatch', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS CloudFormation', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Systems Manager', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Security, Identity and Compliance', - hours: '40 Horas', - children: [ - { - title: 'AWS Certificate Manager', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Identity and Access Management (IAM) ', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Key Management Service (KMS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Secrets Manager ', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Networking & Content Delivery', - hours: '40 Horas', - children: [ - { - title: 'Amazon Virtual Private Cloud (VPC)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Route53', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'AWS Elastic Load Balancing', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon CloudFront', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Storage', - hours: '20 Horas', - children: [ - { - title: 'Amazon Simple Storage Service (S3)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Amazon Elastic File System (EFS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Database', - hours: '10 Horas', - children: [ - { - title: 'Amazon Relational Database Service (RDS)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, -]; diff --git a/src/data/devops-career.ts b/src/data/devops-career.ts deleted file mode 100644 index 06e5218..0000000 --- a/src/data/devops-career.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { CourseLevel, PriceBox } from '../enums'; -import type { Accordion, CareerPrice } from '../types'; - -export const devopsCareerPrices: CareerPrice[] = [ - { - description: 'Ejemplo 1: Al contado con un 60% de descuento.', - discount: true, - prices: [ - { - title: 'Precio regular', - price: 'USD 180', - type: PriceBox.DiscountPrice, - nextSign: '-', - }, - { - title: 'Dscto. 60%', - price: 'USD 108', - nextSign: '=', - }, - { - title: 'Precio final', - price: 'USD 72', - type: PriceBox.FinalPrice, - }, - ], - }, - { - description: 'Ejemplo 2: En tres cuotas mensuales con 40% de descuento.', - prices: [ - { - title: 'Cuota 1 (mes 1)', - price: 'USD 36', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 2 (mes 2)', - price: 'USD 36', - type: PriceBox.CuotePrice, - nextSign: '+', - }, - { - title: 'Cuota 3 (mes 3)', - price: 'USD 36', - type: PriceBox.CuotePrice, - nextSign: '=', - }, - { - title: 'Precio al finalizar el tercer mes', - price: 'USD 108', - type: PriceBox.FinalPrice, - }, - ], - }, -]; -export const devopsCareerProgram: Accordion[] = [ - { - title: 'Collaboration and Productivity', - hours: '20 Horas', - children: [ - { - title: 'Google Workspace I (Gmail - Classroom - Calendar - Drive)', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Google Workspace II (Chat & Space - Meet - Docs - Sheets)', - courseLevel: CourseLevel.Intermediate, - hours: '10 Horas', - }, - ], - }, - { - title: 'Development & Scripting', - hours: '108 Horas', - children: [ - { - title: 'Python 3', - courseLevel: CourseLevel.Introduction, - hours: '45 Horas', - }, - { - title: 'Python 3', - courseLevel: CourseLevel.Intermediate, - hours: '45 Horas', - }, - { - title: 'Bourne Again SHell (Bash)', - courseLevel: CourseLevel.Introduction, - hours: '8 Horas', - }, - { - title: 'Bourne Again SHell (Bash)', - courseLevel: CourseLevel.Intermediate, - hours: '8 Horas', - }, - { - title: 'Google Cloud Shell', - courseLevel: CourseLevel.Introduction, - hours: '2 Horas', - }, - ], - }, - { - title: 'Infrastructure Orchestration', - hours: '54 Horas', - children: [ - { - title: 'Terraform', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Terragrunt ', - courseLevel: CourseLevel.Intermediate, - hours: '20 Horas', - }, - { - title: 'Pulumi ', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - ], - }, - { - title: 'Software Control Managment', - hours: '20 Horas', - children: [ - { - title: 'Git & GitHub', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - { - title: 'Metodologias Ágiles', - courseLevel: CourseLevel.Introduction, - hours: '6 Horas', - }, - ], - }, - { - title: 'Containers Orchestration', - hours: '50 Horas', - children: [ - { - title: 'Docker', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Kubernetes', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Minikube ', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Continuous Integration', - hours: '28 Horas', - children: [ - { - title: 'GitHub Actions', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - { - title: 'GitLab CI', - courseLevel: CourseLevel.Introduction, - hours: '14 Horas', - }, - ], - }, - { - title: 'DataBases', - hours: '10 Horas', - children: [ - { - title: 'MongoDB', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Deployment Automation', - hours: '20 Horas', - children: [ - { - title: 'Helm Charts', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - { - title: 'Kustomize', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, - { - title: 'Public Cloud Services', - hours: '20 Horas', - children: [ - { - title: 'Amazon Web Services', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - ], - }, - { - title: 'Monitoring & Alerting', - hours: '50 Horas', - children: [ - { - title: 'Prometheus', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'Gafana', - courseLevel: CourseLevel.Introduction, - hours: '20 Horas', - }, - { - title: 'AlertManager ', - courseLevel: CourseLevel.Introduction, - hours: '10 Horas', - }, - ], - }, -]; diff --git a/src/data/index.ts b/src/data/index.ts index a844ce3..a156834 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,6 +1,3 @@ -export * from './aws-devops-career.ts'; -export * from './cloudops-career.ts'; -export * from './devops-career.ts'; export * from './features.ts'; export * from './icon-blocks.ts'; export * from './testimonials.ts'; diff --git a/src/libs/careers.ts b/src/libs/careers.ts new file mode 100644 index 0000000..6e8c03f --- /dev/null +++ b/src/libs/careers.ts @@ -0,0 +1,70 @@ +import directus from './directus'; +import { readItems, type Query } from '@directus/sdk'; +import type { CodyopsCareers, Careers } from '../types/codyops-careers'; +import { sumTimes } from '../utils/time'; + +export async function getCareers(): Promise { + const careers = await directus.request( + readItems>('codyops_careers', { + fields: [ + 'slug', + 'name', + 'description', + 'banner', + { + courses: [ + { + codyops_courses_id: [ + 'name', + 'level', + 'category', + { + modules: [ + 'duration' + ] + } + ], + }, + ], + }, + ], + }) + ); + + const careersWithCalculatedHours = careers.map(career => { + let totalCareerMinutes = 0; + + const coursesWithCalculatedHours = career.courses.map(courseItem => { + const course = courseItem.codyops_courses_id; + if (course && course.modules) { + const moduleDurations = course.modules + .map(module => module.duration) + .filter((duration): duration is string => duration !== undefined && duration !== null); // Filter out undefined/null + const { hours, minutes } = sumTimes(moduleDurations); + const totalCourseMinutes = (hours * 60) + minutes; + totalCareerMinutes += totalCourseMinutes; + return { + ...courseItem, + codyops_courses_id: { + ...course, + totalCourseHours: hours + (minutes / 60), // Store as decimal hours + totalCourseMinutes: totalCourseMinutes, + } + }; + } + return courseItem; + }); + + const totalCareerHours = Math.floor(totalCareerMinutes / 60); + const remainingCareerMinutes = totalCareerMinutes % 60; + + return { + ...career, + courses: coursesWithCalculatedHours, + totalCareerHours: totalCareerHours + (remainingCareerMinutes / 60), // Store as decimal hours + totalCareerMinutes: totalCareerMinutes, + }; + }); + + return careersWithCalculatedHours; +} diff --git a/src/libs/courses.ts b/src/libs/courses.ts index 5110360..8875efb 100644 --- a/src/libs/courses.ts +++ b/src/libs/courses.ts @@ -1,14 +1,37 @@ import directus from "./directus"; -import { readItems } from "@directus/sdk"; +import { readItems, type Query } from "@directus/sdk"; +import type { CodyopsCourses, Courses } from "../types/codyops-courses"; export const courses = await directus.request( - readItems("codyops_courses", { + readItems>("codyops_courses", { fields: [ '*', - 'user_created.*', - 'modules.*', - 'codyops_careers.codyops_careers_id.name', - 'codyops_careers.codyops_careers_id.slug', + { + codyops_careers: [ + { + codyops_careers_id: [ + 'name', + 'slug' + ] + } + ] + }, + { + user_created: [ + 'first_name', + 'last_name', + 'avatar', + 'url' + ] + }, + { + modules: [ + 'duration', + 'title', + 'description', + 'video' + ] + } ]} ) ); @@ -30,4 +53,3 @@ export function filterCoursesByCategory(courses: any, category: any) { export function filterCoursesByCloud(courses: any, clouds: string[]) { return courses.filter((course: any) => course.cloud?.includes(clouds)); }; - diff --git a/src/libs/directus.ts b/src/libs/directus.ts index 430c528..5b20f07 100644 --- a/src/libs/directus.ts +++ b/src/libs/directus.ts @@ -30,16 +30,19 @@ type Course = { modules: []; } +import type { Careers } from '../types/codyops-careers'; + type Schema = { codyops_posts: Post[]; codyops_courses: Course[]; codyops_campaigns: []; codyops_reviews: []; codyops_currencies: []; + codyops_careers: Careers[]; } const directus = createDirectus(import.meta.env.ASTRO_DIRECTUS_API) .with(staticToken(import.meta.env.ASTRO_DIRECTUS_TOKEN)) .with(rest()); -export default directus; \ No newline at end of file +export default directus; diff --git a/src/pages/carreras/[slug].astro b/src/pages/carreras/[slug].astro new file mode 100644 index 0000000..539d1c0 --- /dev/null +++ b/src/pages/carreras/[slug].astro @@ -0,0 +1,54 @@ +--- +import Root from '../../layouts/Root.astro'; +import Main from '../../layouts/Bundle.astro'; +import CareerProgram from '../../sections/global/career-program/career-program.astro'; +import CareerSection from '../../sections/global/career-section/career-section.astro'; +import Cta from '../../sections/global/cta/cta.astro'; +import { getCareers } from '../../libs/careers'; +import type { Careers } from '../../types/codyops-careers'; +import { Image } from 'astro:assets'; + +export async function getStaticPaths() { + const careers = await getCareers(); + + return careers.map((career: Careers) => { + return { + params: { slug: career.slug }, + props: { career }, + }; + }); +} + +interface Props { + career: Careers; +} + +const { career } = Astro.props; + +// Dynamically import the description and features components based on the slug +const DescriptionComponent = (await import(`../../sections/${career.slug}/${career.slug}-description.astro`)).default; +const FeaturesComponent = (await import(`../../sections/${career.slug}/${career.slug}-features.astro`)).default; +--- + + +
+
+ +
+ + + + + + +
+
diff --git a/src/pages/carreras/aws-devops.astro b/src/pages/carreras/aws-devops.astro deleted file mode 100644 index 13f2212..0000000 --- a/src/pages/carreras/aws-devops.astro +++ /dev/null @@ -1,35 +0,0 @@ ---- -import Root from '../../layouts/Root.astro'; -import Main from '../../layouts/Bundle.astro'; -import AwsdevopsImage from '../../assets/img/careers/awsdevops-banner-desktop.webp'; -import Banner from '../../components/banner/banner.astro'; -import { awsDevopsCareerProgram } from '../../data'; -import AwsCloudopsDescription from '../../sections/aws-devops/aws-devops-description.astro'; -import AwsDevopsFeatures from '../../sections/aws-devops/aws-devops-features.astro'; -import CareerProgram from '../../sections/global/career-program/career-program.astro'; -import CareerSection from '../../sections/global/career-section/career-section.astro'; -import Cta from '../../sections/global/cta/cta.astro'; - -const programLink:string = 'https://drive.google.com/file/d/12lYUSAcN0qI9R0_N63oPTimBKIYRKOh9/preview'; ---- - -
- - - - - - - - - -
-
diff --git a/src/pages/carreras/cloudops.astro b/src/pages/carreras/cloudops.astro deleted file mode 100644 index 36e1f4b..0000000 --- a/src/pages/carreras/cloudops.astro +++ /dev/null @@ -1,36 +0,0 @@ ---- -import Root from '../../layouts/Root.astro'; -import Main from '../../layouts/Bundle.astro'; -import CloudopsImage from '../../assets/img/careers/cloud-engineer-banner.webp'; -import Banner from '../../components/banner/banner.astro'; -import { cloudopsCareerProgram } from '../../data'; -import CloudopsDescription from '../../sections/cloudops/cloudops-description.astro'; -import CloudopsFeatures from '../../sections/cloudops/cloudops-features.astro'; -import CareerSection from '../../sections/global/career-section/career-section.astro'; -import CareerProgram from '../../sections/global/career-program/career-program.astro'; -import Cta from '../../sections/global/cta/cta.astro'; - -const programLink:string = 'https://drive.google.com/file/d/1IQVPvhikLos-xs8s3gJazN7Nj45ZSNYL/preview'; ---- - - -
- - - - - - - - - -
-
diff --git a/src/pages/carreras/devops.astro b/src/pages/carreras/devops.astro deleted file mode 100644 index 984ef4d..0000000 --- a/src/pages/carreras/devops.astro +++ /dev/null @@ -1,36 +0,0 @@ ---- -import Root from '../../layouts/Root.astro'; -import Main from '../../layouts/Bundle.astro'; -import DevopsImage from '../../assets/img/careers/devops-banner-desktop.webp'; -import Banner from '../../components/banner/banner.astro'; -import { devopsCareerProgram } from '../../data'; -import DevopsDescription from '../../sections/devops/devops-description.astro'; -import DevopsFeatures from '../../sections/devops/devops-features.astro'; -import CareerProgram from '../../sections/global/career-program/career-program.astro'; -import CareerSection from '../../sections/global/career-section/career-section.astro'; -import Cta from '../../sections/global/cta/cta.astro'; - -const programLink:string = 'https://drive.google.com/file/d/1wz_TeKtrt_d8AHiySGDsYX1VVq48j_Ie/preview'; ---- - - -
- - - - - - - - - -
-
diff --git a/src/sections/global/career-program/career-program.astro b/src/sections/global/career-program/career-program.astro index 565a806..6b327a2 100644 --- a/src/sections/global/career-program/career-program.astro +++ b/src/sections/global/career-program/career-program.astro @@ -1,34 +1,54 @@ --- -import { Accordion, CareerCourse } from '../../../types'; -import { toSnakeCase } from '../../../utils'; +import type { Courses } from '../../../types/codyops-courses'; +import type { Careers } from '../../../types/codyops-careers'; +import { toSnakeCase, fromSnakeCase } from '../../../utils'; +import { slugifyCourse } from '../../../utils/text'; type Props = { - href: string; - list?: Accordion[]; - download?: string; + career: Careers; }; -const { href, list, download }: Props = Astro.props; +const { career }: Props = Astro.props; + +// Group courses by category +const groupedCourses = career.courses?.reduce((acc, courseItem) => { + const course = courseItem.codyops_courses_id; + if (course) { + const category = course.category; + if (!acc[category]) { + acc[category] = { courses: [], totalCategoryMinutes: 0 }; + } + acc[category].courses.push(course); + if (course.totalCourseMinutes) { + acc[category].totalCategoryMinutes += course.totalCourseMinutes; + } + } + return acc; +}, {} as Record); + ---

Programa de Carrera

+
+ Total Carrera: {career.totalCareerHours?.toFixed(1)} horas +
- {list && list.map(({children, hours, title}: Accordion) => ( + {groupedCourses && Object.entries(groupedCourses).map(([category, { courses, totalCategoryMinutes }]) => (
-
+
-
+
{ - children && children.map(({courseLevel, hours, title}: CareerCourse)=>( + courses.map((course: Courses)=>(
@@ -63,9 +83,9 @@ const { href, list, download }: Props = Astro.props;
- {title} - {courseLevel}{course.name} + {course.level}
@@ -78,7 +98,7 @@ const { href, list, download }: Props = Astro.props;
- {hours} + {course.totalCourseHours?.toFixed(1)} hours
@@ -97,15 +117,5 @@ const { href, list, download }: Props = Astro.props;
))} -
diff --git a/src/types/accordion.ts b/src/types/accordion.ts index 2c2d382..63f6486 100644 --- a/src/types/accordion.ts +++ b/src/types/accordion.ts @@ -1,4 +1,4 @@ -import type { CareerCourse } from '@/types/career.ts'; +import type { CareerCourse } from './career.ts'; type Position = 'left' | 'right'; diff --git a/src/types/career.ts b/src/types/career.ts index 29ce7b6..c21a541 100644 --- a/src/types/career.ts +++ b/src/types/career.ts @@ -1,4 +1,4 @@ -import type { CourseLevel, PriceBox } from '@/enums'; +import type { CourseLevel, PriceBox } from '../enums'; export type Career = { image: any; diff --git a/src/types/codyops-careers.ts b/src/types/codyops-careers.ts new file mode 100644 index 0000000..5e11891 --- /dev/null +++ b/src/types/codyops-careers.ts @@ -0,0 +1,25 @@ +import type { Courses } from './codyops-courses'; +import type { Users } from './codyops-users'; + +export interface Careers { + id: string; + slug: string; + status: string; + sort: number | null; + user_created: Users; + user_updated: Users; + date_created: string | null; + date_updated: string | null; + name: string; + description: string; + banner: string; + courses: { + codyops_courses_id: Courses; + }[]; + totalCareerHours?: number; + totalCareerMinutes?: number; +} + +export interface CodyopsCareers { + codyops_careers: Careers; +} diff --git a/src/types/codyops-courses.ts b/src/types/codyops-courses.ts new file mode 100644 index 0000000..b1a1d18 --- /dev/null +++ b/src/types/codyops-courses.ts @@ -0,0 +1,34 @@ +import type { Modules } from "./codyops-modules"; +import type { Careers } from "./codyops-careers"; +import type { Users } from "./codyops-users"; + +export interface Courses { + id: string; + status: string; + sort: number | null; + user_created?: Partial; + user_updated: Users; + date_created: string | null; + date_updated: string | null; + name: string; + description: string; + level: string; + type: string; + category: string; + language: string; + features: string; + version: string; + image: string; + modules?: Partial[]; + content: string; + cloud: string; + codyops_careers?: { + codyops_careers_id?: Partial; + }; + totalCourseHours?: number; + totalCourseMinutes?: number; +} + +export interface CodyopsCourses { + codyops_courses: Courses[]; +} diff --git a/src/types/codyops-modules.ts b/src/types/codyops-modules.ts new file mode 100644 index 0000000..018ac9e --- /dev/null +++ b/src/types/codyops-modules.ts @@ -0,0 +1,15 @@ +export interface Modules { + id: string; + status: string; + sort: number | null; + user_created: string | null; + user_updated: string | null; + date_created: string | null; + date_updated: string | null; + title: string; + description: string; + video_theory: string; + video_practice: string; + duration: string; + courses_id: string; +} \ No newline at end of file diff --git a/src/types/codyops-users.ts b/src/types/codyops-users.ts new file mode 100644 index 0000000..e4b244b --- /dev/null +++ b/src/types/codyops-users.ts @@ -0,0 +1,6 @@ +export interface Users { + first_name: string; + last_name: string; + avatar: string; + url: string; +} \ No newline at end of file diff --git a/src/types/course.ts b/src/types/course.ts index 95e0b2a..30919c2 100644 --- a/src/types/course.ts +++ b/src/types/course.ts @@ -1,4 +1,4 @@ -import type { Accordion } from '@/types/accordion.ts'; +import type { Accordion } from './accordion.ts'; export type Course = { title: string; diff --git a/src/utils/course-utils.ts b/src/utils/course-utils.ts index 521f4fc..775e697 100644 --- a/src/utils/course-utils.ts +++ b/src/utils/course-utils.ts @@ -1,28 +1,26 @@ -import type { CollectionEntry } from 'astro:content'; +import type { Courses } from '../types/codyops-courses'; export const getUniqueCategories = ( - courses: Array> + courses: Courses[] ) => { const coursesEntries = courses.flatMap( - // @ts-ignore - // eslint-disable-next-line no-unsafe-optional-chaining - (course: CollectionEntry<'courses'>) => [...course?.data?.categories] + (course: Courses) => [...course.category] ); return [...new Set(coursesEntries)]; }; -export const getUniqueLevels = (courses: CollectionEntry<'courses'>[]) => { +export const getUniqueLevels = (courses: Courses[]) => { const coursesEntries = courses.map( - (course: CollectionEntry<'courses'>) => course.data.level + (course: Courses) => course.level ); return [...new Set(coursesEntries)]; }; -export const sortCoursesByDate = (posts: CollectionEntry<'courses'>[]) => - posts - .filter(({ data }) => !data?.draft) +export const sortCoursesByDate = (courses: Courses[]) => + courses + .filter((course) => course.status !== 'draft') // Assuming 'draft' status is used instead of a 'draft' property .sort( (a, b) => - Math.floor(new Date(b?.data?.date).getTime() / 1000) - - Math.floor(new Date(a?.data?.date).getTime() / 1000) + Math.floor(new Date(b.date_created || '').getTime() / 1000) - + Math.floor(new Date(a.date_created || '').getTime() / 1000) ); diff --git a/src/utils/from-snake-case.ts b/src/utils/from-snake-case.ts index 8cef6d4..7d0e8e0 100644 --- a/src/utils/from-snake-case.ts +++ b/src/utils/from-snake-case.ts @@ -1,3 +1,3 @@ -export const fromSnakeCase = (text: any) => { - return text.replace(/-/g, ' ').replace(/\b\w/g, (word) => word.toUpperCase()); +export const fromSnakeCase = (text: string) => { + return text.replace(/-/g, ' ').replace(/\b\w/g, (word: string) => word.toUpperCase()); }; diff --git a/src/utils/time.ts b/src/utils/time.ts index fb0b52b..ea0ad90 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,23 +1,35 @@ -function parseTime(time) { - const [hours, minutes, milliseconds] = time.split(':').map(Number); +interface TimeParts { + hours: number; + minutes: number; + milliseconds: number; +} + +interface TimeResult { + hours: number; + minutes: number; +} + +function parseTime(time: string | null | undefined): TimeParts { + const safeTime = time || '0:0:0'; // Provide a default string if time is null/undefined/empty + const [hours, minutes, milliseconds] = safeTime.split(':').map(Number); return { hours, minutes, milliseconds }; } -function timeToMilliseconds({ hours, minutes, milliseconds }) { +function timeToMilliseconds({ hours, minutes, milliseconds }: TimeParts): number { return (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + milliseconds; } -function millisecondsToTime(ms) { +function millisecondsToTime(ms: number): TimeResult { const totalMinutes = Math.floor(ms / (60 * 1000)); const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; return { hours, minutes }; } -export function sumTimes(times) { +export function sumTimes(times: string[]): TimeResult { const totalMilliseconds = times .map(parseTime) .map(timeToMilliseconds) - .reduce((acc, ms) => acc + ms, 0); + .reduce((acc: number, ms: number) => acc + ms, 0); return millisecondsToTime(totalMilliseconds); }