first commit
This commit is contained in:
124
components/Course/Accordion.tsx
Normal file
124
components/Course/Accordion.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
"use client"
|
||||
import React from 'react'
|
||||
import Collapse from './Collapse'
|
||||
import ScheduleCollapse from './ScheduleCollapse'
|
||||
import { CoursesProps, ScheduleProps, RerganizedScheduleProps } from '@/types'
|
||||
|
||||
|
||||
async function reorganizedSchedule(schedule: ScheduleProps[]) {
|
||||
|
||||
const sortedSchedule = schedule.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
||||
|
||||
// Then, reorganize the sorted schedule
|
||||
const reorganizedSchedule = sortedSchedule.reduce<Record<string, { yearAndMonth: string; schedule: ScheduleProps[] }>>((acc, item) => {
|
||||
const date = new Date(item.date);
|
||||
const yearMonth = `${date.getFullYear()}年${String(date.getMonth() + 1).padStart(2, '0')}月`;
|
||||
|
||||
if (!acc[yearMonth]) {
|
||||
acc[yearMonth] = {
|
||||
yearAndMonth: yearMonth,
|
||||
schedule: []
|
||||
};
|
||||
}
|
||||
|
||||
acc[yearMonth].schedule.push(item);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.values(reorganizedSchedule);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Accordion = async ({ courseData }: { courseData: CoursesProps }) => {
|
||||
const newSchedule = await reorganizedSchedule(courseData.schedule)
|
||||
|
||||
const fakeData = [
|
||||
{
|
||||
"yearAndMonth": "2024年10月",
|
||||
"schedule": [
|
||||
{
|
||||
"info2": "A room, 14F, Sha Tin Market, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "e19a7934-82fe-4027-aca6-57a419e9623e",
|
||||
"title": "A班",
|
||||
"date": "2024-10-05T01:30:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
},
|
||||
{
|
||||
"info2": "Room B, Sheung Shui Village, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "dc49a1b6-1c1f-44d6-bc52-b757aeba7b5b",
|
||||
"title": "B班",
|
||||
"date": "2024-10-05T01:31:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
},
|
||||
{
|
||||
"info2": "Room B, Sheung Shui Village, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "d4785a90-311b-4a91-8fb7-beb077245f4f",
|
||||
"title": "A班",
|
||||
"date": "2024-10-06T01:31:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"yearAndMonth": "2024年11月",
|
||||
"schedule": [
|
||||
{
|
||||
"info2": "Room B, Sheung Shui Village, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "5c88b2a7-6b4d-455f-b341-1fec3173d208",
|
||||
"title": "A班",
|
||||
"date": "2024-11-06T01:31:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"yearAndMonth": "2025年01月",
|
||||
"schedule": [
|
||||
{
|
||||
"info2": "Room B, Sheung Shui Village, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "8a30dec2-63ed-4237-8f5b-177c42f29dda",
|
||||
"title": "B班",
|
||||
"date": "2025-01-04T01:31:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
},
|
||||
{
|
||||
"info2": "Room B, Sheung Shui Village, N.T.",
|
||||
"info1": "11:00-12:00, 13:00-14:00, 15:00-16:00, 17:00-18:00",
|
||||
"id": "86ddf906-499b-4dbd-967d-cc4dddd9f27b",
|
||||
"title": "A班",
|
||||
"date": "2025-01-05T01:31:00Z",
|
||||
"course_id": "29ab193d-2927-459d-9277-5520771b2dd6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
console.log(newSchedule)
|
||||
return (
|
||||
<div className='relative flex flex-col w-full h-auto items-center pb-60'>
|
||||
<Collapse title='課程資訊' children={courseData.information} />
|
||||
<Collapse title='課程內容' children={courseData.contant} info={true} info_images={courseData.info_images} />
|
||||
<Collapse title='備註' children={courseData.remark} />
|
||||
<p className='text-5xl text-center mt-48'>
|
||||
{"課程時間表"}
|
||||
</p>
|
||||
{
|
||||
fakeData.map((item, index) => {
|
||||
return (
|
||||
<ScheduleCollapse key={index} title={item.yearAndMonth} rerganizedSchedule={{ yearAndMonth: item.yearAndMonth, schedules: item.schedule }} />
|
||||
)
|
||||
})
|
||||
}
|
||||
<div className="bg-[url('/images/014.png')] absolute bottom-0 inset-0 h-auto w-full bg-contain bg-bottom bg-no-repeat -z-10"></div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Accordion
|
24
components/Course/Banner.tsx
Normal file
24
components/Course/Banner.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import { CoursesProps } from '@/types'
|
||||
const Banner = ({ courseData }: { courseData: CoursesProps }) => {
|
||||
console.log(courseData)
|
||||
return (
|
||||
<div className='relative flex w-full h-[40vh] lg:h-[70vh] bg-gradient-to-r from-[#FDB2B8] to-[#FEB3BA] items-center'>
|
||||
|
||||
<div className='flex-col flex lg:ml-48 ml-4 z-10'>
|
||||
<p className='text-black lg:text-5xl text-3xl font-bold text-center'>
|
||||
{courseData?.title}
|
||||
</p>
|
||||
<img src={"/images/line.png"} className='lg:w-[35vh] w-[20vh] h-auto object-cover mt-2' />
|
||||
</div>
|
||||
{/* <div className='absolute bottom-0 right-0 max-sm:-right-32 md:-right-32 lg:right-32 bg-[url("/images/kid2.png")] bg-cover bg-center bg-no-repeat h-full w-[80%]'></div> */}
|
||||
<img
|
||||
src={"/images/kid2.png"}
|
||||
alt="kid2"
|
||||
className="absolute bottom-0 right-0 lg:right-32 object-cover object-left h-full lg:w-auto :w-40"
|
||||
/>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Banner
|
168
components/Course/Collapse.tsx
Normal file
168
components/Course/Collapse.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import { cn } from '../utils'
|
||||
import { VscAdd, VscChromeMinimize } from "react-icons/vsc";
|
||||
import { imageProps, } from '@/types';
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import Slider from "react-slick";
|
||||
import './slick.css'
|
||||
|
||||
interface CollapseProps {
|
||||
title: string
|
||||
children?: string
|
||||
className?: string
|
||||
info?: boolean
|
||||
info_images?: Array<imageProps>
|
||||
|
||||
|
||||
}
|
||||
|
||||
const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, info_images, }) => {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
var settings2 = {
|
||||
arrows: false,
|
||||
infinite: true,
|
||||
//centerPadding: "30px",
|
||||
dots: true,
|
||||
speed: 500,
|
||||
slidesToShow: 3,
|
||||
slidesToScroll: 1,
|
||||
appendDots: (dots: any) => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: '24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<ul> {dots} </ul>
|
||||
</div>
|
||||
),
|
||||
dotsClass: 'dots_custom'
|
||||
};
|
||||
|
||||
var settings1 = {
|
||||
arrows: false,
|
||||
className: "center",
|
||||
centerMode: true,
|
||||
infinite: true,
|
||||
centerPadding: "30px",
|
||||
dots: true,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
beforeChange: (current: number, next: number) => setCurrentSlide(next),
|
||||
appendDots: (dots: any) => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: '24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<ul> {dots} </ul>
|
||||
</div>
|
||||
),
|
||||
dotsClass: 'dots_custom'
|
||||
};
|
||||
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [height, setHeight] = useState<number | undefined>(0)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) setHeight(ref.current?.scrollHeight)
|
||||
else setHeight(0)
|
||||
}, [isOpen])
|
||||
|
||||
return (
|
||||
children && children.length > 0 ? (
|
||||
<div className={cn('mt-6 rounded-3xl ', className)}>
|
||||
<button
|
||||
className={`flex justify-between items-center w-full p-6 text-left ${isOpen ? "rounded-t-lg bg-mainColor" : "rounded-lg bg-[#F2D6D5] "}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<span className={`text-xl font-bold ml-3 ${isOpen ? "text-white " : " text-black"}`}>{title}</span>
|
||||
{isOpen ? (
|
||||
<VscChromeMinimize className='text-white mr-3' />
|
||||
) : (
|
||||
<VscAdd className='text-black mr-3' />
|
||||
)}
|
||||
|
||||
</button>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ height: height, }}
|
||||
className="flex relative overflow-hidden transition-[height] duration-300 ease-in-out "
|
||||
>
|
||||
<div className='relative flex flex-col bg-[#FAF6F5] rounded-b-lg w-[65vw] max-sm:w-[90vw]'>
|
||||
|
||||
{info ? (<div className='relative justify-between items-center '>
|
||||
|
||||
{info_images && info_images.length > 0 && (
|
||||
<div className="flex flex-col relative w-ful h-auto ">
|
||||
<div className='relative justify-between items-center hidden lg:flex pt-10'>
|
||||
<Slider className=" w-full h-[17vw] px-12 " {...settings2}>
|
||||
{info_images.map((image, index) => (
|
||||
<div key={index} className="relative w-full h-[13vw] px-1 transition-all justify-items-center ">
|
||||
<img
|
||||
src={`http://localhost/${image.image}`}
|
||||
alt={`Course Image ${index + 1}`}
|
||||
className="w-full h-full object-cover object-center "
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
</div>
|
||||
{/* <div className='relative flex sm:block md:block lg:hidden xl:hidden 2xl:hidden'> */}
|
||||
<div className='sm:block md:block lg:hidden xl:hidden 2xl:hidden pt-10'>
|
||||
<Slider {...settings1}>
|
||||
{info_images.map((image, index) => (
|
||||
<div key={index} className="px-4">
|
||||
<div key={index} className="relative h-[50vw] transition-all justify-items-center mb-16">
|
||||
<img
|
||||
src={`http://localhost/${image.image}`}
|
||||
alt={`Course Image ${index + 1}`}
|
||||
className="w-full h-full object-cover object-center "
|
||||
/>
|
||||
{index !== currentSlide && (
|
||||
<div
|
||||
className="absolute inset-0 bg-white bg-opacity-50 transition-opacity duration-300 "
|
||||
style={{
|
||||
objectFit: 'cover',
|
||||
objectPosition: 'center'
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>)
|
||||
:
|
||||
(<div></div>)}
|
||||
<div
|
||||
className='px-12 py-14 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
|
||||
dangerouslySetInnerHTML={{ __html: children || '' }}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
)
|
||||
)
|
||||
}
|
||||
export default Collapse
|
20
components/Course/Course.tsx
Normal file
20
components/Course/Course.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import Banner from './Banner'
|
||||
import LongDesc from './LongDesc'
|
||||
import CourseImagesSilder from './CourseImagesSilder'
|
||||
import Accordion from './Accordion'
|
||||
import { CoursesProps } from '@/types'
|
||||
import Footer from '../Footer'
|
||||
const Course = ({ course }: { course: CoursesProps }) => {
|
||||
return (
|
||||
<div className='bg-[#F6E8E8]'>
|
||||
<Banner courseData={course} />
|
||||
<LongDesc courseData={course} />
|
||||
<CourseImagesSilder courseData={course} />
|
||||
<Accordion courseData={course} />
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Course
|
158
components/Course/CourseImagesSilder.tsx
Normal file
158
components/Course/CourseImagesSilder.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
"use client"
|
||||
import React, { useState } from 'react'
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import Slider from "react-slick";
|
||||
import './slick.css'
|
||||
import { CoursesProps, CoursesArrayProps } from "@/types";
|
||||
import { IoIosArrowForward, IoIosArrowBack } from "react-icons/io";
|
||||
|
||||
const SamplePrevArrow = (props: any) => {
|
||||
const { className, style, onClick } = props;
|
||||
return (
|
||||
<div
|
||||
id='prev'
|
||||
onClick={onClick}
|
||||
className="group flex h-12 w-12 rounded-full bg-[#F2D6D5] justify-center items-center absolute top-1/2 -translate-x-1/2 -left-8 transform -translate-y-1/2 z-10 cursor-pointer hover:bg-[#D60050]"
|
||||
|
||||
>
|
||||
<IoIosArrowBack className='text-black mr-1 group-hover:text-white transition-colors duration-200' size={30} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function SampleNextArrow(props: any) {
|
||||
const { className, style, onClick } = props;
|
||||
return (
|
||||
<div
|
||||
id='next'
|
||||
onClick={onClick}
|
||||
className="group flex h-12 w-12 rounded-full bg-[#F2D6D5] justify-center items-center absolute top-1/2 -translate-x-1/2 -right-20 transform -translate-y-1/2 z-10 cursor-pointer hover:bg-[#D60050]"
|
||||
>
|
||||
<IoIosArrowForward className='text-black ml-1 group-hover:text-white transition-colors duration-200' size={30} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
var settings = {
|
||||
arrows: false,
|
||||
className: "center",
|
||||
centerMode: true,
|
||||
infinite: true,
|
||||
centerPadding: "30px",
|
||||
dots: true,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
beforeChange: (current: number, next: number) => setCurrentSlide(next),
|
||||
appendDots: (dots: any) => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: '24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<ul> {dots} </ul>
|
||||
</div>
|
||||
),
|
||||
dotsClass: 'dots_custom'
|
||||
};
|
||||
|
||||
var settings2 = {
|
||||
fade: true,
|
||||
centerMode: true,
|
||||
infinite: true,
|
||||
dots: true,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
nextArrow: <SampleNextArrow to="next" />,
|
||||
prevArrow: <SamplePrevArrow to="prev" />,
|
||||
beforeChange: (current: number, next: number) => setCurrentSlide(next),
|
||||
appendDots: (dots: any) => (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: '24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<ul> {dots} </ul>
|
||||
</div>
|
||||
),
|
||||
dotsClass: 'dots_custom'
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className='w-full h-auto my-20'>
|
||||
|
||||
<div className='relative mx-[15vw] justify-between items-center hidden lg:flex'>
|
||||
{courseData.images.length > 0 && (
|
||||
<div className="w-full ">
|
||||
<Slider className="w-full h-[40vw]" {...settings2}>
|
||||
{courseData.images.map((image, index) => (
|
||||
<div key={index} className="relative w-full h-[35vw] transition-all justify-items-center mb-28 ">
|
||||
<img
|
||||
src={`http://localhost/${image.image}`}
|
||||
alt={`Course Image ${index + 1}`}
|
||||
className="w-full h-full object-cover object-center rounded-xl "
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='relative flex justify-between items-center sm:block md:block lg:hidden xl:hidden 2xl:hidden'>
|
||||
{courseData.images.length > 0 && (
|
||||
<div className="w-full">
|
||||
<Slider {...settings}>
|
||||
{courseData.images.map((image, index) => {
|
||||
return (
|
||||
<div key={index} className="px-4">
|
||||
<div key={index} className="relative h-[38vw] transition-all justify-items-center mb-16">
|
||||
<img
|
||||
src={`http://localhost/${image.image}`}
|
||||
alt={`Course Image ${index + 1}`}
|
||||
className="w-full h-full object-cover object-center rounded-xl"
|
||||
/>
|
||||
{index !== currentSlide && (
|
||||
<div
|
||||
className="absolute inset-0 bg-white bg-opacity-50 transition-opacity duration-300 rounded-xl"
|
||||
style={{
|
||||
objectFit: 'cover',
|
||||
objectPosition: 'center'
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Slider>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default CourseImagesSilder
|
20
components/Course/LongDesc.tsx
Normal file
20
components/Course/LongDesc.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import { CoursesProps } from '@/types'
|
||||
const LongDesc = ({ courseData }: { courseData: CoursesProps }) => {
|
||||
return (
|
||||
<div className='w-full h-auto lg:mt-64 mt-20'>
|
||||
<div className='flex flex-col items-center justify-center mx-[5vw] lg:mx-[20vw]'>
|
||||
<p className='text-5xl text-center'>
|
||||
{courseData.title}
|
||||
</p>
|
||||
<div
|
||||
className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center'
|
||||
dangerouslySetInnerHTML={{ __html: courseData.long_description }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default LongDesc
|
75
components/Course/ScheduleCollapse.tsx
Normal file
75
components/Course/ScheduleCollapse.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import { cn } from '../utils'
|
||||
import { VscAdd, VscChromeMinimize } from "react-icons/vsc";
|
||||
import { ScheduleProps } from '@/types';
|
||||
import moment from 'moment';
|
||||
|
||||
interface CollapseProps {
|
||||
title: string
|
||||
rerganizedSchedule?: {
|
||||
yearAndMonth: string
|
||||
schedules: ScheduleProps[]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ScheduleCollapse: React.FC<CollapseProps> = ({ title, rerganizedSchedule }) => {
|
||||
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [height, setHeight] = useState<number | undefined>(0)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) setHeight(ref.current?.scrollHeight)
|
||||
else setHeight(0)
|
||||
}, [isOpen])
|
||||
|
||||
return (
|
||||
<div className={cn('mt-6 rounded-3xl ')}>
|
||||
<button
|
||||
className={`flex justify-between items-center w-full p-6 text-left ${isOpen ? "rounded-t-lg bg-[#D60050]" : "rounded-lg bg-[#F2D6D5] "}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<span className={`text-xl font-bold ml-3 ${isOpen ? "text-white " : " text-black"}`}>{title}</span>
|
||||
{isOpen ? (
|
||||
<VscChromeMinimize className='text-white mr-3' />
|
||||
) : (
|
||||
<VscAdd className='text-black mr-3' />
|
||||
)}
|
||||
|
||||
</button>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ height: height, }}
|
||||
className="flex relative overflow-hidden transition-[height] duration-300 ease-in-out "
|
||||
>
|
||||
<div className='relative flex flex-col bg-[#FAF6F5] rounded-b-lg w-[65vw] max-sm:w-[90vw]'>
|
||||
|
||||
|
||||
{rerganizedSchedule ? (
|
||||
<div className='px-12 py-14'>
|
||||
{rerganizedSchedule.schedules.map((schedule, index) => (
|
||||
<div key={index} className='mb-4'>
|
||||
<h3 className='text-xl font-bold'>
|
||||
{ moment.utc(schedule.date).format("MM月DD日")}
|
||||
</h3>
|
||||
<p className='text-gray-600'>{schedule.title}</p>
|
||||
<p className='text-gray-600'>{schedule.info1}</p>
|
||||
<p className='text-gray-600'>{schedule.info2}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
export default ScheduleCollapse
|
30
components/Course/slick.css
Normal file
30
components/Course/slick.css
Normal file
@@ -0,0 +1,30 @@
|
||||
.dots_custom {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: auto 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dots_custom li {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 0 6px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dots_custom li button {
|
||||
border: none;
|
||||
background: #d1d1d1;
|
||||
color: transparent;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dots_custom li.slick-active button {
|
||||
background-color: #D60050;
|
||||
}
|
Reference in New Issue
Block a user