diff --git a/.github/workflows/labels.yaml b/.github/workflows/labels.yaml index 5326acd4..083f2648 100644 --- a/.github/workflows/labels.yaml +++ b/.github/workflows/labels.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: node-version: "20" - uses: SpaceyaTech/gh-action-open-source-labels@main diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d5d21b58..dd6fe1aa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Prepare .env file run: | rm -f .env && touch .env @@ -17,7 +17,7 @@ jobs: echo "VITE_SERVICE_ID=123fAkE" >> .env echo "VITE_TEMPLATE_ID=123fAkE" >> .env echo "VITE_PUBLIC_ID=123fAkE" >> .env - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: "20.x" - name: Install dependencies diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e2f990ce..fb30bfe7 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -8,8 +8,8 @@ jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: "20.x" - name: Install dependencies diff --git a/package.json b/package.json index 9d53f342..4b3288db 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", "react-lazy-load-image-component": "^1.6.0", + "react-phone-number-input": "^3.4.12", "react-photo-album": "^2.3.0", "react-router-dom": "^6.11.2", "react-share": "^5.1.0", @@ -54,8 +55,8 @@ }, "devDependencies": { "@playwright/test": "^1.44.1", - "@testing-library/react": "^16.0.0", "@tailwindcss/typography": "^0.5.13", + "@testing-library/react": "^16.0.0", "@types/node": "^20.14.1", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", diff --git a/public/confirmation-card.svg b/public/confirmation-card.svg new file mode 100644 index 00000000..0eab0867 --- /dev/null +++ b/public/confirmation-card.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/discount.svg b/public/discount.svg new file mode 100644 index 00000000..40c637f0 --- /dev/null +++ b/public/discount.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/icons/duration.svg b/src/assets/images/icons/duration.svg new file mode 100644 index 00000000..d530b3f2 --- /dev/null +++ b/src/assets/images/icons/duration.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icons/index.js b/src/assets/images/icons/index.js index cfa4eef4..76cd0faa 100644 --- a/src/assets/images/icons/index.js +++ b/src/assets/images/icons/index.js @@ -4,6 +4,7 @@ import briefcase from "./briefcase.svg"; import calendar from "./calendar.svg"; import check from "./check.svg"; import dotpoints from "./dotpoints.svg"; +import duration from "./duration.svg"; import folder from "./folder.svg"; import sytLogoGreen from "./logo-green-bg.svg"; import sytLogoWhite from "./logo-white-bg.svg"; @@ -18,6 +19,7 @@ export { calendar, check, dotpoints, + duration, folder, sytLogoGreen, sytLogoWhite, diff --git a/src/assets/images/mastercraft/mastercraft-hero-footer.png b/src/assets/images/mastercraft/mastercraft-hero-footer.png index 0d2e126c..4d28d7dd 100644 Binary files a/src/assets/images/mastercraft/mastercraft-hero-footer.png and b/src/assets/images/mastercraft/mastercraft-hero-footer.png differ diff --git a/src/assets/images/mastercraft/mastercraft-hero.png b/src/assets/images/mastercraft/mastercraft-hero.png deleted file mode 100644 index 0f546051..00000000 Binary files a/src/assets/images/mastercraft/mastercraft-hero.png and /dev/null differ diff --git a/src/assets/images/mastercraft/mastercraft-hero.svg b/src/assets/images/mastercraft/mastercraft-hero.svg new file mode 100644 index 00000000..c6d71074 --- /dev/null +++ b/src/assets/images/mastercraft/mastercraft-hero.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/resources-page/android.png b/src/assets/images/resources-page/android.png new file mode 100644 index 00000000..fb06d2c7 Binary files /dev/null and b/src/assets/images/resources-page/android.png differ diff --git a/src/assets/images/resources-page/backend.png b/src/assets/images/resources-page/backend.png index 1ecbdb2a..bdd31f25 100644 Binary files a/src/assets/images/resources-page/backend.png and b/src/assets/images/resources-page/backend.png differ diff --git a/src/assets/images/resources-page/frontend.png b/src/assets/images/resources-page/frontend.png index f56bf7a6..da865683 100644 Binary files a/src/assets/images/resources-page/frontend.png and b/src/assets/images/resources-page/frontend.png differ diff --git a/src/assets/images/resources-page/icons/twitter-x.svg b/src/assets/images/resources-page/icons/twitter-x.svg new file mode 100644 index 00000000..9ce405e4 --- /dev/null +++ b/src/assets/images/resources-page/icons/twitter-x.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/resources-page/index.js b/src/assets/images/resources-page/index.js index e04e4d58..9fb77244 100644 --- a/src/assets/images/resources-page/index.js +++ b/src/assets/images/resources-page/index.js @@ -1,3 +1,4 @@ +export { default as android } from "./android.png"; export { default as backend } from "./backend.png"; export { default as UI } from "./bad-good-UI.png"; export { default as UIDesignEra } from "./UIDesignEra.png"; @@ -6,7 +7,7 @@ export { default as frontend } from "./frontend.png"; export { default as hero } from "./hero.png"; export { default as resourceHero } from "./resource-hero.png"; export { default as heroFooter } from "./hero-footer.png"; -export { default as productDesign } from "./product-design.png"; +export { default as productDesign } from "./productDesign.png"; export { default as podPoster } from "./podcast-poster.png"; export { default as youtube } from "./icons/youtube.svg"; diff --git a/src/assets/images/resources-page/productDesign.png b/src/assets/images/resources-page/productDesign.png new file mode 100644 index 00000000..61b3ae2a Binary files /dev/null and b/src/assets/images/resources-page/productDesign.png differ diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 3956ef1a..707fa594 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -29,23 +29,28 @@ const navLinks = [ link: "Products", route: "/products", }, - { - id: 5, - link: "Blogs", - route: "/blogs", - }, - { - id: 6, - link: "Resources", - route: "/resources", - }, + // { + // id: 5, + // link: "Blogs", + // route: "/blogs", + // }, + // { + // id: 6, + // link: "Resources", + // route: "/resources", + // }, { id: 7, - link: "Shop", - route: "/shop", + link: "Mastercraft", + route: "/mastercraft", }, // { // id: 8, + // link: "Shop", + // route: "/shop", + // }, + // { + // id: 9, // link: "Donate", // route: "/donate", // }, diff --git a/src/components/index.js b/src/components/index.js index 4a8d5804..729a89cf 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -2,7 +2,8 @@ export { default as Button } from "./Button"; export { default as Caroussel } from "./Caroussel"; export { default as CartDrawer } from "./shop/CartDrawer"; export { default as Counter } from "./shop/Counter"; -export { default as CurriculumAccordion } from "./CurriculumAccordion"; +export { default as CurriculumAccordion } from "../pages/mastercraft/sections/CurriculumAccordion"; +export { default as MastercraftFAQAccordion } from "../pages/mastercraft-home/sections/MastercraftFAQAccordion"; export { default as FAQ } from "./FAQ"; export { default as Footer } from "./Footer"; export { default as GoBackBtn } from "./GoBackBtn"; diff --git a/src/hooks/Mutations/mastercraft/useCheckPaymentStatus.jsx b/src/hooks/Mutations/mastercraft/useCheckPaymentStatus.jsx new file mode 100644 index 00000000..fc330f56 --- /dev/null +++ b/src/hooks/Mutations/mastercraft/useCheckPaymentStatus.jsx @@ -0,0 +1,131 @@ +import { useQuery } from "@tanstack/react-query"; +import { useCallback, useEffect, useRef, useState } from "react"; +import privateAxios from "../../../api/privateAxios"; + +const useCheckPaymentStatus = (enrollmentId, options = {}) => { + const { + enabled = false, + pollingInterval = 5000, + maxAttempts = 3, + isOpen = true, + } = options; + + const [status, setStatus] = useState({ + isSuccess: false, + isError: false, + isPending: true, + message: "", + errorCode: null, + }); + + const attemptsRef = useRef(0); + + const { data, isError, isSuccess, refetch, isLoading } = useQuery({ + queryKey: ["paymentStatus", enrollmentId], + queryFn: async () => { + if (!enrollmentId) return null; + try { + const response = await privateAxios.get(`/callback/${enrollmentId}/`); + return response.data; + } catch (error) { + if (error.response?.status === 400) { + setStatus({ + isSuccess: false, + isError: true, + isPending: false, + message: error.response.data?.message || "Payment failed", + errorCode: error.response.status, + }); + + return { + error: true, + message: error.response.data?.message || "Payment failed", + errorCode: error.response.status, + }; + } + throw error; + } + }, + enabled: + Boolean(enrollmentId) && + enabled && + attemptsRef.current < maxAttempts && + isOpen, + refetchInterval: status.isPending ? pollingInterval : false, + refetchOnWindowFocus: false, + retry: (failureCount, error) => { + if (error.response?.status === 400) { + return false; + } + if (attemptsRef.current >= maxAttempts) { + return false; + } + return failureCount < maxAttempts; + }, + }); + + useEffect(() => { + if ((isSuccess || isError) && !isLoading && status.isPending) { + attemptsRef.current += 1; + } + + if (attemptsRef.current >= maxAttempts && status.isPending) { + setStatus({ + isSuccess: false, + isError: true, + isPending: false, + message: "Payment verification timed out. Please contact support.", + errorCode: "TIMEOUT", + }); + return; + } + + if (isSuccess && data) { + if (data.error) { + setStatus({ + isSuccess: false, + isError: true, + isPending: false, + message: data.message || "Payment failed", + errorCode: data.errorCode || "PAYMENT_FAILED", + }); + } else { + setStatus({ + isSuccess: true, + isError: false, + isPending: false, + message: data.message || "Payment successful", + errorCode: null, + }); + } + } + }, [data, isSuccess, isError, isLoading, status.isPending, maxAttempts]); + + const checkStatus = useCallback(() => { + if (!enrollmentId) return null; + attemptsRef.current = 0; + setStatus({ + isSuccess: false, + isError: false, + isPending: true, + message: "", + errorCode: null, + }); + return refetch(); + }, [enrollmentId, refetch]); + + useEffect(() => { + if (enrollmentId && enabled && isOpen) { + checkStatus(); + } + }, [checkStatus, enabled, enrollmentId, isOpen]); + + return { + ...status, + data, + checkStatus, + attemptsCount: attemptsRef.current, + }; +}; + +export default useCheckPaymentStatus; diff --git a/src/hooks/Mutations/mastercraft/useEnroll.jsx b/src/hooks/Mutations/mastercraft/useEnroll.jsx new file mode 100644 index 00000000..a359375a --- /dev/null +++ b/src/hooks/Mutations/mastercraft/useEnroll.jsx @@ -0,0 +1,49 @@ +/* eslint-disable no-alert */ +/* eslint-disable no-console */ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import privateAxios from "../../../api/privateAxios"; + +const useEnroll = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (formData) => { + const response = await privateAxios.post( + "/mastercraft-enrollment/", + formData, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + return response.data; + }, + mutationKey: ["enrollment"], + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["enrollment"] }); + + return data; + }, + onError: (error) => { + const profileErrors = error?.response?.data?.profile; + + if (profileErrors && typeof profileErrors === "object") { + Object.entries(profileErrors).forEach(([field, messages]) => { + if (Array.isArray(messages)) { + messages.forEach((message) => { + console.error(`${field}: ${message}`); + alert(`${field}: ${message}`); + }); + } + }); + } else if (error?.response?.data?.program_name) { + console.error("Program name error:", error.response.data.program_name); + } else { + console.error("Enrollment failed with an unknown error", error); + } + }, + }); +}; + +export default useEnroll; diff --git a/src/index.css b/src/index.css index 34ded2a8..a2049747 100644 --- a/src/index.css +++ b/src/index.css @@ -2,6 +2,7 @@ @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;500;600;700;800;900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"); @tailwind base; @tailwind components; @@ -73,6 +74,11 @@ border-bottom: 2px solid #f3f4f5; } +.PhoneInputInput { + background-color: transparent; + outline: none; +} + @media (min-width: 768px) { .scrollbar::-webkit-scrollbar { width: 6px; diff --git a/src/index.js b/src/index.js index b223d4de..81cf6eed 100644 --- a/src/index.js +++ b/src/index.js @@ -72,6 +72,9 @@ const InventoryReport = lazy( const OrdersPage = lazy(() => import("./pages/admin/shop/OrdersPage")); const Mastercraft = lazy(() => import("./pages/mastercraft/Mastercraft")); +const MastercraftEnroll = lazy( + () => import("./pages/mastercraft-enroll/MastercraftEnroll") +); export { AboutUs, @@ -103,6 +106,7 @@ export { Layout, LogIn, Mastercraft, + MastercraftEnroll, OrdersPage, ProductDisplay, Products, diff --git a/src/main.jsx b/src/main.jsx index 9bbe2f50..0b05b17e 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -11,6 +11,7 @@ import { SearchBlogProvider } from "./context/searchBlog"; import router from "./router"; import { ErrorBoundary } from "."; import "react-lazy-load-image-component/src/effects/blur.css"; +import "react-phone-number-input/style.css"; const queryClient = new QueryClient({ defaultOptions: { diff --git a/src/pages/landingPage/sections/OurEvents.jsx b/src/pages/landingPage/sections/OurEvents.jsx index 4b57995d..b963b14b 100644 --- a/src/pages/landingPage/sections/OurEvents.jsx +++ b/src/pages/landingPage/sections/OurEvents.jsx @@ -1,9 +1,10 @@ import PropTypes from "prop-types"; import { useEffect } from "react"; +import { FiArrowRightCircle } from "react-icons/fi"; import { LazyLoadImage } from "react-lazy-load-image-component"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { error500svg } from "../../../assets/images/errorPages"; -import { Button, Loader } from "../../../components"; +import { Loader } from "../../../components"; import useTopEvents from "../../../hooks/Queries/eventsSection/useTopEvents"; import { calculateDistanceToDate, @@ -20,6 +21,8 @@ function OurEvents() { refetch: refetchTopEvents, } = useTopEvents(""); + const navigate = useNavigate(); + useEffect(() => { refetchTopEvents(); }, [refetchTopEvents]); @@ -44,9 +47,20 @@ function OurEvents() {

Upcoming Events

- + {isError && (
{/*
*/} {topEvents?.count === 0 ? ( -

No events found!

+
+

+ No events found! +

+
) : ( topEvents?.results .slice(0, 6) diff --git a/src/pages/mastercraft-enroll/MastercraftEnroll.jsx b/src/pages/mastercraft-enroll/MastercraftEnroll.jsx new file mode 100644 index 00000000..a7ed4eea --- /dev/null +++ b/src/pages/mastercraft-enroll/MastercraftEnroll.jsx @@ -0,0 +1,43 @@ +import { useLocation } from "react-router-dom"; +import SeoMetadata from "../../components/SeoMetadata"; +import { Header as MastercraftHeader } from "../mastercraft/sections"; +import { programs } from "../mastercraft/sections/data"; +import MastercraftEnrollmentComponent from "./sections/MastercraftEnrollmentComponent"; + +function MastercraftEnroll() { + const { pathname } = useLocation(); + const program = pathname.split("/")[2]; + + const programRender = programs.find(({ slug }) => slug === program); + + return ( + <> + +
+
+ + + +
+
+ + ); +} + +export default MastercraftEnroll; diff --git a/src/pages/mastercraft-enroll/sections/CourseInfoCard.jsx b/src/pages/mastercraft-enroll/sections/CourseInfoCard.jsx new file mode 100644 index 00000000..5af1d1af --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/CourseInfoCard.jsx @@ -0,0 +1,32 @@ +import { Palette } from "lucide-react"; + +function CourseInfoCard() { + return ( +
+
+
+
+ +
+
UI/UX Designer
+

+ Mastercraft Internship ● 8 weeks +

+
+
+ +
+

+ KES{" "} + {new Intl.NumberFormat("en-US", { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + }).format(2800)} +

+
+
+ ); +} + +export default CourseInfoCard; diff --git a/src/pages/mastercraft-enroll/sections/EnrollmentHeader.jsx b/src/pages/mastercraft-enroll/sections/EnrollmentHeader.jsx new file mode 100644 index 00000000..1d6f06a2 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/EnrollmentHeader.jsx @@ -0,0 +1,19 @@ +/* eslint-disable react/prop-types */ +function EnrollmentHeader({ title }) { + return ( +
+

+ I'm enrolling for the Mastercraft program, in{" "} + {title} +

+ +
+

+ 121 have already enrolled +

+
+
+ ); +} + +export default EnrollmentHeader; diff --git a/src/pages/mastercraft-enroll/sections/EnrollmentProgressBar.jsx b/src/pages/mastercraft-enroll/sections/EnrollmentProgressBar.jsx new file mode 100644 index 00000000..61aea8a2 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/EnrollmentProgressBar.jsx @@ -0,0 +1,16 @@ +/* eslint-disable react/prop-types */ +function EnrollmentProgressBar({ currentStep }) { + return ( +
+
+
1 ? "bg-primary" : "bg-[#BAC6C3]"}`} + /> +
2 ? "bg-primary" : "bg-[#BAC6C3]"}`} + /> +
+ ); +} + +export default EnrollmentProgressBar; diff --git a/src/pages/mastercraft-enroll/sections/EnrollmentSteps.jsx b/src/pages/mastercraft-enroll/sections/EnrollmentSteps.jsx new file mode 100644 index 00000000..209cf49b --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/EnrollmentSteps.jsx @@ -0,0 +1,31 @@ +/* eslint-disable react/prop-types */ +import Payment from "./Payment"; +import PersonalInformation from "./PersonalInformation"; +import Qualifications from "./Qualifications"; + +function EnrollmentStep({ index, formData, handleInputChange }) { + switch (index) { + case 1: + return ( + + ); + case 2: + return ( + + ); + case 3: + return ( + + ); + default: + return null; + } +} + +export default EnrollmentStep; diff --git a/src/pages/mastercraft-enroll/sections/FormStepNavigation.jsx b/src/pages/mastercraft-enroll/sections/FormStepNavigation.jsx new file mode 100644 index 00000000..55ef4d8a --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/FormStepNavigation.jsx @@ -0,0 +1,47 @@ +/* eslint-disable no-nested-ternary */ +import PropTypes from "prop-types"; + +function FormStepNavigation({ + currentStep, + isProcessing, + isPending, + onBack, + onContinue, +}) { + return ( +
+ + +
+ ); +} + +export default FormStepNavigation; + +FormStepNavigation.propTypes = { + currentStep: PropTypes.number.isRequired, + isProcessing: PropTypes.bool.isRequired, + isPending: PropTypes.bool.isRequired, + onBack: PropTypes.func.isRequired, + onContinue: PropTypes.func.isRequired, +}; diff --git a/src/pages/mastercraft-enroll/sections/MastercraftEnrollmentComponent.jsx b/src/pages/mastercraft-enroll/sections/MastercraftEnrollmentComponent.jsx new file mode 100644 index 00000000..bc95cac2 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/MastercraftEnrollmentComponent.jsx @@ -0,0 +1,267 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable no-console */ +/* eslint-disable no-alert */ +import { useState, useMemo } from "react"; +import useEnroll from "../../../hooks/Mutations/mastercraft/useEnroll"; +import EnrollmentHeader from "./EnrollmentHeader"; +import EnrollmentProgressBar from "./EnrollmentProgressBar"; +import FormStepNavigation from "./FormStepNavigation"; +import Payment from "./Payment"; +import PaymentStatusModal from "./PaymentStatusModal"; +import PersonalInformation from "./PersonalInformation"; +import Qualifications from "./Qualifications"; +import TermsOfUseCheckbox from "./TermsOfUseCheckbox"; + +function MastercraftEnrollmentComponent({ program }) { + const [index, setIndex] = useState(1); + const [isOpen, setIsOpen] = useState(false); + const [isProcessing, setIsProcessing] = useState(false); + const [enrollmentId, setEnrollmentId] = useState(null); + const [paymentInitiated, setPaymentInitiated] = useState(false); + const [formData, setFormData] = useState({ + fullName: "", + email: "", + phoneNumber: "", + github: "", + project: "", + linkedIn: "", + objective: "", + experienceYears: "", + professionalJourney: "", + collaborationTools: "", + dailyHours: "", + referralSource: "", + availableDay: "", + paymentPhoneNumber: "", + promoCode: "", + termsOfUse: false, + }); + + const { + mutate: enrollMastercraft, + isPending: isEnrollmentPending, + // isSuccess: enrollmentSuccess, + // isError: enrollmentError, + // error: enrollmentErrorMessage, + // data: enrollmentData, + } = useEnroll(); + + const reformattedFormData = useMemo( + () => ({ + profile: { + full_name: formData.fullName, + email: formData.email, + phone_number: formData.phoneNumber, + github_link: formData.github, + project_link: formData.project, + linkedin_url: formData.linkedIn, + objective: formData.objective, + duration_of_experience: formData.experienceYears, + collaboration_tools_familiarity: formData.collaborationTools, + daily_learning_hours: formData.dailyHours, + referral_source: formData.referralSource, + standup_day: formData.availableDay, + }, + program_name: "PRODUCT_DESIGN", + mpesa_phone_number: formData.paymentPhoneNumber, + }), + [formData] + ); + + const closeModal = () => { + setIsOpen(false); + setEnrollmentId(null); + setPaymentInitiated(false); + }; + + const openModal = () => { + setIsOpen(true); + }; + + const handleInputChange = (name, value) => { + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const validatePersonalInfo = () => { + const { fullName, email, phoneNumber } = formData; + return fullName && email && phoneNumber; + }; + + const validateQualifications = () => { + const { + experienceYears, + objective, + collaborationTools, + dailyHours, + availableDay, + } = formData; + return ( + experienceYears && + objective && + collaborationTools && + dailyHours && + availableDay + ); + }; + + const validatePayment = () => { + const { termsOfUse, paymentPhoneNumber } = formData; + return termsOfUse && paymentPhoneNumber; + }; + + const handleContinue = async () => { + if (isProcessing) return; + setIsProcessing(true); + + const hasInputData = Object.values(formData).some( + (value) => value !== "" || typeof value === "boolean" + ); + + if (!hasInputData) { + alert("Please fill all required fields"); + setIsProcessing(false); + return; + } + + let isValid = false; + if (index === 1) { + isValid = validatePersonalInfo(); + if (!isValid) { + alert("Please fill all required fields in Personal Information"); + setIsProcessing(false); + return; + } + } else if (index === 2) { + isValid = validateQualifications(); + if (!isValid) { + alert("Please fill all required fields in Qualifications"); + setIsProcessing(false); + return; + } + } else if (index === 3) { + isValid = validatePayment(); + if (!isValid) { + alert("Please accept the terms of use and enter a valid phone number"); + setIsProcessing(false); + return; + } + } + + if (index < 3) { + setIndex((prev) => prev + 1); + setIsProcessing(false); + return; + } + + if (index === 3) { + try { + enrollMastercraft(reformattedFormData, { + onSuccess: (data) => { + console.log("Enrollment successful:", data); + if (!data && data.message !== "Payment initiated successfully") { + alert("Enrollment successful but couldn't track payment status"); + return; + } + setEnrollmentId(data.enrollment_id); + setPaymentInitiated(true); + openModal(); + }, + onError: (error) => { + console.error("Enrollment error:", error); + alert( + error?.response?.data?.message || "An unexpected error occurred" + ); + }, + }); + } catch (error) { + console.error("Error during enrollment process:", error); + // alert("An unexpected error occurred"); + } + } + + setTimeout(() => setIsProcessing(false), 300); + }; + + const handleBack = () => { + if (isProcessing) return; + setIsProcessing(true); + if (index > 1) { + setIndex((prev) => prev - 1); + } + setTimeout(() => setIsProcessing(false), 300); + }; + + const handleRetryPayment = () => { + closeModal(); + }; + + const renderFormStep = () => { + switch (index) { + case 1: + return ( + + ); + case 2: + return ( + + ); + case 3: + return ( + + ); + default: + return null; + } + }; + + return ( +
+
+ + + + {renderFormStep()} + + {index === 3 && ( + handleInputChange("termsOfUse", checked)} + /> + )} + + + + +
+
+ ); +} + +export default MastercraftEnrollmentComponent; diff --git a/src/pages/mastercraft-enroll/sections/Payment.jsx b/src/pages/mastercraft-enroll/sections/Payment.jsx new file mode 100644 index 00000000..75e2f2b0 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/Payment.jsx @@ -0,0 +1,83 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable react/prop-types */ +import { Palette } from "lucide-react"; +import PhoneInput from "react-phone-number-input"; + +function Payment({ formData, handleInputChange, programTitle, pricing }) { + return ( +
+

+ Payment Information +

+ +
+
+
+
+
+ +
+
{programTitle}
+

+ Mastercraft Program ● 8 weeks +

+
+
+ +
+

+ KES{" "} + {new Intl.NumberFormat("en-US", { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + }).format(Number(pricing.fullAmount - pricing.discountedAmount))} +

+
+
+ +
+ + handleInputChange("paymentPhoneNumber", value)} + type="text" + name="paymentPhoneNumber" + id="paymentPhoneNumber" + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + /> +

+ You will receive a payment prompt on this number +

+
+ +
+ + handleInputChange("promoCode", e.target.value)} + className="p-3 border border-[#BAC6C3] rounded-lg text-sm text-[#2E2E2E] bg-white" + placeholder="Enter promo code if available" + /> +
+
+
+ ); +} + +export default Payment; diff --git a/src/pages/mastercraft-enroll/sections/PaymentStatusModal.jsx b/src/pages/mastercraft-enroll/sections/PaymentStatusModal.jsx new file mode 100644 index 00000000..c85b27ee --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/PaymentStatusModal.jsx @@ -0,0 +1,292 @@ +/* eslint-disable react/prop-types */ +import { + Description, + Dialog, + DialogPanel, + DialogTitle, + Transition, + TransitionChild, +} from "@headlessui/react"; +import { XCircle, Clock, AlertTriangle } from "lucide-react"; +import { Fragment, useEffect } from "react"; +import useCheckPaymentStatus from "../../../hooks/Mutations/mastercraft/useCheckPaymentStatus"; +import CourseInfoCard from "./CourseInfoCard"; + +function PaymentStatusModal({ + isOpen, + closeModal, + enrollmentId, + setEnrollmentId, + paymentInitiated, + setPaymentInitiated, + onRetryPayment, + setFormData, + setIndex, +}) { + const { + isSuccess: paymentSuccess, + isError: paymentError, + isPending: paymentPending, + message: paymentMessage, + errorCode, + checkStatus, + // attemptsCount, + data: paymentData, + } = useCheckPaymentStatus(enrollmentId, { + enabled: paymentInitiated && enrollmentId !== null, + pollingInterval: 5000, + maxAttempts: 3, + isOpen, + }); + + const handleCheckPayment = () => { + if (enrollmentId) { + checkStatus(); + } + }; + + const getErrorMessage = () => { + if (paymentMessage === "Request cancelled by user") { + return "You cancelled the payment request. Please try again when you're ready."; + } + + if (errorCode === "TIMEOUT") { + return "The payment verification timed out. Please check if the payment was completed on your M-PESA and try checking the status again."; + } + + return ( + paymentMessage || + "There was an issue processing your payment. Please try again or contact support." + ); + }; + + useEffect(() => { + if (paymentSuccess || paymentData?.message === "Payment successful") { + setFormData({ + fullName: "", + email: "", + phoneNumber: "", + github: "", + project: "", + linkedIn: "", + objective: "", + experienceYears: "", + professionalJourney: "", + collaborationTools: "", + dailyHours: "", + referralSource: "", + availableDay: "", + paymentPhoneNumber: "", + promoCode: "", + termsOfUse: false, + }); + setIndex(1); + } + }, [paymentSuccess, paymentData, setFormData, setIndex]); + + useEffect(() => { + if (paymentInitiated && enrollmentId && isOpen) { + checkStatus(); + } + }, [paymentInitiated, enrollmentId, checkStatus, isOpen]); + + const handleRetryPayment = () => { + closeModal(); + setTimeout(() => { + onRetryPayment(); + }, 300); + }; + + const handleCancelEnrollment = () => { + setEnrollmentId(null); + setPaymentInitiated(false); + closeModal(); + }; + + useEffect(() => { + if (!isOpen) { + setEnrollmentId(null); + setPaymentInitiated(false); + } + }, [isOpen, setEnrollmentId, setPaymentInitiated]); + + return ( + + + + + + ); +} + +export default PaymentStatusModal; diff --git a/src/pages/mastercraft-enroll/sections/PersonalInformation.jsx b/src/pages/mastercraft-enroll/sections/PersonalInformation.jsx new file mode 100644 index 00000000..9d4572e2 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/PersonalInformation.jsx @@ -0,0 +1,119 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable react/prop-types */ + +import PhoneInput from "react-phone-number-input"; + +function PersonalInformation({ formData, handleInputChange }) { + return ( +
+

+ Personal Information +

+ +
+ + handleInputChange("fullName", e.target.value)} + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + placeholder="John Doe" + required + /> +
+ +
+ + handleInputChange("email", e.target.value)} + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + placeholder="johndoe@gmail.com" + required + /> +
+ +
+ + handleInputChange("phoneNumber", value)} + value={formData.phoneNumber} + type="text" + name="phoneNumber" + id="phoneNumber" + placeholder="254712345678" + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + /> +
+ +
+ + handleInputChange("github", e.target.value)} + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + placeholder="https://github.com/johndoe" + /> +
+ +
+ + handleInputChange("project", e.target.value)} + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + placeholder="https://johndoe.github.io/my-project" + /> +
+ +
+ + handleInputChange("linkedIn", e.target.value)} + className="border border-[#E5E5E5] bg-[#F4F4F5] rounded-md py-2 px-3 text-sm outline-none text-[#A3A3A3]" + placeholder="https://www.linkedin.com/in/johndoe" + /> +
+
+ ); +} +export default PersonalInformation; diff --git a/src/pages/mastercraft-enroll/sections/Qualifications.jsx b/src/pages/mastercraft-enroll/sections/Qualifications.jsx new file mode 100644 index 00000000..f412cac8 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/Qualifications.jsx @@ -0,0 +1,146 @@ +/* eslint-disable react/prop-types */ +const qualificationsForm = [ + { + id: "objective", + question: "What is your objective for joining the Mastercraft program?", + options: [ + { value: "", label: "--" }, + { value: "objective-1", label: "I want to enhance my technical skills" }, + { + value: "objective-2", + label: "I want to get work experience to land tech roles", + }, + { + value: "objective-3", + label: + "I am interested in working in a team of peers and other techies", + }, + { value: "objective-4", label: "I want to learn how to code or design" }, + ], + }, + { + id: "experienceYears", + question: "How many years of experience do you have in your field?", + options: [ + { value: "", label: "--" }, + { value: "experience-1", label: "Less than a year" }, + { value: "experience-2", label: "2-3 years" }, + { value: "experience-3", label: "4-6 years" }, + { value: "experience-4", label: "More than 6 years" }, + ], + }, + { + id: "professionalJourney", + question: + "Which of the following have you completed as part of your professional or learning journey?", + options: [ + { value: "", label: "--" }, + { + value: "journey-1", + label: + "I currently have a role in industry and is looking to advance my skills", + }, + { value: "journey-2", label: "I have at least one portfolio project" }, + { + value: "journey-3", + label: "I have a working product that I have founded", + }, + { + value: "journey-4", + label: + "I have no experience building projects but I know the basics of my tech stack", + }, + ], + }, + { + id: "collaborationTools", + question: + "How familiar are you with collaboration tools commonly used in development teams? (Git, Figma, Jira, Trello and Slack)?", + options: [ + { value: "", label: "--" }, + { + value: "tools-1", + label: + "I have no experience working with version control tools but I want to learn", + }, + { + value: "tools-2", + label: + "I have a basic understanding of how to use version control tools", + }, + { + value: "tools-3", + label: + "I am comfortable with version control tools but want to learn more", + }, + { + value: "tools-4", + label: "I have working experience with version control tools", + }, + ], + }, + { + id: "dailyHours", + question: "How many hours in a day can you allocate to this program?", + options: [ + { value: "", label: "--" }, + { value: "hours-1", label: "Less than 2 hours a day" }, + { value: "hours-2", label: "2-4 hours a day" }, + { value: "hours-3", label: "4-8 hours a day" }, + { value: "hours-4", label: "More than 8 hours a day" }, + ], + }, + { + id: "referralSource", + question: "How did you hear about us?", + options: [ + { value: "", label: "--" }, + { value: "referral-1", label: "Twitter (X)" }, + { value: "referral-2", label: "LinkedIn" }, + { value: "referral-3", label: "Through a friend" }, + { value: "referral-4", label: "At a SpaceYaTech event" }, + ], + }, + { + id: "availableDay", + question: "Which day of the week are you available for an online stand-up?", + options: [ + { value: "", label: "--" }, + { value: "day-1", label: "Mondays" }, + { value: "day-2", label: "Tuesdays" }, + { value: "day-3", label: "Wednesdays" }, + { value: "day-4", label: "Thursdays" }, + { value: "day-5", label: "Fridays" }, + ], + }, +]; + +function Qualifications({ formData, handleInputChange }) { + return ( +
+

Qualifications

+ + {qualificationsForm.map(({ id, question, options }) => ( +
+ + +
+ ))} +
+ ); +} +export default Qualifications; diff --git a/src/pages/mastercraft-enroll/sections/TermsOfUseCheckbox.jsx b/src/pages/mastercraft-enroll/sections/TermsOfUseCheckbox.jsx new file mode 100644 index 00000000..89cca786 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/TermsOfUseCheckbox.jsx @@ -0,0 +1,49 @@ +import PropTypes from "prop-types"; + +function TermsOfUseCheckbox({ checked, onChange }) { + return ( +
+
+ onChange(e.target.checked)} + aria-label="Accept terms of use" + /> + +
+ +
+ ); +} + +export default TermsOfUseCheckbox; + +TermsOfUseCheckbox.propTypes = { + checked: PropTypes.bool.isRequired, + onChange: PropTypes.func.isRequired, +}; diff --git a/src/pages/mastercraft-enroll/sections/validationSchema.js b/src/pages/mastercraft-enroll/sections/validationSchema.js new file mode 100644 index 00000000..65e4ab84 --- /dev/null +++ b/src/pages/mastercraft-enroll/sections/validationSchema.js @@ -0,0 +1,31 @@ +import { object, string, number, array, boolean } from "yup"; + +const validationSchema = object().shape({ + fullName: string().required("Full name is required"), + email: string() + .email("Invalid email address") + .required("Email address is required"), + phoneNumber: string().required("Phone number is required"), + github: string().url("Invalid GitHub URL"), + project: string().url("Invalid project URL"), + linkedIn: string().url("Invalid LinkedIn URL"), + objective: string().required("Objective is required"), + experienceYears: number().required("Experience years is required"), + professionalJourney: string().required("Professional journey is required"), + collaborationTools: string().required("Collaboration tools is required"), + dailyHours: number().required("Daily hours is required"), + referralSource: string().required("Referral source is required"), + availableDay: string().required("Available day is required"), + paymentPhoneNumber: string().required("Payment phone number is required"), + promoCode: string(), + termsOfUse: boolean().oneOf([true], "Terms of use must be accepted"), + qualifications: array().of( + object().shape({ + qualification: string().required("Qualification is required"), + experience: number().required("Experience is required"), + }) + ), + paymentMethod: string().required("Payment method is required"), +}); + +export default validationSchema; diff --git a/src/pages/mastercraft-home/MastercraftHome.jsx b/src/pages/mastercraft-home/MastercraftHome.jsx index 10a13998..6741e2ea 100644 --- a/src/pages/mastercraft-home/MastercraftHome.jsx +++ b/src/pages/mastercraft-home/MastercraftHome.jsx @@ -5,20 +5,26 @@ import { FAQSection, Features, HeroSection, + Mentors, Stats, - StudentShowcase, + // StudentShowcase, WhatWeOffer, + XFeedback, } from "./sections"; const components = [ { - title: "our impact", - component: , + title: "our mentors", + component: , }, { - title: "student showcase", - component: , + title: "our impact", + component: , }, + // { + // title: "student showcase", + // component: , + // }, { title: "faq", component: , @@ -44,6 +50,7 @@ function MastercraftHome() { + {components.map(({ component, title }) => ( {component} diff --git a/src/pages/mastercraft-home/sections/FAQSection.jsx b/src/pages/mastercraft-home/sections/FAQSection.jsx index b5c3cd5e..bae5d84f 100644 --- a/src/pages/mastercraft-home/sections/FAQSection.jsx +++ b/src/pages/mastercraft-home/sections/FAQSection.jsx @@ -1,5 +1,5 @@ -import { FAQ } from "../../../components"; import { questions } from "./data"; +import MastercraftFAQAccordion from "./MastercraftFAQAccordion"; function FAQSection() { return ( @@ -10,7 +10,7 @@ function FAQSection() {
- + ); } diff --git a/src/pages/mastercraft-home/sections/Features.jsx b/src/pages/mastercraft-home/sections/Features.jsx index 21aa82bd..1de2e466 100644 --- a/src/pages/mastercraft-home/sections/Features.jsx +++ b/src/pages/mastercraft-home/sections/Features.jsx @@ -13,7 +13,7 @@ function Features() {

Quickly grasp the essentials and get your feet wet with our introductory course and jump straight into real world projects to - enable you to understand the basics alongside their implmentation + enable you to understand the basics alongside their implmentation{" "}

@@ -56,7 +56,9 @@ function Features() {
-

Foundations

+

+ Project-based +

Lay the groundwork for the coming weeks with weekly learning @@ -103,7 +105,7 @@ function Features() {

- Build on Foundations + Flexible and remote

@@ -145,7 +147,9 @@ function Features() {

-

Internship

+

+ Mentorship by Industry Experts +

Nurture your skills with first-hand experience, working diff --git a/src/pages/mastercraft-home/sections/HeroSection.jsx b/src/pages/mastercraft-home/sections/HeroSection.jsx index 21be91d5..655385c3 100644 --- a/src/pages/mastercraft-home/sections/HeroSection.jsx +++ b/src/pages/mastercraft-home/sections/HeroSection.jsx @@ -1,7 +1,7 @@ import React from "react"; import { LazyLoadImage } from "react-lazy-load-image-component"; -import heroImg from "../../../assets/images/mastercraft/mastercraft-hero.png"; +import heroImg from "../../../assets/images/mastercraft/mastercraft-hero.svg"; import img3 from "../../../assets/Landing Page Images/Ellipse 127.png"; import img2 from "../../../assets/Landing Page Images/Ellipse 128.png"; import img4 from "../../../assets/Landing Page Images/Ellipse 148.png"; @@ -10,13 +10,13 @@ import img from "../../../assets/Landing Page Images/Ellipse 159.png"; function HeroSection() { return (

-
-
+
+

mastercraft program

-

+

Fast-track your path to the @@ -25,9 +25,10 @@ function HeroSection() {

-

- Learn from the best teachers in the Kenyan tech market, horning your - craft with our completely free and comprehensive learning programme. +

+ Join us for an intensive 8-week, team-based training program that + immerses you in a collaborative project while you get expert + mentorship.

@@ -47,9 +48,14 @@ function HeroSection() {

- Trusted by 600+ students + 600+ graduates to date

+ + {/*
+ + Intake closes on 29th May, 2025 +
*/}
{ + setActiveQuestion(activeQuestion === index ? null : index); + }; + + return ( +
+ {questions.map( + ({ id, title, description, description2, list }, index) => ( +
+
+ +
+
+ {!list ? ( +

+ {description} +

+ ) : ( +
+

{description}

+ +
    + {list.map((li) => ( +
  • {li}
  • + ))} +
+ + {description2 &&

{description2}

} +
+ )} +
+
+ ) + )} +
+ ); +} + +export default MastercraftFAQAccordion; diff --git a/src/pages/mastercraft-home/sections/Mentors.jsx b/src/pages/mastercraft-home/sections/Mentors.jsx new file mode 100644 index 00000000..d3076f08 --- /dev/null +++ b/src/pages/mastercraft-home/sections/Mentors.jsx @@ -0,0 +1,45 @@ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +import { InstructorsComponent } from "@/pages/mastercraft/sections"; +import { programs } from "@/pages/mastercraft/sections/data"; + +function Mentors() { + const mentors = programs.map(({ mentors: men }) => men).flat(); + + return ( +
+

+ Get mentorship from our selected industry experts +

+
2 ? "scrollbar-2" : "justify-center"} py-6 w-[80%] md:w-[90%]`} + > + {mentors.map( + ({ + id, + name, + role, + organisation, + img, + experience, + linkedin, + twitter, + }) => ( + + ) + )} +
+
+ ); +} + +export default Mentors; diff --git a/src/pages/mastercraft-home/sections/WhatWeOffer.jsx b/src/pages/mastercraft-home/sections/WhatWeOffer.jsx index b241cf71..72f1632d 100644 --- a/src/pages/mastercraft-home/sections/WhatWeOffer.jsx +++ b/src/pages/mastercraft-home/sections/WhatWeOffer.jsx @@ -45,6 +45,7 @@ function WhatWeOffer() { {selected === 2 && } {selected === 3 && } {selected === 4 && } + {/* {selected === 5 && } */}
diff --git a/src/pages/mastercraft-home/sections/XFeedback.jsx b/src/pages/mastercraft-home/sections/XFeedback.jsx new file mode 100644 index 00000000..f9be2011 --- /dev/null +++ b/src/pages/mastercraft-home/sections/XFeedback.jsx @@ -0,0 +1,27 @@ +import React from "react"; + +import x from "../../../assets/images/resources-page/icons/twitter-x.svg"; + +function XFeedback() { + return ( +
+
+ twitter-x + +

+ Mastercraft is what every techie getting ready to interview for their + first role needs. The program is tailored to sharpen your technical + skills to see you ace technical and behavioral interviews. I am + speaking as a mentor in the program +

+ +
+

Belinda Koech

+ @uxbesh +
+
+
+ ); +} + +export default XFeedback; diff --git a/src/pages/mastercraft-home/sections/data.js b/src/pages/mastercraft-home/sections/data.js index bbc6d16c..91342851 100644 --- a/src/pages/mastercraft-home/sections/data.js +++ b/src/pages/mastercraft-home/sections/data.js @@ -1,25 +1,24 @@ import { + android, productDesign, frontend, backend, - dataScience, } from "../../../assets/images/resources-page"; import img2 from "../../../assets/Landing Page Images/Ellipse 127.png"; import img3 from "../../../assets/Landing Page Images/Ellipse 128.png"; import img5 from "../../../assets/Landing Page Images/Ellipse 138.png"; -import img4 from "../../../assets/Landing Page Images/Ellipse 148.png"; import img from "../../../assets/Landing Page Images/Ellipse 159.png"; export const categoriesData = [ { id: 1, - slug: "product-design", - category: "Product Design", - cover: productDesign, - tags: ["UI Design", "UX Design", "Prototyping", "Product Design"], - title: "Take your product design skills from zero to hero", + slug: "android-development", + category: "Android Development", + cover: android, + tags: ["Kotlin", "Flutter", "React Native", "Swift UI"], + title: "Android Development (Kotlin)", period: "2 months", - courseType: "Cohort-based course", + courseType: "Cohort-based program", hosts: [ { id: 1, @@ -40,10 +39,10 @@ export const categoriesData = [ slug: "frontend-development", category: "Frontend Development", cover: frontend, - tags: ["HTML", "CSS", "JS", "React", "NextJS"], - title: "Building blocks for exceptional interfaces", - period: "3 months", - courseType: "Cohort-based course", + tags: ["React Components", "Redux", "Tailwind CSS", "Hooks"], + title: "Frontend Development (React)", + period: "2 months", + courseType: "Cohort-based program", hosts: [ { id: 1, @@ -59,9 +58,9 @@ export const categoriesData = [ category: "Backend Development", cover: backend, tags: ["APIs", "Databases", "SQL", "JWT and Backend Safety"], - title: "Backend basics for advanced systems", - period: "5 months", - courseType: "Cohort-based course", + title: "Backend Development (Django)", + period: "2 months", + courseType: "Cohort-based program", hosts: [ { id: 1, @@ -73,52 +72,111 @@ export const categoriesData = [ }, { id: 4, - slug: "data-science-and-analytics", - category: "Data Science and Analytics", - cover: dataScience, - tags: ["Python", "R", "Machine Learning", "Data Analysis"], - title: "Data Science with Superpowers", - period: "6 months", - courseType: "Cohort-based course", + slug: "product-design", + category: "Product Design", + cover: productDesign, + tags: ["UI Design", "UX Design", "Prototyping", "Product Design"], + title: "Product Design", + period: "2 months", + courseType: "Cohort-based program", hosts: [ { id: 1, - headshot: img4, - name: "Juma Lawrence", - role: "Senior Data Scientist at SpaceYaTech", + headshot: img, + name: "Emmy Akinyi", + role: "Senior Product Designer at SpaceYaTech", + }, + { + id: 2, + headshot: img2, + name: "Pamela Owino", + role: "Senior Product Designer at SpaceYaTech", }, ], }, + // { + // id: 5, + // slug: "data-science-and-analytics", + // category: "Data Science and Analytics", + // cover: dataScience, + // tags: ["Python", "R", "Machine Learning", "Data Analysis"], + // title: "Data Science and Analytics", + // period: "6 months", + // courseType: "Cohort-based course", + // hosts: [ + // { + // id: 1, + // headshot: img4, + // name: "Juma Lawrence", + // role: "Senior Data Scientist at SpaceYaTech", + // }, + // ], + // }, ]; export const questions = [ { id: 1, - question: "Is SpaceYaTech free?", - answer: - "Yes, SpaceYaTech is totally free for anyone who wishes to learn technology and contribute to Open Source", + title: "What is Mastercraft?", + description: + "Mastercraft is an 8-week immersive training program that helps aspiring software engineers and product designers become job-ready through real-world, team-based project experience. You’ll work alongside peers and mentors in a simulated tech team—just like in the industry.", }, { id: 2, - question: "Does SpaceYaTech only mentor developers?", - answer: - "No, SpaceYaTech mentors anyone who is involved in modern technology. This includes developers, designers, product managers, and more. SpaceYaTech believes that everyone has the potential to learn and grow in the tech industry, and they are committed to providing mentorship to anyone who wants it.", + title: "Who is this program for?", + description: + "Mastercraft is ideal for junior or self-taught developers/designers looking to sharpen their skills, build real experience, and grow confident in a team. If you know the basics and want to level up for jobs or internships—this is for you.", }, { id: 3, - question: "Does SpaceYaTech pay mentors?", - answer: - "No, SpaceYaTech does not pay mentors. However, mentors do receive a number of benefits.", + title: "What tracks are available?", + description: + "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", + list: [ + "UI/UX Design", + "Frontend Development (React)", + "Backend Development (Django)", + "Android Development (Kotlin)", + "Data Science and Analytics", + ], + description2: + "Each track works as part of a cross-functional team on the same product", }, { id: 4, - question: "Can I collaborate with SpaceYaTech?", - answer: "Yes, you can collaborate with SpaceYaTech in a number of ways.", + title: "Is Mastercraft remote or on-site?", + description: + "This program is completely remote, with a possibility of physical meets should the organizers feel it is necessary. The program takes 8-weeks, which are packed with tasks assigned by a PM and key deliverables.", + list: [ + "Team collaboration sessions.", + "Weekly mentorship check-ins.", + "Practical tasks based on your track.", + "Design and development milestones.", + "End-of-program demo and mock interviews", + ], }, { id: 5, - question: "Can I join the mentorship sessions at any time?", - answer: - "No, you cannot join the mentorship sessions at any time. SpaceYaTech's mentorship sessions are offered on a rolling basis, and there are limited spots available. To be considered for a mentorship session, you must submit an application and be selected by SpaceYaTech.", + title: "Will I get a certificate?", + description: + "Yes! Trainees who successfully complete the program and present their final project receive a certificate of completion and a portfolio-ready case study/project link to showcase to employers.", + }, + { + id: 6, + title: "Is this a job placement program?", + description: + "Mastercraft is not a direct job placement program, but it equips you with the experience, confidence, and portfolio projects that make you far more competitive for internships, freelance, and entry-level tech roles.", + }, + { + id: 7, + title: "Is the program free?", + description: + "No, the Mastercraft program is not free. It costs KES 3500 for residents ans citizens of Kenya and $30 for international students. The KES 3,500 covers the entire 8-week program, including live mentorship, training resources, career guidance, team project work, and access to a collaborative Slack community. There are no additional or hidden costs.", + }, + { + id: 8, + title: "How do I get started?", + description: + "Once you pay and sign up, you’ll receive a welcome email with your track details and next steps—including joining our Slack channel, where you'll meet your mentor and team members.", }, ]; diff --git a/src/pages/mastercraft-home/sections/index.js b/src/pages/mastercraft-home/sections/index.js index 8e00f6d1..a2963770 100644 --- a/src/pages/mastercraft-home/sections/index.js +++ b/src/pages/mastercraft-home/sections/index.js @@ -1,6 +1,8 @@ export { default as FAQSection } from "./FAQSection"; export { default as Features } from "./Features"; export { default as HeroSection } from "./HeroSection"; +export { default as Mentors } from "./Mentors"; export { default as Stats } from "./Stats"; export { default as StudentShowcase } from "./StudentShowcase"; export { default as WhatWeOffer } from "./WhatWeOffer"; +export { default as XFeedback } from "./XFeedback"; diff --git a/src/pages/mastercraft/Mastercraft.jsx b/src/pages/mastercraft/Mastercraft.jsx index 8af178c6..f49c6ccd 100644 --- a/src/pages/mastercraft/Mastercraft.jsx +++ b/src/pages/mastercraft/Mastercraft.jsx @@ -1,8 +1,15 @@ import React from "react"; +import { useLocation } from "react-router-dom"; import SeoMetadata from "../../components/SeoMetadata"; -import { Description, Header, Portfolio } from "./sections"; +import { Description, Header } from "./sections"; +import { programs } from "./sections/data"; function Mastercraft() { + const { pathname } = useLocation(); + const program = pathname.split("/").pop(); + + const programRender = programs.find(({ slug }) => slug === program); + return ( <>
-
- - +
+ + {/* */}
diff --git a/src/components/CurriculumAccordion.jsx b/src/pages/mastercraft/sections/CurriculumAccordion.jsx similarity index 51% rename from src/components/CurriculumAccordion.jsx rename to src/pages/mastercraft/sections/CurriculumAccordion.jsx index 56ef5ba7..24cf7174 100644 --- a/src/components/CurriculumAccordion.jsx +++ b/src/pages/mastercraft/sections/CurriculumAccordion.jsx @@ -1,46 +1,9 @@ +/* eslint-disable react/prop-types */ import { useState } from "react"; import { FiPlusCircle, FiMinusCircle } from "react-icons/fi"; -function CurriculumAccordion() { +function CurriculumAccordion({ milestones = [] }) { const [activeQuestion, setActiveQuestion] = useState(null); - const questions = [ - { - id: 1, - title: "Introduction to Product Design", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - { - id: 2, - title: "UX Research", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - { - id: 3, - title: "IA & Ideation", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - { - id: 4, - title: "User Flows & Wireframes", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - { - id: 5, - title: "Color & Typography", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - { - id: 6, - title: "UI Components", - description: - "Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.", - }, - ]; const toggleQuestion = (index) => { setActiveQuestion(activeQuestion === index ? null : index); @@ -48,14 +11,14 @@ function CurriculumAccordion() { return (
- {questions.map((question, index) => ( + {milestones.map(({ week, header, desc, deliverables }, index) => (
-

+
-

+
-

+ {/*

{question.description} -

+

*/} +
+
✅ Deliverable: {desc}
+ +
    + {deliverables.map((deliverable) => ( +
  • {deliverable}
  • + ))} +
+
))} @@ -110,17 +81,3 @@ function CurriculumAccordion() { } export default CurriculumAccordion; - -// CurriculumAccordion.propTypes = { -// questions: PropTypes.arrayOf( -// PropTypes.shape({ -// id: PropTypes.number, -// question: PropTypes.string, -// answer: PropTypes.string, -// }) -// ), -// }; - -// CurriculumAccordion.defaultProps = { -// questions: [], -// }; diff --git a/src/pages/mastercraft/sections/Description.jsx b/src/pages/mastercraft/sections/Description.jsx index bbb2fed7..d68c33e5 100644 --- a/src/pages/mastercraft/sections/Description.jsx +++ b/src/pages/mastercraft/sections/Description.jsx @@ -1,16 +1,29 @@ +/* eslint-disable react/prop-types */ import React from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import CurriculumAccordion from "./CurriculumAccordion"; import InstructorsComponent from "./InstructorsComponent"; -import NextCohortSlot from "./NextCohortSlot"; -// eslint-disable-next-line import/extensions, import/no-unresolved -import { CurriculumAccordion } from "@/components"; -// eslint-disable-next-line import/extensions, import/no-unresolved -import { LeadershipData } from "@/pages/aboutUs/data"; - -function Description() { +// import NextCohortSlot from "./NextCohortSlot"; + +function Description({ + category, + mentors, + milestones, + pricing, + programDescription, + programOutcomes, + whatYouWillLearn, + whoCanApply, +}) { const defaultFocusedLinkRef = React.useRef(null); const [subscription, setSubscription] = React.useState("now"); + const navigate = useNavigate(); + const { pathname } = useLocation(); + const words = pathname.split("/"); + const topic = words[words.length - 1]; + React.useEffect(() => { if (defaultFocusedLinkRef.current) { defaultFocusedLinkRef.current.focus(); @@ -23,20 +36,31 @@ function Description() { element.scrollIntoView({ behavior: "smooth" }); } }; + + const programPrice = new Intl.NumberFormat("en-US", { + style: "currency", + currency: pricing.currency, + }).format(pricing.fullAmount); + const discountedPrice = new Intl.NumberFormat("en-US", { + style: "currency", + currency: pricing.currency, + }).format(Number(pricing.fullAmount - pricing.discountedAmount)); + return (
-
+
+ -
- {/* Course Description */} -
+
@@ -141,82 +164,87 @@ function Description() {

What you will learn

-
    -
  • - Understand the basics of UX research, like planning research - studies, conducting interviews and usability studies, and - synthesizing research results -
  • -
  • - Apply foundational UX concepts, like user-centered design, - accessibility, and equity-focused design -
  • -
  • - Follow the design process: empathize with users, define pain - points, ideate solutions, create wireframes and prototypes, test - and iterate on designs -
  • -
  • - Create a professional UX portfolio that includes 3 end-to-end - projects: a mobile app, a responsive website, and a cross-platform - experience -
  • +
      + {whatYouWillLearn.map(({ content, subTitle }) => ( +
    • +

      + {subTitle && {subTitle} } + + {content} +

      +
    • + ))}

- {/* Curriculum */} -
+ {/* Milestones */} +

- Curriculum + Program milestones

- +

- {/* Course Outcome */} -
+ {/* Program Outcome */} +

- Course Outcome + Program Outcomes

-
    -
  • Lorem ipsum dolor sit amet
  • -
  • Lorem ipsum dolor sit amet
  • -
  • Lorem ipsum dolor sit amet
  • -
  • Lorem ipsum dolor sit amet
  • +
      + {programOutcomes.map(({ content, subTitle }) => ( +
    • +

      + {subTitle && {subTitle} } + + {content} +

      +
    • + ))}

- {/* Instructors */} -
-

- Instructors -

+ {/* Mentors */} +
+

Mentors

- Each cohort is taught by a team of UI/UX professionals who are - thriving in the industry as professional product designers. The - teachers will be supported by mentors who are equally professionals - in product design. + We have carefully selected mentors, with whom you will meet for 1:1 + sessions and group sessions, to unblock you and guide you on best + practices.

- {LeadershipData.map(({ name, title, image, linkedin, twitter }) => ( - - ))} + {mentors.map( + ({ + id, + name, + role, + organisation, + img, + experience, + linkedin, + twitter, + }) => ( + + ) + )}
@@ -232,17 +260,17 @@ function Description() { and mentors to keep showing up besides passion.

-
-
+
+
-

Pay at once. Save Ksh 2000

+

Pay before {pricing.offerDeadline}

- KSH 8000 - KSH 6000 + + {programPrice} + + {discountedPrice}
@@ -281,13 +312,13 @@ function Description() {
  • - 2 weekly live classes + Team-based collaboration
  • Learn industry tools
  • - Graduate with 2 projects + Graduate with 2 real world project
  • 1:1 support from mentors @@ -299,16 +330,17 @@ function Description() {
-
+ {/*
*/} + {/* To be returned after the first cohort is complete */} {/* Next Cohort */} -
+ {/*

Next Cohort

- Our Product Design Masterclasses tend to fill up pretty quickly + Our Mastercraft program openings tend to fill up pretty quickly which is why we open them months in advance. Live Classes are 90 minutes on Thursday and Saturday from 9:00 pm - 10:00pm.

@@ -317,10 +349,10 @@ function Description() {
-
+
*/} -
-
+ {/*
*/} +
); } diff --git a/src/pages/mastercraft/sections/Header.jsx b/src/pages/mastercraft/sections/Header.jsx index 373d7eb1..5f0d0157 100644 --- a/src/pages/mastercraft/sections/Header.jsx +++ b/src/pages/mastercraft/sections/Header.jsx @@ -1,73 +1,78 @@ /* eslint-disable react/prop-types */ import React from "react"; import { LazyLoadImage } from "react-lazy-load-image-component"; -import { - award, - calendar, - dotpoints, - folder, -} from "../../../assets/images/icons"; -import workers from "../../../assets/images/mastercraft/workers.png"; +import { useLocation, useNavigate } from "react-router-dom"; + +function Header({ + isRegOpen, + category, + cover, + description, + nextCohortDate, + productInfo, + tags, + title, +}) { + const { pathname } = useLocation(); + const isEnroll = pathname.endsWith("enroll"); + const words = pathname.split("/"); + const topic = words[words.length - 1]; + const navigate = useNavigate(); -function Header() { return (
{/* Left */}

- product design + {category}

-
-
Registration is - open +
+
{" "} + Registration is {isRegOpen ? "open" : "closed"}
-

- Introduction to Product Design -

+

{title}

-

- Jumpstart your UI/UX design career with out comprehensive 2-month - boot camp on user experience and visual design. -

+

{description}

- - - - + {productInfo?.map((info) => ( + + ))}

- Next cohort opens on 15th September + Next cohort opens on {nextCohortDate}

@@ -77,24 +82,20 @@ function Header() {
-

- UX Design -

-

- UX Design -

-

- Prototyping -

-

- Product Design -

+ {tags?.map((tag) => ( +

+ {tag} +

+ ))}
diff --git a/src/pages/mastercraft/sections/InstructorsComponent.jsx b/src/pages/mastercraft/sections/InstructorsComponent.jsx index e76e4a97..a842ce35 100644 --- a/src/pages/mastercraft/sections/InstructorsComponent.jsx +++ b/src/pages/mastercraft/sections/InstructorsComponent.jsx @@ -4,27 +4,49 @@ import { FaBriefcase, FaLinkedinIn } from "react-icons/fa"; import { FaXTwitter } from "react-icons/fa6"; import { LazyLoadImage } from "react-lazy-load-image-component"; -function InstructorsComponent({ name, title, image, linkedin, twitter }) { +function InstructorsComponent({ + name, + title, + image, + linkedin, + twitter, + organisation, + experience, +}) { return (
-
-

{name}

+ ); diff --git a/src/pages/mastercraft/sections/data.js b/src/pages/mastercraft/sections/data.js new file mode 100644 index 00000000..56972728 --- /dev/null +++ b/src/pages/mastercraft/sections/data.js @@ -0,0 +1,931 @@ +/* eslint-disable quotes */ +/* eslint-disable import/extensions */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable import/prefer-default-export */ +import { + award, + calendar, + duration, + folder, +} from "../../../assets/images/icons"; +import { catherine, murabula } from "@/assets/images/aboutPage"; +import { + android, + backend, + frontend, + productDesign, +} from "@/assets/images/resources-page"; + +export const programs = [ + { + id: 1, + slug: "android-development", + category: "Android Development", + isRegOpen: true, + nextCohortDate: "30th May", + cover: android, + tags: ["Kotlin", "Flutter", "React Native", "Swift UI"], + title: "Android Development - Kotlin", + description: + "Build and deploy an Android application in a cross-functional team, while learning ecosystem best practices from seasoned mentors.", + productInfo: [ + { + icon: duration, + title: "8 weeks", + description: "8 weeks of collaborative building in a full-stack team.", + }, + { + icon: award, + title: "Beginner-Intermediate level", + description: "Recommended experience level.", + }, + { + icon: folder, + title: "2 Projects", + description: "Collaborate on two portfolio projects.", + }, + { + icon: calendar, + title: "Flexible schedules", + description: "Remote, work from home.", + }, + ], + // description + programDescription: + "Step into the world of Android development with our immersive 8-week program. Join a real full-stack tech team where you’ll work shoulder-to-shoulder with UI/UX designers, backend engineers, PMs, QAs, and fellow Android trainees. Together, you’ll build real-world mobile apps that powers your portfolio and proves your technical skills. Along the way, sharpen your resume, prep for interviews, and grow your professional toolkit. By the end, you’ll be ready to launch your career as a confident, job-ready Android developer.", + whoCanApply: [ + "Students or recent graduates interested in full-stack web development.", + "Self-taught junior or beginner developers seeking real-world experience working in teams.", + "Intermediate developers aiming to enhance their portfolio with deployed full-stack projects.", + "Developers from other domains looking to transition into web development.", + ], + whatYouWillLearn: [ + { + subTitle: "Team-Based Web Development:", + content: + "Collaborate in a real product team alongside designers, frontend/backend developers, PMs, and QAs to build a real-world web app.", + }, + { + subTitle: "Practical Web Development Skills:", + content: + "Gain hands-on experience with modern web technologies like React, Node.js, REST APIs, and cloud deployment.", + }, + { + subTitle: "Career & Interview Readiness:", + content: + "Refine your resume, practice mock interviews, and build confidence presenting your project to future employers.", + }, + ], + milestones: [ + { + week: 1, + header: "Ideation, Research & Planning", + desc: "Basic user flows, wireframes, architecture skeletons, tech stack finalized.", + deliverables: [ + "Kickoff meeting: Introduce team roles and the product idea.", + "UI/UX Team: Start gathering user requirements and sketch early low-fidelity wireframes.", + "PM: Scope down an MVP (must-have features only).", + "Backend: Define basic API requirements (login, fetch/save data).", + "Android Frontend: Set up Android project repo, define initial architecture (MVVM/Clean Architecture).", + "QA: Set up QA strategy (how testing will be done).", + ], + }, + { + week: 2, + header: "Ideation, Research & Planning", + desc: "Approved UI designs, first working API endpoints, Android skeleton screens.", + deliverables: [ + "UI/UX Team: Deliver full hi-fidelity mockups + a prototype.", + "Backend: Build database models, setup API endpoints for authentication and basic CRUD.", + "Android Frontend: Set up base screens (login, signup, home).", + "QA: Begin writing early test cases for APIs and UI.", + ], + }, + { + week: 3, + header: "Authentication and Core Infrastructure", + desc: "Working user login/signup integrated on device.", + deliverables: [ + "Backend: Finalize user authentication APIs.", + "Android Frontend: Integrate user registration/login flows using APIs.", + "Backend & Frontend: Set up Postman documentation / API handover docs.", + 'PM: Organize internal demos every Friday ("demo days").', + ], + }, + { + week: 4, + header: "Core Features Development Sprint✅", + desc: "Working core user flow — user can sign in and interact with main app content.", + deliverables: [ + "Backend: Build and deliver main feature APIs (e.g., posting content, fetching lists).", + "Android Frontend: Build UI screens and logic for main features (fetching data, displaying lists, detail screens).", + "QA: Test API endpoints and early Android app flows.", + ], + }, + { + week: 5, + header: "Extended Features + Edge Cases✅", + desc: "Extended user journeys complete.", + deliverables: [ + "Backend: Secondary features (profile management, settings).", + "Android Frontend: Secondary screens (profile, edit settings, etc).", + "Android Frontend: Add better state management (loading states, errors).", + "UI/UX: Polish UX based on internal feedback.", + ], + }, + { + week: 6, + header: "Testing, Polish & Refinement✅", + desc: "App usable from start to finish, even if not perfect.", + deliverables: [ + "Full bug bash week!", + "Android Frontend: UI polish, add basic animations, user feedback (toasts, snackbars).", + "Backend: Clean up APIs, optimize performance.", + "QA: Full testing cycle starts — bug reports created daily.", + "PM: Track all bugs in one place (e.g., Jira, Trello).", + ], + }, + { + week: 7, + header: "Final Integrations + Deployment Prep✅", + desc: 'A "release candidate" version of the app.', + deliverables: [ + "Android Frontend: Fix bugs, integrate all feedback.", + "Backend: Stabilize, prepare for possible public deployment (even if only demo).", + "PM + Android + Backend: Prepare demo data, staging environment.", + "QA: Final test passes.", + ], + }, + { + week: 8, + header: "Soft Launch + Internal Demos✅", + desc: "Finished app ready to be shown on Demo Day.", + deliverables: [ + "Dry run of presentations.", + "Final fixes.", + "PM + UI/UX + Android + Backend: Team does internal presentations to leadership/mentors.", + 'Mock "real-world" scenarios: bug report drills, unexpected feedback sessions.', + ], + }, + { + week: 9, + header: "Demo Day & Mock Interviews✅", + desc: "Finished app ready to be shown on Demo Day.", + deliverables: [ + "Morning: Team app presentations to mentors/guests.", + "Afternoon: Individual mock interviews (tech questions, behavioral questions, demoing your project).", + ], + }, + ], + // whatYouWillLearn: [ + // { + // subTitle: "UI/UX Design Foundations:", + // content: + // "Master user research, wireframing, prototyping, usability testing, and responsive design for both mobile and web.", + // }, + // { + // subTitle: "Design-to-Code Collaboration:", + // content: + // "Learn how to work closely with developers and hand off designs that are production-ready, including a basic understanding of frontend principles (HTML/CSS/React basics).", + // }, + // { + // subTitle: "Professional Practice:", + // content: + // "Build real-world team experience in agile environments — design critiques, sprint planning, stakeholder presentations, and feedback integration", + // }, + // ], + programOutcomes: [ + { + subTitle: "Job Readiness: ", + content: + "You'll walk away having built an Android app the real way — collaborating, compromising, debugging, and launching like a true product engineer.", + }, + { + subTitle: "Team Confidence: ", + content: + "You’ll gain the confidence to work inside any tech team — understanding not just your role, but how to support designers, PMs, QAs, and backend engineers", + }, + { + subTitle: "Technical Capacity: ", + content: + "You’ll master the Android fundamentals recruiters want: clean code, scalable architecture, API integration, testing, and mobile best practices.", + }, + { + subTitle: "Career Momentum: ", + content: + "Career Momentum: You won't leave with just skills; you'll leave with a personal growth plan, a portfolio app, mentorship feedback, and next steps toward landing your first Android developer role.", + }, + ], + mentors: [ + { + id: 1, + name: "Rachel Murabula", + role: "Android Engineer", + organisation: "SpaceYaTech", + img: murabula, + experience: 4, + linkedin: { + href: "https://www.linkedin.com/in/rachel-murabula/", + username: "Rachel Murabula", + }, + twitter: { + href: "", + username: "", + }, + }, + { + id: 2, + name: "Catherine Kiiru", + role: "Dev Relations & Opensource Programs", + organisation: "Mastercraft", + img: catherine, + experience: 3, + linkedin: { + href: "https://www.linkedin.com/in/catherine-kiiru-47b2688b/", + username: "Catherine Kiiru", + }, + twitter: { + href: "https://x.com/catetherinekiiru", + username: "catetherinekiiru", + }, + }, + ], + pricing: { + fullAmount: 3500, + discountPercentage: 20, + discountedAmount: 700, + currency: "KES", + offerDeadline: "27th April", + }, + }, + { + id: 2, + slug: "backend-development", + category: "Backend Development", + isRegOpen: false, + nextCohortDate: "30th May", + cover: backend, + tags: ["REST API", "Databases", "JWT", "Django"], + title: "Backend Development - Django", + description: + "Build and deploy a backend application in a cross-functional team, while learning ecosystem best practices from seasoned mentors.", + productInfo: [ + { + icon: duration, + title: "8 weeks", + description: "8 weeks of collaborative building in a backend team.", + }, + { + icon: award, + title: "Beginner-Junior level", + description: "Recommended experience level.", + }, + { + icon: folder, + title: "1 Project", + description: "Collaborate and build one portfolio project.", + }, + { + icon: calendar, + title: "Flexible schedules", + description: "Remote, work from home.", + }, + ], + // description + programDescription: + "This 8-week Backend Engineering program with Python Django will see you work as part of a real full-stack tech team alongside UI/UX designers, frontend engineers, PMs, QAs, and fellow backend trainees. Build a real-world project that strengthens your resume and portfolio, while gaining practical, collaborative experience. Beyond coding, receive mentorship in resume writing, interview preparation, and professional development. This program is designed to help you grow into a confident, job-ready backend developer with hands-on team-based experience.", + whoCanApply: [ + "Aspiring backend developers with basic Python knowledge.", + "Recent graduates or self-taught learners looking for real-world experience.", + "Individuals eager to learn and work collaboratively in cross-functional teams.", + "People with a growth mindset and commitment to completing an intensive 8-week program.", + ], + whatYouWillLearn: [ + { + content: + "Master the fundamentals of Python Django, including models, views, authentication, APIs, and deployment.", + }, + { + content: + "Gain hands-on experience working in a real agile team with designers, frontend engineers, QAs, and PMs.", + }, + { + content: + "Learn version control (Git), code reviews, debugging, and writing clean, scalable code in production-like environments.", + }, + ], + milestones: [ + { + week: 1, + header: "Orientation & Environment Setup", + desc: "Local dev environment set up, GitHub repo access, initial ERD draft.", + deliverables: [ + "Intro to project scope and team structure", + "Set up Python, Django, virtual environments, and Git", + "Connect to collaborative tools (Slack, GitHub, Notion, etc.)", + "Intro to Agile & sprint planning", + "Begin exploring the app's data model", + ], + }, + { + week: 2, + header: "Database Design & Models", + desc: "Complete models, migrations, updated ERD, and test data populated.", + deliverables: [ + "Finalize data schema in collaboration with UI/UX and PM", + "Create Django models for all core entities", + "Write initial migration files and apply them", + "Discuss API contract with frontend and mobile teams", + ], + }, + { + week: 3, + header: "REST API Foundations", + desc: "Working CRUD APIs, tested endpoints, API documentation started.", + deliverables: [ + "Set up Django REST Framework (DRF)", + "Build basic CRUD endpoints for primary models", + "Add simple input validation", + "Write unit tests for API endpoints", + ], + }, + { + week: 4, + header: "Authentication & User Roles", + desc: "Authentication system with protected routes, tested and documented.", + deliverables: [ + "Implement user registration, login/logout (JWT or session-based)", + "Define user roles (admin, regular user, etc.)", + "Secure endpoints based on roles", + "Collaborate with frontend teams to consume auth APIs", + ], + }, + { + week: 5, + header: "Advanced API Features", + desc: "Functional, complex APIs beyond CRUD, integrated with FE/Android.", + deliverables: [ + "Add business logic and computed fields (e.g., scores, analytics)", + "Build custom views and serializers for dashboard and filtered data", + "Implement pagination, search, and sorting", + ], + }, + { + week: 6, + header: "Integrations & Admin Features", + desc: "At least one integration complete, admin panel configured, bug fixes underway.", + deliverables: [ + "Integrate third-party APIs or services (e.g., email, file uploads)", + "Build Django admin dashboards for internal use", + "QA with frontend and Android teams", + ], + }, + { + week: 7, + header: "Final Integrations + Deployment Prep", + desc: "Final tested API, test coverage >70%, performance improvements.", + deliverables: [ + "Write unit and integration tests", + "Fix API inconsistencies from QA feedback", + "Improve performance (caching, DB indexing, etc.)", + "Freeze the API spec", + ], + }, + { + week: 8, + header: "Soft Launch + Internal Demos", + desc: "Live backend API, clear documentation, demo-ready setup.", + deliverables: [ + "Set up staging and production environments (Heroku, Railway, etc.)", + "Final deployment with working frontends", + "Prep for presentation with demo-ready API", + "Write backend documentation and setup guide", + ], + }, + { + week: 9, + header: "Demo Day & Mock Interviews", + desc: "Finished app ready to be shown on Demo Day.", + deliverables: [ + "Morning: Team app presentations to mentors/guests", + "Afternoon: Individual mock interviews (tech questions, behavioral questions, demoing your project)", + ], + }, + ], + programOutcomes: [ + { + content: + "Build and deploy a real-world full-stack project for your portfolio", + }, + { + content: "Develop strong technical and team collaboration skills", + }, + { + content: + "Receive mentorship in resume writing and technical interviews", + }, + { + content: + "Gain confidence in working with designers, PMs, QAs, and frontend devs", + }, + ], + mentors: [ + { + id: 1, + name: "Rachel Murabula", + role: "Android Engineer", + organisation: "SpaceYaTech", + img: murabula, + experience: 4, + linkedin: { + href: "https://www.linkedin.com/in/rachel-murabula/", + username: "Rachel Murabula", + }, + twitter: { + href: "", + username: "", + }, + }, + { + id: 2, + name: "Catherine Kiiru", + role: "Dev Relations & Opensource Programs", + organisation: "Mastercraft", + img: catherine, + experience: 3, + linkedin: { + href: "https://www.linkedin.com/in/catherine-kiiru-47b2688b/", + username: "Catherine Kiiru", + }, + twitter: { + href: "https://x.com/catetherinekiiru", + username: "catetherinekiiru", + }, + }, + ], + pricing: { + fullAmount: 3500, + discountPercentage: 20, + discountedAmount: 700, + currency: "KES", + offerDeadline: "27th April", + }, + }, + { + id: 3, + slug: "product-design", + category: "Product Design", + isRegOpen: false, + nextCohortDate: "30th May", + cover: productDesign, + tags: ["UX Design", "UI Design", "Prototyping", "Product Design"], + title: "Product Design", + description: + "Learn advanced design concepts while building a multi-platform application alongside a development team.", + productInfo: [ + { + icon: duration, + title: "8 weeks", + description: "8 weeks of collaborative building in a design team.", + }, + { + icon: award, + title: "Beginner-Junior level", + description: "Recommended experience level.", + }, + { + icon: folder, + title: "2 Projects", + description: "Collaborate on two portfolio projects.", + }, + { + icon: calendar, + title: "Flexible schedules", + description: "Remote, work from home.", + }, + ], + // description + programDescription: + "This 8-week Design Engineering program will immerse you in a real cross-functional tech team alongside frontend engineers, backend developers, PMs, QAs, and fellow designers. You'll work on a real-world project that combines UI/UX design principles with practical frontend implementation skills, helping you build a strong portfolio and resume. Beyond design and coding, you’ll receive mentorship in resume writing, interview preparation, and professional development — all geared to grow you into a confident, job-ready design engineer with true team-based experience.", + whoCanApply: [ + "Beginners looking to explore the world of UI/UX Design", + "Aspiring UI/UX designers who want real-world, cross-functional team experience.", + "Junior designers who want to sharpen their technical and collaborative skills.", + "Frontend developers looking to strengthen their design thinking and user experience skills.", + ], + whatYouWillLearn: [ + { + subTitle: "UI/UX Design Foundations: ", + content: + "Master user research, wireframing, prototyping, usability testing, and responsive design for both mobile and web.", + }, + { + subTitle: "Design-to-Code Collaboration: ", + content: + "Learn how to work closely with developers and hand off designs that are production-ready, including a basic understanding of frontend principles (HTML/CSS/React basics).", + }, + { + subTitle: "Professional Practice: ", + content: + "Build real-world team experience in agile environments — design critiques, sprint planning, stakeholder presentations, and feedback integration.", + }, + ], + milestones: [ + { + week: 1, + header: "Product Thinking & Team Onboarding", + desc: "Persona drafts, early insights doc, competitor matrix, design brief outline", + deliverables: [ + "Introduce the real-world project challenge and intended users.", + "Set expectations for collaboration with PMs, frontend/backend, QA.", + "Conduct stakeholder and team interviews to gather context.", + "Guide trainees in creating a Notion hub for documentation.", + "Start competitor analysis & heuristic reviews", + ], + }, + { + week: 2, + header: "Research & Experience Mapping", + desc: "User personas, journey maps, clear MVP definition, UX principles.", + deliverables: [ + "Run or review user interviews, surveys, or field research (if available)", + "Synthesize findings using affinity mapping", + "Create journey maps, empathy maps, and user stories", + "Map out MVP features with the PM and tech leads", + "Draft early design principles to guide the project", + ], + }, + { + week: 3, + header: "Information Architecture & Low-Fidelity Prototypes", + desc: "Site map, task flows, grayscale wireframes (mobile + web), usability hypotheses.", + deliverables: [ + "Collaborate on sitemap & user flows for web and mobile", + "Conduct ideation sessions (Crazy 8s, how-might-we)", + "Sketch wireframes across core screens and states", + "Facilitate internal feedback loops (w/ engineers & PM)", + ], + }, + { + week: 4, + header: "Design Systems & Mid-Fidelity Prototyping", + desc: "Figma component library v1, mid-fi clickable prototype, style guide draft.", + deliverables: [ + "Introduce atomic design principles & system thinking", + "Begin building UI components in Figma libraries", + "Create mid-fi mockups with consistent spacing, type, and color logic", + "Co-review designs with frontend devs for feasibility", + "Peer crit sessions to raise visual clarity & hierarchy", + ], + }, + { + week: 5, + header: "High-Fidelity Design & Responsive Layouts", + desc: "Hi-fi mockups (mobile + web), responsive designs, motion/interaction docs.", + deliverables: [ + "Translate mid-fi to polished UI, using grids and accessibility best practices", + "Adapt interfaces for mobile and desktop contexts", + "Document interaction behaviors (hover, empty states, loading, etc.)", + "Work 1:1 with devs to annotate designs", + ], + }, + { + week: 6, + header: "Design QA & Dev Handoff", + desc: "Design QA checklist, updated mockups for dev alignment, revised system docs.", + deliverables: [ + "Collaborate deeply with frontend/Android engineers to check implementation", + "Use Zeplin, Figma Dev Mode, or Storybook to inspect handoff", + "Log design bugs and offer alternatives when engineering limitations arise", + "Adjust design system based on dev feedback", + ], + }, + { + week: 7, + header: "Usability Testing & Iteration", + desc: "Usability report, iteration log, final design review deck.", + deliverables: [ + "Run 1–2 usability tests with clickable prototypes or dev builds", + "Document insights using video clips, quotes, and task success rates", + "Refine UX flows, microcopy, or visuals based on feedback", + "Reflect on design decisions with mentees via crit sessions", + ], + }, + { + week: 8, + header: "Polish, Document, and Portfolio Presentation", + desc: "Final polished design file, case study (PDF/Notion), portfolio-ready mockups.", + deliverables: [ + "Create a well-documented case study walkthrough", + "Annotate final screens for your Behance/Notion/Dribbble", + "Mentor each trainee through storytelling their design journey", + "Final group critique, polish Figma files, and archive assets", + ], + }, + { + week: 9, + header: "Presentations & Career Prep", + desc: "Recorded presentation, updated portfolios, mentorship reflections.", + deliverables: [ + "Final presentation to other track members (backend, frontend, Android)", + "Trainees walk through the product, process, and what they’d do differently", + "Run mock whiteboard design challenges", + "Refine portfolios and LinkedIn profiles", + "Personal mentorship on job search, imposter syndrome, design tests", + ], + }, + ], + + programOutcomes: [ + { + content: + "Build a full design case study, complete with research, wireframes, UI screens, prototypes, and final developer handoff — ready for your portfolio.", + }, + { + content: + "Learn to defend your design decisions with research, usability data, and business reasoning.", + }, + { + content: + "Gain hands-on experience working within an agile tech team (PMs, engineers, QA, designers).", + }, + { + content: + "Strengthen your frontend basics to better collaborate with engineers (HTML/CSS fundamentals, component-based thinking).", + }, + { + content: + "Receive mentorship on building a strong design portfolio, personal branding, and preparing for design job interviews.", + }, + ], + mentors: [ + { + id: 1, + name: "Rachel Murabula", + role: "Android Engineer", + organisation: "SpaceYaTech", + img: murabula, + experience: 4, + linkedin: { + href: "https://www.linkedin.com/in/rachel-murabula/", + username: "Rachel Murabula", + }, + twitter: { + href: "", + username: "", + }, + }, + { + id: 2, + name: "Catherine Kiiru", + role: "Dev Relations & Opensource Programs", + organisation: "Mastercraft", + img: catherine, + experience: 3, + linkedin: { + href: "https://www.linkedin.com/in/catherine-kiiru-47b2688b/", + username: "Catherine Kiiru", + }, + twitter: { + href: "https://x.com/catetherinekiiru", + username: "catetherinekiiru", + }, + }, + ], + pricing: { + fullAmount: 3500, + discountPercentage: 20, + discountedAmount: 700, + currency: "KES", + offerDeadline: "27th April", + }, + }, + { + id: 4, + slug: "frontend-development", + category: "Frontend Development", + isRegOpen: false, + nextCohortDate: "30th May", + cover: frontend, + tags: ["React Components", "Redux", "Tailwind CSS", "Hooks"], + title: "Frontend Development - React", + description: + "Jumpstart your frontend development carier with out comprehensive 2-month boot camp on React, Redux, Tailwind CSS, and Hooks.", + productInfo: [ + { + icon: duration, + title: "8 weeks", + description: "8 weeks of collaborative building in a full-stack team.", + }, + { + icon: award, + title: "Junior-Mid level", + description: "Recommended experience level.", + }, + { + icon: folder, + title: "1 Projects", + description: "Collaborate on a portfolio project.", + }, + { + icon: calendar, + title: "Flexible schedules", + description: "Remote, work from home.", + }, + ], + // description + programDescription: + "This 8-week Frontend Engineering program with React is designed to immerse you in the fast-paced world of modern web development. You'll be part of a real, cross-functional tech team alongside backend engineers, designers, PMs, QAs, and other frontend developers, building an actual product from scratch. Expect to go beyond just writing code — you’ll collaborate, build, break, fix, test, and learn. From mastering components, hooks, and responsive layouts to integrating APIs and managing state, this program turns your basic knowledge into production-ready skills. We’ll also guide you on career prep with sessions on portfolio building, resume design, and mock interviews to help you become job-ready.", + whoCanApply: [ + "Aspiring frontend developers looking to break into tech with a solid foundation in modern web development.", + "Recent graduates or self-taught learners looking for real-world experience", + "Individuals eager to learn and work collaboratively in cross-functional teams", + "People with a growth mindset and commitment to completing an intensive 8-week program", + ], + whatYouWillLearn: [ + { + subTitle: "Advanced React — ", + content: "Components, JSX, hooks, state management, and routing.", + }, + { + content: + "Gain hands-on experience working in a real agile team with designers, frontend engineers, QAs, and PMs.", + }, + { + subTitle: "Working with APIs — ", + content: + "Consume REST APIs and work alongside backend teams in a real product setup", + }, + { + content: + "Learn version control (Git), code reviews, debugging, and writing clean, scalable code in production-like environments.", + }, + { + subTitle: "Deployment & Optimization — ", + content: + "Learn how to deploy React apps, handle environment variables, and optimize performance for production.", + }, + ], + milestones: [ + { + week: 1, + header: "Kickoff & Setup", + desc: "Project created and deployed (e.g., Vercel staging app). Routing system and base pages in place (e.g., /login, /signup, /dashboard with placeholder screens).", + deliverables: [ + "Understand the product requirements: walkthrough by PM and UX team.", + "Study user flows and wireframes: Understand every screen and interaction.", + "Set up project: Create React App or Vite setup, Install essential libraries (e.g., React Router, Axios, TailwindCSS/MUI)", + "Set up folder structure (pages, components, services, utils)", + "Set up GitHub repository and workflows (branching strategy, PR rules).", + ], + }, + { + week: 2, + header: "Ideation, Research & Planning", + desc: "Functional Login/Signup forms, Authentication context fully set up. Protected routing working.", + deliverables: [ + "Build real authentication pages: Sign Up, Login, Forgot Password", + "Client-side form validation (e.g., Formik + Yup or React Hook Form).", + "Use mock data first (simulate login success).", + "Create Auth Context/State Management (e.g., Context API, Redux Toolkit, Zustand).", + "Protect private routes: Only logged-in users can access Dashboard, Profile, etc.", + "Style the forms using design system from UI/UX.", + ], + }, + { + week: 3, + header: "Authentication and Core Infrastructure", + desc: "Login & authenticated user session saved. User sees personalized dashboard after login.", + deliverables: [ + "Integrate with real backend API (once available): Axios setup with interceptors (attach auth tokens).", + "Fetch user profile upon login.", + "Build main navigation: Sidebar/Menu bar (responsive), Top bar (notifications, profile dropdown).", + "Start building core screens: Dashboard/Home Feed (empty state designs included)", + ], + }, + { + week: 4, + header: "Core Features Sprint", + desc: "Users can create, view, edit, delete core resources (e.g., posts, resources).", + deliverables: [ + "Implement major user-facing features: Create/Edit Posts (forms, file uploads if needed).", + "View list of Posts (Home feed or Group feed).", + "Search and Filter functionality.", + "CRUD operations: Create, Read, Update, Delete posts or entries from API.", + "Responsive design tuning (desktop, tablet, mobile).", + ], + }, + { + week: 5, + header: "Profile Management", + desc: "Profile screen functional. Users can update their account info.", + deliverables: [ + "Build user profile screen: View and Edit profile info (name, bio, profile picture).", + "Upload profile picture to server (handle file uploads).", + "Display user’s contributions (e.g., list of posts/comments/bookmarks).", + "Link profile data across the app (e.g., click username → view profile).", + ], + }, + { + week: 6, + header: "Testing, Polish & Refinement", + desc: "In-app notifications working. App feels smooth and responsive.", + deliverables: [ + "Fetch and display notifications (e.g., new messages, comments).", + 'Add Toast notifications for success/fail events (e.g., "Post created", "Login failed").', + "Improve UI transitions and loading states (spinners, skeleton loaders).", + "Micro-interactions (button feedback, subtle animations).", + ], + }, + { + week: 7, + header: "Final Integrations + Deployment Prep", + desc: "Feature complete, bug-free candidate for release.", + deliverables: [ + "Full walkthrough of app flows: Create, Edit, Delete actions.", + "Error handling: 404 pages, empty states, network errors.", + "Polish: Animation polishing, Cross-browser compatibility, Mobile responsiveness (final tweaks).", + "Optimize performance: Lazy loading routes and components, Code splitting.", + ], + }, + { + week: 8, + header: "Soft Launch + Internal Demos", + desc: "Finished app ready to be shown on Demo Day.", + deliverables: [ + "Dry run of presentations.", + "Final fixes.", + "PM + UI/UX + Android + Backend: Team does internal presentations to leadership/mentors.", + "Mock 'real-world' scenarios: bug report drills, unexpected feedback sessions.", + ], + }, + { + week: 9, + header: "Demo Day & Mock Interviews", + desc: "Finished app ready to be shown on Demo Day.", + deliverables: [ + "Morning: Team app presentations to mentors/guests.", + "Afternoon: Individual mock interviews (tech questions, behavioral questions, demoing your project).", + ], + }, + ], + programOutcomes: [ + { + content: + "Build and deploy a real-world full-stack project for your portfolio.", + }, + { + content: "Develop strong technical and team collaboration skills.", + }, + { + content: + "Receive mentorship in resume writing and technical interviews.", + }, + { + content: + "Gain confidence in working with designers, PMs, QAs, and frontend devs.", + }, + ], + mentors: [ + { + id: 1, + name: "Rachel Murabula", + role: "Android Engineer", + organisation: "SpaceYaTech", + img: murabula, + experience: 4, + linkedin: { + href: "https://www.linkedin.com/in/rachel-murabula/", + username: "Rachel Murabula", + }, + twitter: { + href: "", + username: "", + }, + }, + { + id: 2, + name: "Catherine Kiiru", + role: "Dev Relations & Opensource Programs", + organisation: "Mastercraft", + img: catherine, + experience: 3, + linkedin: { + href: "https://www.linkedin.com/in/catherine-kiiru-47b2688b/", + username: "Catherine Kiiru", + }, + twitter: { + href: "https://x.com/catetherinekiiru", + username: "catetherinekiiru", + }, + }, + ], + pricing: { + fullAmount: 3500, + discountPercentage: 20, + discountedAmount: 700, + currency: "KES", + offerDeadline: "27th April", + }, + }, +]; diff --git a/src/router/index.jsx b/src/router/index.jsx index 40417c6b..1e8cfe5e 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -6,8 +6,8 @@ import { AboutUs, AdminLayout, AllProducts, - Blog, - Blogs, + // Blog, + // Blogs, CategoriesProducts, Checkout, CommunityPage, @@ -24,15 +24,16 @@ import { Layout, LogIn, Mastercraft, + MastercraftEnroll, MastercraftHome, MastercraftLayout, - MastercraftSearch, + // MastercraftSearch, OrdersPage, ProductDisplay, Products, ResetPassword, - Resource, - ResourcesHome, + // Resource, + // ResourcesHome, ShopDashboard, ShopSales, SignUp, @@ -86,22 +87,22 @@ const router = createBrowserRouter( ), }, - { - path: "/blogs", - element: ( - }> - - - ), - }, - { - path: "/blogs/:titleSlug", - element: ( - }> - - - ), - }, + // { + // path: "/blogs", + // element: ( + // }> + // + // + // ), + // }, + // { + // path: "/blogs/:titleSlug", + // element: ( + // }> + // + // + // ), + // }, { path: "/events", element: ( @@ -279,38 +280,46 @@ const router = createBrowserRouter( ), }, - ], - }, - { - path: "/resources", - element: , - children: [ - { - path: "/resources", - element: ( - }> - - - ), - }, - { - path: "/resources/search", - element: ( - }> - - - ), - }, { - path: "/resources/search/:id", + path: "/mastercraft/:id/enroll", element: ( }> - + ), }, ], }, + // { + // path: "/resources", + // element: , + // children: [ + // { + // path: "/resources", + // element: ( + // }> + // + // + // ), + // }, + // { + // path: "/resources/search", + // element: ( + // }> + // + // + // ), + // }, + // { + // path: "/resources/search/:id", + // element: ( + // }> + // + // + // ), + // }, + // ], + // }, { path: "/error-400", element: ( diff --git a/tailwind.config.js b/tailwind.config.js index 092233ed..212b2a85 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -37,6 +37,7 @@ module.exports = { poppins: ["Poppins", "sans-serif"], sora: ["Sora", "sans-serif"], openSans: ["Open Sans", "sans-serif"], + inter: ["Inter", "sans-serif"], }, backgroundImage: { landingPageBg: "url('/landing-bg.png')",