updated
|
@ -3,6 +3,7 @@ import ResponsiveNav from "@/components/Navbar/ResponsiveNav";
|
|||
import { fetchCourses, fetchSettings, fetchAboutus } from "@/utils";
|
||||
import {AboutusProps } from "@/types";
|
||||
import Home from '@/components/Aboutus/Home';
|
||||
|
||||
async function getCourses() {
|
||||
const courses = await fetchCourses();
|
||||
return courses;
|
||||
|
@ -31,6 +32,7 @@ export default async function Page() {
|
|||
|
||||
return (
|
||||
<div className='bg-[#F6E8E9]'>
|
||||
|
||||
<ResponsiveNav courses={courses} settings={settings} />
|
||||
<Home settings={settings} aboutus={aboutus} />
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
colors:{
|
||||
|
||||
},
|
||||
api_link: "http://localhost/api/v1/",
|
||||
}
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import ResponsiveNav from "@/components/Navbar/ResponsiveNav";
|
||||
import { fetchCourses, fetchCourse, fetchSettings } from "@/utils";
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const DynamicComponent = dynamic(() => import('@/components/Course/Course'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
@ -19,10 +20,6 @@ async function getSettings() {
|
|||
return settings;
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: "All In One",
|
||||
description: "**",
|
||||
}
|
||||
|
||||
export default async function Page({ params }: { params: { slug: string } }) {
|
||||
const courses = await getCourses();
|
||||
|
@ -34,6 +31,7 @@ export default async function Page({ params }: { params: { slug: string } }) {
|
|||
}
|
||||
return (
|
||||
<div className='bg-[#F6E8E8]'>
|
||||
|
||||
<ResponsiveNav courses={courses} settings={settings} />
|
||||
<DynamicComponent course={course} settings={settings} />
|
||||
</div>
|
||||
|
|
BIN
app/favicon.ico
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 15 KiB |
|
@ -15,6 +15,20 @@
|
|||
.mainColor{
|
||||
color: #D60050;
|
||||
}
|
||||
.navigation-loader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #0006;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dots {
|
||||
li{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
import Head from 'next/head';
|
||||
import NextTopLoader from 'nextjs-toploader';
|
||||
import { Toaster } from "react-hot-toast";
|
||||
const geistSans = localFont({
|
||||
src: "./fonts/GeistVF.woff",
|
||||
|
@ -13,9 +15,31 @@ const geistMono = localFont({
|
|||
weight: "100 900",
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
title: "All And One Music",
|
||||
description: "發掘你的音樂之路",
|
||||
icons: [
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '32x32',
|
||||
url: '/favicon/favicon-32x32.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '16x16',
|
||||
url: '/favicon/favicon-16x16.png',
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '180x180',
|
||||
url: '/favicon/apple-touch-icon.png',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
@ -26,9 +50,12 @@ export default function RootLayout({
|
|||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-[#F6E5E9]`}
|
||||
>
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
<NextTopLoader color="#D60050" height={5} />
|
||||
<Toaster position="bottom-center" />
|
||||
{children}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Home from "../components/Home/Home";
|
||||
import { fetchCourses, fetchSettings } from "../utils/index";
|
||||
import ResponsiveNav from "../components/Navbar/ResponsiveNav";
|
||||
|
||||
import Head from "next/head";
|
||||
|
||||
async function getCourses() {
|
||||
const courses = await fetchCourses();
|
||||
|
@ -14,8 +14,11 @@ async function getSettings(){
|
|||
}
|
||||
|
||||
export const metadata = {
|
||||
title: "All In One",
|
||||
title: "All And One Music",
|
||||
description: "**",
|
||||
icons: {
|
||||
icon: '/favicon.ico',
|
||||
},
|
||||
}
|
||||
|
||||
export default async function HomePage() {
|
||||
|
@ -23,6 +26,8 @@ export default async function HomePage() {
|
|||
const settings = await getSettings();
|
||||
return (
|
||||
<div>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
|
||||
<ResponsiveNav courses={courses} settings={settings} />
|
||||
<Home courses={courses} settings={settings} />
|
||||
</div>
|
||||
|
|
|
@ -75,7 +75,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
|
|||
/>
|
||||
</div>
|
||||
<div className="relative flex flex-col lg:mt-0 mt-8">
|
||||
<p className="lg:text-4xl text-3xl">
|
||||
<p className="lg:text-4xl text-2xl">
|
||||
{item.title}
|
||||
</p>
|
||||
{/* <p className="text-base overflow-hidden mt-8">
|
||||
|
|
|
@ -7,7 +7,7 @@ const Banner = () => {
|
|||
|
||||
|
||||
<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'>
|
||||
<p className='text-black lg:text-5xl text-2xl font-bold text-center'>
|
||||
{"關於我們"}
|
||||
</p>
|
||||
<img src={"/images/line.png"} className='lg:w-[35vh] h-auto object-cover mt-2' />
|
||||
|
|
|
@ -55,7 +55,7 @@ const ContactForm = ({ startLoading, stopLoading }: Props) => {
|
|||
|
||||
<div className="grid lg:grid-cols-10 ">
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex rounded-tl-3xl rounded-bl-3xl h-auto lg:col-span-6 items-center justify-center">
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex rounded-tl-3xl rounded-bl-3xl h-[62vh] max-sm:h-auto lg:col-span-6 items-center justify-center">
|
||||
|
||||
<div className="space-y-3 w-full mx-11 h-auto">
|
||||
<div className="grid md:grid-cols-2 space-y-3">
|
||||
|
|
|
@ -39,7 +39,7 @@ const Accordion = async ({ courseData }: { courseData: CoursesProps }) => {
|
|||
<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 className='lg:text-5xl text-2xl font-bold text-center mt-48'>
|
||||
{"課程時間表"}
|
||||
</p>
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@ const Banner = ({ courseData }: { courseData: CoursesProps }) => {
|
|||
<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'>
|
||||
<p className='text-black lg:text-5xl text-2xl font-bold text-center'>
|
||||
{courseData?.title}
|
||||
</p>
|
||||
<img src={"/images/line.png"} className='lg:w-[35vh] w-[20vh] h-auto object-cover mt-2' />
|
||||
|
|
|
@ -89,7 +89,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
|
|||
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>
|
||||
<span className={`lg:text-xl text-lg font-bold ml-3 ${isOpen ? "text-white " : " text-black"}`}>{title}</span>
|
||||
{isOpen ? (
|
||||
<VscChromeMinimize className='text-white mr-3' />
|
||||
) : (
|
||||
|
@ -153,7 +153,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
|
|||
:
|
||||
(<div></div>)}
|
||||
<div
|
||||
className='px-12 py-14 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
|
||||
className='px-12 py-14 lg:[&_*]:!text-[1.5rem] [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
|
||||
dangerouslySetInnerHTML={{ __html: children || '' }}
|
||||
/>
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
|
|||
{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">
|
||||
<div key={index} className="relative h-[45vw] transition-all justify-items-center mb-16">
|
||||
<img
|
||||
src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
|
||||
alt={`Course Image ${index + 1}`}
|
||||
|
|
|
@ -4,11 +4,11 @@ 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'>
|
||||
<p className='lg:text-5xl text-xl text-center'>
|
||||
{courseData.title}
|
||||
</p>
|
||||
<div
|
||||
className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center [&_*]:!bg-transparent'
|
||||
className='mt-10 lg:[&_*]:!text-[1.5rem] [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!text-center [&_*]:!bg-transparent'
|
||||
dangerouslySetInnerHTML={{ __html: courseData.long_description }}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -51,7 +51,7 @@ const ScheduleCollapse: React.FC<CollapseProps> = ({ title, rerganizedSchedule }
|
|||
<div className='px-12 py-14'>
|
||||
{rerganizedSchedule.schedules.map((schedule, index) => (
|
||||
<div key={index} className='mb-4'>
|
||||
<h3 className='text-xl font-bold'>
|
||||
<h3 className='text-base font-bold'>
|
||||
{ moment.utc(schedule.date).format("MM月DD日")}
|
||||
</h3>
|
||||
<p className='text-gray-600'>{schedule.title}</p>
|
||||
|
|
|
@ -5,6 +5,7 @@ import "slick-carousel/slick/slick.css";
|
|||
import "slick-carousel/slick/slick-theme.css";
|
||||
import Slider from "react-slick";
|
||||
import './slick.css'
|
||||
import Link from "next/link";
|
||||
|
||||
//npm i --save-dev @types/react-slick
|
||||
|
||||
|
@ -62,12 +63,13 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
|
|||
{course.sort_description}
|
||||
</p>
|
||||
<div>
|
||||
<button
|
||||
className={`mt-16 middle none center rounded-full bg-[#D60050] h-12 w-28 text-base text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40`}
|
||||
<Link
|
||||
className={`mt-16 flex items-center justify-center rounded-full bg-[#D60050] px-6 py-3 text-base text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40`}
|
||||
data-ripple-light="true"
|
||||
href={`/courses/${course.id}`}
|
||||
>
|
||||
{"了解更多"}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -86,12 +88,13 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
|
|||
<p className="text-base line-clamp-3 overflow-hidden text-center mb-8">
|
||||
{course.sort_description}
|
||||
</p>
|
||||
<button
|
||||
className="rounded-full bg-white h-12 w-28 text-sm text-black shadow-md shadow-gray-400/20 transition-all hover:shadow-lg hover:shadow-gray-500/40"
|
||||
<Link
|
||||
href={`/courses/${course.id}`}
|
||||
className="flex items-center justify-center rounded-full bg-white h-12 w-28 text-sm text-black shadow-md shadow-gray-400/20 transition-all hover:shadow-lg hover:shadow-gray-500/40"
|
||||
data-ripple-light="true"
|
||||
>
|
||||
{"了解更多"}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
"use client"
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { usePathname, useSearchParams } from 'next/navigation'
|
||||
import Loading from './Loading'
|
||||
// const Loader = () => (
|
||||
// <div className="fixed top-0 left-0 w-screen h-screen z-[99999999999999] flex items-center justify-center bg-black/40">
|
||||
// <div className="animate-spin rounded-full h-32 w-32 border-t-2 border-b-2 border-white"></div>
|
||||
// </div>
|
||||
// );
|
||||
|
||||
export default function RouteLoader() {
|
||||
const pathname = usePathname()
|
||||
const searchParams = useSearchParams()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
const timer = setTimeout(() => setLoading(false), 300) // Adjust timeout as needed
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [pathname, searchParams])
|
||||
|
||||
return loading ? <Loading /> : null
|
||||
}
|
BIN
favicon.ico
Before Width: | Height: | Size: 43 KiB |
|
@ -20,6 +20,7 @@
|
|||
"moment": "^2.30.1",
|
||||
"next": "14.2.13",
|
||||
"nextjs-cors": "^2.2.0",
|
||||
"nextjs-toploader": "^3.7.15",
|
||||
"react": "^18",
|
||||
"react-animate-on-scroll": "^2.1.9",
|
||||
"react-dom": "^18",
|
||||
|
@ -3899,6 +3900,24 @@
|
|||
"next": "^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nextjs-toploader": {
|
||||
"version": "3.7.15",
|
||||
"resolved": "https://registry.npmjs.org/nextjs-toploader/-/nextjs-toploader-3.7.15.tgz",
|
||||
"integrity": "sha512-DvvXEJVRPfE2j1HVXgFhmPl8pRcLb/4mvyVBDuYdMdkbEY7KJghp0fG5iOZ002cV6awbBw9j/Di7vQL8LRazxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nprogress": "^0.2.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://buymeacoffee.com/thesgj"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": ">= 6.0.0",
|
||||
"react": ">= 16.0.0",
|
||||
"react-dom": ">= 16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
|
@ -3909,6 +3928,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nprogress": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||
"integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"moment": "^2.30.1",
|
||||
"next": "14.2.13",
|
||||
"nextjs-cors": "^2.2.0",
|
||||
"nextjs-toploader": "^3.7.15",
|
||||
"react": "^18",
|
||||
"react-animate-on-scroll": "^2.1.9",
|
||||
"react-dom": "^18",
|
||||
|
|
Before Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "MyWebSite",
|
||||
"short_name": "MySite",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 76 KiB |