diff --git a/new-dti-website/components/courses/DDProjects.tsx b/new-dti-website/components/courses/DDProjects.tsx new file mode 100644 index 00000000..2b9b00d4 --- /dev/null +++ b/new-dti-website/components/courses/DDProjects.tsx @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; + +interface DDProjectsProps { + title: string; + description: string; + imageSrc: string; +} + +/** + * `DDProjects` Component - Displays information about past student Projects within Trends :) + * + * @remarks + * This component is used to present information about student projects, including the title, + * description, and an image representing the project that they did in Trends class. There is an interactive expand/collapse + * functionality, allowing the user to toggle additional details with a smooth transition effect. + * The card's background color changes based on its state (open/closed). The component is also responsive to screen size. + * + * The component is designed to receive data via props and a json object. + * + * @param props - Contains: + * - `title`: The title of the project, displayed prominently at the top of the card. + * - `description`: A brief description of the project, revealed when the card is expanded. + * - `imageSrc`: The URL for the image that represents the project, displayed in the expanded view. + */ +export default function DDProjects({ title, description, imageSrc }: DDProjectsProps) { + const [isOpen, setIsOpen] = useState(false); + + const toggleCard = () => { + setIsOpen(!isOpen); + }; + + return ( +
+
+

+ {title} +

+ +
+ + {/* Smooth transition for the Additional Content onClick */} +
+
+

{description}

+ {title} +
+
+
+ ); +} diff --git a/new-dti-website/components/courses/Experiences.tsx b/new-dti-website/components/courses/Experiences.tsx new file mode 100644 index 00000000..ab13b924 --- /dev/null +++ b/new-dti-website/components/courses/Experiences.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Image from 'next/image'; + +interface IconProps { + icon: string; + title: string; + description: string; +} + +/** + * `Experiences` Component - Displays key experiences for students in Trends :) + * + * @remarks + * This component is used to highlight three core experiences students will get from Trends, + * including best practices, deploying web applications, and completing a final project. Each experience consists + * of an icon, a title, and a brief description. The component adapts its layout based on screen size, with responsive + * text sizes and image scaling. + * + * @param props - Contains: + * - `icon`: The URL path to the icon image associated with the experience. + * - `title`: The title of the key experience. + * - `description`: A short description about the past student's projects. + */ +export default function Experiences({ icon, title, description }: IconProps) { + return ( + <> +
+
+ {icon} +
{title}
+
+
{description}
+
+ + ); +} diff --git a/new-dti-website/components/courses/TestimonialCard.tsx b/new-dti-website/components/courses/TestimonialCard.tsx new file mode 100644 index 00000000..37204b29 --- /dev/null +++ b/new-dti-website/components/courses/TestimonialCard.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +export interface TestimonialCardProps { + description: string; + name: string; + semesterTaken: string; +} + +/** + * `TestimonialCard` Component - Displays a single testimonial from a student who has done Trends. + * + * @remarks + * This component is used to present testimonials from students about their experiences taking the course. It showcases + * a brief description of the testimonial, the name of the student (or anonymous), and the semester in which they took the class. + * + * @param props - Contains: + * - `description`: The student's written testimonial about the course. + * - `name`: The name of the student who gave the testimonial, or "Anonymous" if the student prefers not to disclose their name. + * - `semesterTaken`: The semester when the student took the course. + */ +export default function TestimonialCard({ + description, + name, + semesterTaken +}: TestimonialCardProps) { + return ( +
+
❛❛
+

{description}

+
{name}
+
{semesterTaken}
+
+ ); +} diff --git a/new-dti-website/components/courses/TestimonialSlider.tsx b/new-dti-website/components/courses/TestimonialSlider.tsx new file mode 100644 index 00000000..dcb120d0 --- /dev/null +++ b/new-dti-website/components/courses/TestimonialSlider.tsx @@ -0,0 +1,65 @@ +import React, { useRef, useState, useEffect } from 'react'; +import TestimonialCard, { TestimonialCardProps } from './TestimonialCard'; + +interface TestimonialSliderProps { + testimonials: TestimonialCardProps[]; +} + +export default function TestimonialSlider({ testimonials }: TestimonialSliderProps) { + const sliderRef = useRef(null); + const [isScrolling, setIsScrolling] = useState(false); + const [scrollAtEnd, setScrollAtEnd] = useState(false); + + useEffect(() => { + const slider = sliderRef.current; + + // Return early if there are no testimonials or if we've reached the end + if (!slider || testimonials.length === 0 || scrollAtEnd || isScrolling) { + return undefined; // Return `undefined` explicitly to satisfy consistent-return rule + } + + const scrollInterval = setInterval(() => { + const maxScrollLeft = slider.scrollWidth - slider.clientWidth; + + if (slider.scrollLeft < maxScrollLeft) { + slider.scrollLeft += 2; // Adjust the scroll speed as needed + } else { + setScrollAtEnd(true); // Stop scrolling when the end is reached + // slider.scrollLeft = 0 if we want to start from the begnning :) + } + }, 25); // Adjust the interval as needed for smoother/faster scrolling + + // Return the cleanup function + return () => { + clearInterval(scrollInterval); + }; + }, [testimonials, isScrolling, scrollAtEnd]); + + const handleMouseEnter = () => { + setIsScrolling(true); + }; + + const handleMouseLeave = () => { + setIsScrolling(false); + }; + + return ( +
+
+ {testimonials.map((testimonial, index) => ( + + ))} +
+
+ ); +} diff --git a/new-dti-website/components/courses/Timeline.tsx b/new-dti-website/components/courses/Timeline.tsx new file mode 100644 index 00000000..d224f631 --- /dev/null +++ b/new-dti-website/components/courses/Timeline.tsx @@ -0,0 +1,217 @@ +import React, { useLayoutEffect, useRef, useState } from 'react'; +import useScreenSize from '../../src/hooks/useScreenSize'; +import { MOBILE_BREAKPOINT } from '../../src/consts'; + +export type Event = { + title: string; + date: string; + time: string; +}; + +type TimelineProps = { + events: Event[]; + currentDate: Date; +}; + +/** + * `Timeline` Component - Displays a chronological timeline of Trends events. + * + * @remarks + * This component is used to render the timeline with all the events. The timeline automatically adjusts based on screen size + * (mobile vs. desktop) via a useEffect and displays a progress line showing how far along the current date is relative to the events. + * Each event includes a title, date, and time. The date string should be formatted with the month (abbreviated), day, and + * optionally, the time (formatted as `hh:mm AM/PM`). If no time is provided, the default will be 12:00 AM. + * If no Year is provided, the default will be the Current Year + * + * @param props - Contains: + * - `events`: An array of event objects. Each object should contain: + * - `title`: The name of the event. + * - `date`: The date of the event in the format `MMM DD, YYYY` (e.g., "Feb 19, 2024"). MONTH NOT REQUIRED TO BE ABBREVIATED both Feb and February are valid + * - `time`: (Optional) The time of the event in the format `hh:mm AM/PM` (e.g., "12:00 PM EST"). + * - `currentDate`: A `Date` object representing the current date and time, used to calculate the progress through the timeline. + */ +export default function Timeline({ events, currentDate }: TimelineProps) { + const firstEventRef = useRef(null); + const lastEventRef = useRef(null); + const [lineLength, setLineLength] = useState(0); + const [isMobile, setIsMobile] = useState(false); + + /** + * Parses an event's date and time to a `Date` object. + * + * @remarks + * This function converts a date string (formatted as `MMM DD`) and an optional time string (formatted as `hh:mm AM/PM`) + * into a JavaScript `Date` object. If no time is provided, the default time is 12:00 AM. The year is automatically + * set to the current year if no year is provided, so ensure that the date values are formatted properly. + * + * @param dateStr - The date string of the event in the format `MMM DD, YYYY` (e.g., "Feb 19, 2024"). MONTH NOT REQUIRED TO BE ABBREVIATED + * @param timeStr - (Optional) The time of the event in the format `hh:mm AM/PM` (e.g., "12:00 PM EST"). + * + * @returns A `Date` object corresponding to the event's date and time. + * + * @example + * ```ts + * const eventDate = parseEventDate("Feb 19", "7:30 PM"); + * ``` + */ + const parseEventDate = (dateStr: string, timeStr: string) => { + const currentYear = new Date().getFullYear(); + const date = new Date(`${dateStr} ${currentYear} ${timeStr || '12:00 AM'}`); + return date; + }; + + /** + * Calculates the percentage of progress through the events based on the current date. + * + * @remarks + * This function sorts the events by date, calculates the total time span between the first + * and last events, and determines how far along the current date is within that time span. + * The result is a percentage that is used to fill the progress line on the timeline. + * + * @returns A number representing the percentage of progress (from 0 to 100). + * + * @example + * ```ts + * const progressPercentage = getProgressPercentage(); + * ``` + */ + const getProgressPercentage = () => { + const sortedEvents = events.sort((a, b) => { + const aDate = parseEventDate(a.date, a.time); + const bDate = parseEventDate(b.date, b.time); + return aDate.getTime() - bDate.getTime(); + }); + + const firstEventDate = parseEventDate(sortedEvents[0].date, sortedEvents[0].time); + const lastEventDate = parseEventDate( + sortedEvents[sortedEvents.length - 1].date, + sortedEvents[sortedEvents.length - 1].time + ); + + const totalTimeSpan = lastEventDate.getTime() - firstEventDate.getTime(); + const timeElapsed = Math.max(0, currentDate.getTime() - firstEventDate.getTime()); + const progress = (timeElapsed / totalTimeSpan) * 100; + + return Math.min(100, Math.max(0, progress)); + }; + + const progressPercentage = getProgressPercentage(); + const { width } = useScreenSize(); + + useLayoutEffect(() => { + /** + * Calculates the length of the line connecting the first and last events. + * + * @remarks + * This function determines the position of the first and last event elements + * on the page and calculates the distance between them, either vertically + * (on mobile) or horizontally (on desktop). It updates the line length state accordingly. + * + * @returns A number representing the length of the line in pixels. + * + * @example + * ```ts + * calculateLineLength(); + * ``` + */ + const calculateLineLength = () => { + if (events.length === 1 || events.length === 0) { + setLineLength(0); + return; + } + if (firstEventRef.current && lastEventRef.current) { + const firstPos = firstEventRef.current.getBoundingClientRect(); + const lastPos = lastEventRef.current.getBoundingClientRect(); + + const firstCenter = { + x: firstPos.left + firstPos.width / 2, + y: firstPos.top + firstPos.height / 2 + }; + + const lastCenter = { + x: lastPos.left + lastPos.width / 2, + y: lastPos.top + lastPos.height / 2 + }; + + if (isMobile) { + const verticalDistance = Math.abs(lastCenter.y - firstCenter.y); + setLineLength(verticalDistance); + } else { + const horizontalDistance = Math.abs(lastCenter.x - firstCenter.x); + setLineLength(horizontalDistance); + } + } + }; + + if (width) { + const mobile = width < MOBILE_BREAKPOINT; + setIsMobile(mobile); + calculateLineLength(); + } + }, [isMobile, lineLength, events.length, width]); + + return ( + <> +
+
+
+
+
+ + {events.map((event, idx) => { + const eventDate = parseEventDate(event.date, event.time); + const isPast = eventDate < currentDate; + let eventRef = null; + if (idx === 0) { + eventRef = firstEventRef; + } else if (idx === events.length - 1) { + eventRef = lastEventRef; + } + + return ( +
+ {/* Red Dot for Completed / Grey Dot for Mobile */} +
+ {/* Title and Date */} +
+

{event.title}

+
+

{event.date}

+

{event.time}

+
+
+ {/* Red Dot for Completed / Grey Dot for Tablet/Laptop */} + + ); + })} +
+ + ); +} diff --git a/new-dti-website/components/courses/data/key_experiences.json b/new-dti-website/components/courses/data/key_experiences.json new file mode 100644 index 00000000..ae4bec83 --- /dev/null +++ b/new-dti-website/components/courses/data/key_experiences.json @@ -0,0 +1,19 @@ +{ + "key_experiences": [ + { + "icon": "/icons/courses/pencil.png", + "title": "Best Practices", + "description": "We emphasize best engineering practices for every element of the course taught, from API design to frontend modularization" + }, + { + "icon": "/icons/courses/rocket.png", + "title": "Deploy", + "description": "Learn how to deploy your web applications ot the cloud using service provider such as Heroku or the Google Cloud Platform" + }, + { + "icon": "/icons/courses/presentation.png", + "title": "Final Project", + "description": "The class ends with a final project capstone project consolidating all class topics, which can be used on your resume or portfolio" + } + ] +} diff --git a/new-dti-website/components/courses/data/student_projects.json b/new-dti-website/components/courses/data/student_projects.json new file mode 100644 index 00000000..4d964c05 --- /dev/null +++ b/new-dti-website/components/courses/data/student_projects.json @@ -0,0 +1,24 @@ +{ + "student_projects": [ + { + "title": "Project Name1", + "description": "This project is about blah blah blah blah blah blah blah blah blah blah blah blah blah.", + "imageSrc": "https://via.placeholder.com/400" + }, + { + "title": "Project Name2", + "description": "This project is about blah blah blah blah blah blah blah blah blah blah blah blah blah.", + "imageSrc": "https://via.placeholder.com/400" + }, + { + "title": "Project Name3", + "description": "This project is about blah blah blah blah blah blah blah blah blah blah blah blah blah.", + "imageSrc": "https://via.placeholder.com/400" + }, + { + "title": "Project Name4", + "description": "This project is about blah blah blah blah blah blah blah blah blah blah blah blah blah.", + "imageSrc": "https://via.placeholder.com/400" + } + ] +} diff --git a/new-dti-website/components/courses/data/testimonials.json b/new-dti-website/components/courses/data/testimonials.json new file mode 100644 index 00000000..671fcdf7 --- /dev/null +++ b/new-dti-website/components/courses/data/testimonials.json @@ -0,0 +1,39 @@ +{ + "testimonials": [ + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + }, + { + "description": "It was so great yeah you should take it like it was crazy like all the best things in the world would want to go take the class like I feel enlightened", + "name": "NAME/ANONYMOUS 1", + "semesterTaken": "Fall 2023" + } + ] +} diff --git a/new-dti-website/components/courses/data/timeline_events.json b/new-dti-website/components/courses/data/timeline_events.json new file mode 100644 index 00000000..ad6da5cb --- /dev/null +++ b/new-dti-website/components/courses/data/timeline_events.json @@ -0,0 +1,24 @@ +{ + "timeline_events": [ + { + "title": "Sign-up Deadline", + "date": "Feb 16", + "time": "12:00 PM EST" + }, + { + "title": "First Day", + "date": "Feb 19", + "time": "7:30 PM EST" + }, + { + "title": "Final Project", + "date": "April 12", + "time": "" + }, + { + "title": "Project Presentation", + "date": "November 26, 2024", + "time": "" + } + ] +} diff --git a/new-dti-website/components/courses/data/trend_instructors.json b/new-dti-website/components/courses/data/trend_instructors.json new file mode 100644 index 00000000..83339178 --- /dev/null +++ b/new-dti-website/components/courses/data/trend_instructors.json @@ -0,0 +1,64 @@ +{ + "trend_instructors": [ + { + "lastName": "Wang", + "hometown": "", + "github": "https://github.com/oscarwang20", + "website": "", + "formerSubteams": [], + "role": "developer", + "minor": "", + "doubleMajor": "", + "netid": "ow39", + "about": "", + "linkedin": "", + "firstName": "Oscar", + "major": "", + "graduation": "", + "pronouns": "", + "subteams": ["idol"], + "roleDescription": "Developer", + "email": "ow39@cornell.edu" + }, + { + "lastName": "Wang", + "hometown": "", + "github": "https://github.com/oscarwang20", + "website": "", + "formerSubteams": [], + "role": "developer", + "minor": "", + "doubleMajor": "", + "netid": "ow39", + "about": "", + "linkedin": "", + "firstName": "Oscar", + "major": "", + "graduation": "", + "pronouns": "", + "subteams": ["idol"], + "roleDescription": "Developer", + "email": "ow39@cornell.edu" + }, + { + "lastName": "Wang", + "hometown": "", + "github": "https://github.com/oscarwang20", + "website": "", + "formerSubteams": [], + "role": "developer", + "minor": "", + "doubleMajor": "", + "netid": "ow39", + "about": "", + "linkedin": "", + "firstName": "Oscar", + "major": "", + "graduation": "", + "pronouns": "", + "subteams": ["idol"], + "roleDescription": "Developer", + "email": "ow39@cornell.edu" + } + ] +} diff --git a/new-dti-website/components/team/MemberDisplay.tsx b/new-dti-website/components/team/MemberDisplay.tsx index e55b8337..39d9f5be 100644 --- a/new-dti-website/components/team/MemberDisplay.tsx +++ b/new-dti-website/components/team/MemberDisplay.tsx @@ -89,6 +89,7 @@ const MemberDisplay: React.FC = () => { selectedMember={selectedMember} selectedRole={selectedRole} memberDetailsRef={memberDetailsRef} + isCard={false} /> ); })} diff --git a/new-dti-website/components/team/MemberGroup.tsx b/new-dti-website/components/team/MemberGroup.tsx index 2eeb0e2c..a377ff5a 100644 --- a/new-dti-website/components/team/MemberGroup.tsx +++ b/new-dti-website/components/team/MemberGroup.tsx @@ -242,6 +242,7 @@ type MemberGroupProps = { selectedMember: IdolMember | undefined; selectedRole: string; memberDetailsRef: RefObject; + isCard: boolean; }; const MemberGroup: React.FC = ({ @@ -251,7 +252,8 @@ const MemberGroup: React.FC = ({ setSelectedMember, selectedMember, selectedRole, - memberDetailsRef + memberDetailsRef, + isCard }) => { const selectedMemberIndex: number = useMemo( () => (selectedMember ? members.indexOf(selectedMember) : -1), @@ -295,16 +297,9 @@ const MemberGroup: React.FC = ({ const onCloseMemberDetails = () => setSelectedMember(undefined); return ( - (selectedRole === roleName || selectedRole === 'Full Team') && ( -
-

{`${roleName} ${ - roleName !== 'Leads' ? '' : 'Team' - }`}

-

{description}

-
+ <> + {isCard ? ( +
{members.map((member, index) => ( <> = ({ ))}
-
- ) + ) : ( + (selectedRole === roleName || selectedRole === 'Full Team') && ( +
+

{`${roleName} ${ + roleName !== 'Leads' ? '' : 'Team' + }`}

+

{description}

+
+ {members.map((member, index) => ( + <> + + setSelectedMember(member === selectedMember ? undefined : member) + } + cardState={selectedMember ? index - selectedMemberIndex : undefined} + /> + {selectedMember && canInsertMemberDetails(index) && ( +
+ +
+ )} + + ))} +
+
+ ) + )} + ); }; diff --git a/new-dti-website/public/icons/courses/pencil.png b/new-dti-website/public/icons/courses/pencil.png new file mode 100644 index 00000000..f6779ccc Binary files /dev/null and b/new-dti-website/public/icons/courses/pencil.png differ diff --git a/new-dti-website/public/icons/courses/presentation.png b/new-dti-website/public/icons/courses/presentation.png new file mode 100644 index 00000000..b86279c4 Binary files /dev/null and b/new-dti-website/public/icons/courses/presentation.png differ diff --git a/new-dti-website/public/icons/courses/rocket.png b/new-dti-website/public/icons/courses/rocket.png new file mode 100644 index 00000000..ca118975 Binary files /dev/null and b/new-dti-website/public/icons/courses/rocket.png differ diff --git a/new-dti-website/public/icons/courses/trends_logo.png b/new-dti-website/public/icons/courses/trends_logo.png new file mode 100644 index 00000000..7b335be0 Binary files /dev/null and b/new-dti-website/public/icons/courses/trends_logo.png differ diff --git a/new-dti-website/src/app/courses/page.tsx b/new-dti-website/src/app/courses/page.tsx new file mode 100644 index 00000000..db11e98c --- /dev/null +++ b/new-dti-website/src/app/courses/page.tsx @@ -0,0 +1,285 @@ +'use client'; + +// *IMPORTS +import Image from 'next/image'; +import Link from 'next/link'; +import React, { useEffect, useRef, useState } from 'react'; +import { populateMembers } from '../../utils'; + +// *IMPORT DATA +import experiencesData from '../../../components/courses/data/key_experiences.json'; +import timelineData from '../../../components/courses/data/timeline_events.json'; +import testimonialData from '../../../components/courses/data/testimonials.json'; +import studentProjectData from '../../../components/courses/data/student_projects.json'; +import trendsData from '../../../components/courses/data/trend_instructors.json'; +import teamRoles from '../../../components/team/data/roles.json'; + +// *IMPORT COMPONENTS +import RedBlob from '../../../components/blob'; +import Experiences from '../../../components/courses/Experiences'; +import Timeline from '../../../components/courses/Timeline'; +import MemberGroup from '../../../components/team/MemberGroup'; +import TestimonialSlider from '../../../components/courses/TestimonialSlider'; +import DDProjects from '../../../components/courses/DDProjects'; +import { TestimonialCardProps } from '../../../components/courses/TestimonialCard'; + +//* DATA +const { key_experiences } = experiencesData; +const { timeline_events } = timelineData; +const { testimonials }: { testimonials: TestimonialCardProps[] } = testimonialData; +const { student_projects } = studentProjectData; +const trend_instructors = trendsData.trend_instructors as IdolMember[]; + +// * BEGIN COURSES PAGE +export default function Courses() { + const trendsLogoRef = useRef(null); + const [selectedRole] = useState('Full Team'); + const [selectedMember, setSelectedMember] = useState(undefined); + + const memberDetailsRef = useRef(null); + + const roles = populateMembers( + teamRoles as { + [key: string]: { + roleName: string; + description: string; + members: IdolMember[]; + order: string[]; + color: string; + }; + }, + trend_instructors + ); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.classList.add('fade-in-animate'); + } + }); + }, + { threshold: 0.6 } + ); + + const sections = document.querySelectorAll('section'); + sections.forEach((section) => { + section.classList.add('fade-in'); + observer.observe(section); + }); + + return () => { + sections.forEach((section) => { + observer.unobserve(section); + }); + }; + }, []); + + return ( + <> +
{ + const target = event.target as HTMLElement; + if ( + !(target.id === 'memberCard' || target.parentElement?.id === 'memberCard') && + !memberDetailsRef.current?.contains(target) + ) + setSelectedMember(undefined); + }} + > + {/* Hero Section */} +
+
+
+
+
+

+ OUR
+ COURSE +

+
+
+ +
+

+ Teaching the{' '} + community +

+

+ A project team is meant, above all, to be a learning experience. Given our mission + of community impact, we want to help everyone + learn and grow + through our training course in{' '} + product development. +

+
+
+
+ +
+
+
+ + {/* WRAPPER */} +
+ {/* LOGO SECTION */} + + + {/* KEY EXPERIENCES SECTION */} +
+
+ {key_experiences.map((experiences) => ( + + ))} +
+
+ + {/* TIMELINE SECTION */} +
+
+ +
+
+ + {/* COURSE STAFF SECTION */} +
+
+
+ Course Staff +
+
+ {Object.keys(roles).map((role) => { + const value = roles[role as Role]; + if (role === 'tpm' || role === 'dev-advisor') return <>; + return ( + + ); + })} +
+
+
+ + {/* PAST STUDENT EXPERIENCES SECTION */} +
+
+
+ Past Student Experiences +
+ +
+
+ + {/* PAST STUDENT PROJECTS SECTION */} +
+
+
+ Past Student Projects +
+
+ With the right skills, you will be able to create projects like ours. +
+
+ {student_projects.map((project) => ( + + ))} +
+
+
+
+ + {/* STYLING SECTION */} + +
+ + ); +} diff --git a/new-dti-website/src/app/globals.css b/new-dti-website/src/app/globals.css index 8abdb15c..919b72ab 100644 --- a/new-dti-website/src/app/globals.css +++ b/new-dti-website/src/app/globals.css @@ -2,6 +2,10 @@ @tailwind components; @tailwind utilities; +::-webkit-scrollbar { + display: none; +} + @layer base { :root { --background: 0 0% 100%; diff --git a/new-dti-website/src/consts.ts b/new-dti-website/src/consts.ts index b5c4dee3..dd761254 100644 --- a/new-dti-website/src/consts.ts +++ b/new-dti-website/src/consts.ts @@ -1,2 +1,3 @@ +export const MOBILE_BREAKPOINT = 640; export const TABLET_BREAKPOINT = 768; export const LAPTOP_BREAKPOINT = 1024;