This commit is contained in:
Philip Cheung 2024-10-08 13:35:35 +08:00
parent 3272da0b2b
commit bd0f852f8a
12 changed files with 79 additions and 20 deletions

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM node:18-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
RUN npm install --save-dev
# Build the Next.js app
RUN npm run build
# Expose the port the app will run on
EXPOSE 3000
COPY . .
CMD ["npm", "run", "dev"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -13,7 +13,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
<div className='lg:grid lg:grid-cols-2 w-[80vw] mb-28'> <div className='lg:grid lg:grid-cols-2 w-[80vw] mb-28'>
<div className="flex col-span-1 h-auto w-full "> <div className="flex col-span-1 h-auto w-full ">
<img <img
src={`http://localhost/${item.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
alt="Course Image" alt="Course Image"
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md" className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
style={{ aspectRatio: '1 / 1' }} style={{ aspectRatio: '1 / 1' }}
@ -28,7 +28,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
{item.description} {item.description}
</p> */} </p> */}
<div <div
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]' className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
dangerouslySetInnerHTML={{ __html: item.description }} dangerouslySetInnerHTML={{ __html: item.description }}
/> />
@ -54,7 +54,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
</div> </div>
<div className="flex col-span-1 h-auto w-full justify-end "> <div className="flex col-span-1 h-auto w-full justify-end ">
<img <img
src={`http://localhost/${item.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
alt="Course Image" alt="Course Image"
className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md" className="w-full h-[25vw] object-cover object-center rounded-lg shadow-md"
style={{ aspectRatio: '1 / 1' }} style={{ aspectRatio: '1 / 1' }}
@ -68,7 +68,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
<div className="flex flex-col relative w-[80vw] lg:mb-28 mb-10 lg:hidden"> <div className="flex flex-col relative w-[80vw] lg:mb-28 mb-10 lg:hidden">
<div className="flex col-span-1 h-auto w-full "> <div className="flex col-span-1 h-auto w-full ">
<img <img
src={`http://localhost/${item.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${item.image}`}
alt="Course Image" alt="Course Image"
className="w-full h-[44vw] object-cover object-center rounded-lg shadow-md" className="w-full h-[44vw] object-cover object-center rounded-lg shadow-md"
style={{ aspectRatio: '1 / 1' }} style={{ aspectRatio: '1 / 1' }}
@ -82,7 +82,7 @@ const AboutusContent = ({ aboutus }: { aboutus: AboutusProps[] }) => {
{item.description} {item.description}
</p> */} </p> */}
<div <div
className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]' className='mt-8 [&_*]:!text-[1.0rem] [&_*]:!text-gray-600 [&_*]:!bg-transparent'
dangerouslySetInnerHTML={{ __html: item.description }} dangerouslySetInnerHTML={{ __html: item.description }}
/> />

View File

@ -47,7 +47,7 @@ const Map = ({ settings }: { settings: SettingsProps }) => {
<p> <p>
{"地址"} {"地址"}
</p> </p>
<button className='text-gray-700 hover:text-mainColor' <button className='text-gray-700 hover:text-mainColor text-left'
onClick={() => { window.open(`https://maps.google.com/?q=${settings.latitude},${settings.longitude}`, '_blank') }}> onClick={() => { window.open(`https://maps.google.com/?q=${settings.latitude},${settings.longitude}`, '_blank') }}>
{settings.address} {settings.address}
</button> </button>

View File

@ -113,7 +113,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
{info_images.map((image, index) => ( {info_images.map((image, index) => (
<div key={index} className="relative w-full h-[13vw] px-1 transition-all justify-items-center "> <div key={index} className="relative w-full h-[13vw] px-1 transition-all justify-items-center ">
<img <img
src={`http://localhost/${image.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
alt={`Course Image ${index + 1}`} alt={`Course Image ${index + 1}`}
className="w-full h-full object-cover object-center " className="w-full h-full object-cover object-center "
/> />
@ -128,7 +128,7 @@ const Collapse: React.FC<CollapseProps> = ({ title, children, className, info, i
<div key={index} className="px-4"> <div key={index} className="px-4">
<div key={index} className="relative h-[50vw] transition-all justify-items-center mb-16"> <div key={index} className="relative h-[50vw] transition-all justify-items-center mb-16">
<img <img
src={`http://localhost/${image.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
alt={`Course Image ${index + 1}`} alt={`Course Image ${index + 1}`}
className="w-full h-full object-cover object-center " className="w-full h-full object-cover object-center "
/> />

View File

@ -107,7 +107,7 @@ const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
{courseData.images.map((image, index) => ( {courseData.images.map((image, index) => (
<div key={index} className="relative w-full h-[35vw] transition-all justify-items-center mb-28 "> <div key={index} className="relative w-full h-[35vw] transition-all justify-items-center mb-28 ">
<img <img
src={`http://localhost/${image.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
alt={`Course Image ${index + 1}`} alt={`Course Image ${index + 1}`}
className="w-full h-full object-cover object-center rounded-xl " className="w-full h-full object-cover object-center rounded-xl "
/> />
@ -127,7 +127,7 @@ const CourseImagesSilder = ({ courseData }: { courseData: CoursesProps }) => {
<div key={index} className="px-4"> <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-[38vw] transition-all justify-items-center mb-16">
<img <img
src={`http://localhost/${image.image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${image.image}`}
alt={`Course Image ${index + 1}`} alt={`Course Image ${index + 1}`}
className="w-full h-full object-cover object-center rounded-xl" className="w-full h-full object-cover object-center rounded-xl"
/> />

View File

@ -8,7 +8,7 @@ const LongDesc = ({ courseData }: { courseData: CoursesProps }) => {
{courseData.title} {courseData.title}
</p> </p>
<div <div
className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center' className='mt-10 [&_*]:!text-[1.5rem] [&_*]:!text-gray-600 [&_*]:!text-center [&_*]:!bg-transparent'
dangerouslySetInnerHTML={{ __html: courseData.long_description }} dangerouslySetInnerHTML={{ __html: courseData.long_description }}
/> />
</div> </div>

View File

@ -49,7 +49,7 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
<div className="hidden lg:grid lg:grid-cols-2 w-full mb-28"> <div className="hidden lg:grid lg:grid-cols-2 w-full mb-28">
<div className="flex col-span-1 h-auto justify-self-end mr-20"> <div className="flex col-span-1 h-auto justify-self-end mr-20">
<img <img
src={`http://localhost/${course.images[0].image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${course.images[0].image}`}
alt="Course Image" alt="Course Image"
className="w-[50vh] h-[70vh] object-cover object-center rounded-3xl shadow-md" className="w-[50vh] h-[70vh] object-cover object-center rounded-3xl shadow-md"
style={{ aspectRatio: '1 / 1' }} style={{ aspectRatio: '1 / 1' }}
@ -77,7 +77,7 @@ const CoursesSilder = ({ courses }: { courses: CoursesProps[] }) => {
<div className=" relative w-[50vh] h-[70vh] max-sm:w-[36vh] max-sm:h-[60vh] mt-20 mb-28 overflow-hidden rounded-3xl shadow-md sm:block md:block lg:hidden xl:hidden 2xl:hidden mx-auto"> <div className=" relative w-[50vh] h-[70vh] max-sm:w-[36vh] max-sm:h-[60vh] mt-20 mb-28 overflow-hidden rounded-3xl shadow-md sm:block md:block lg:hidden xl:hidden 2xl:hidden mx-auto">
<img <img
src={`http://localhost/${course.images[0].image}`} src={`${process.env.NEXT_PUBLIC_IMAGE_URL}${course.images[0].image}`}
alt="Course Image" alt="Course Image"
className="absolute inset-0 w-full h-full object-cover object-center " className="absolute inset-0 w-full h-full object-cover object-center "
/> />

35
docker-compose.yml Normal file
View File

@ -0,0 +1,35 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: docker-next
env_file:
- .env
ports:
- '3000:3000'
volumes:
- .:/app
- /app/node_modules
labels:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
- traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le
# Enable www redirection for HTTP and HTTPS
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.middlewares=${STACK_NAME?Variable not set}-www-redirect
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect,${STACK_NAME?Variable not set}-www-redirect
networks:
- traefik-public
- default

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -11,7 +11,7 @@ export async function postMessage(data: MessageProps) {
}; };
const body = JSON.stringify(data); const body = JSON.stringify(data);
const url = `http://localhost/api/v1/messages/`; const url = `${process.env.NEXT_PUBLIC_API_URL}messages/`;
try { try {
const response = await fetch(url, { const response = await fetch(url, {
@ -41,7 +41,7 @@ export async function fetchAboutus() {
const headers: HeadersInit = { const headers: HeadersInit = {
accept: "application/json" accept: "application/json"
}; };
const url = `${process.env.api_url}aboutUs`; const url = `${process.env.NEXT_PUBLIC_API_URL}aboutUs/`;
console.log('Fetching from URL:', url); console.log('Fetching from URL:', url);
try { try {
const response = await fetch(url, { headers }); const response = await fetch(url, { headers });
@ -67,7 +67,7 @@ export async function fetchCourses() {
accept: "application/json" accept: "application/json"
}; };
const url = `${process.env.api_url}course/?skip=0&limit=100`; const url = `${process.env.NEXT_PUBLIC_API_URL}course/?skip=0&limit=100`;
console.log('Fetching from URL:', url); console.log('Fetching from URL:', url);
try { try {
@ -83,7 +83,11 @@ export async function fetchCourses() {
info_images: course?.info_images?.sort((a: any, b: any) => a.index - b.index), info_images: course?.info_images?.sort((a: any, b: any) => a.index - b.index),
})); }));
return coursesArray; const sortedCoursesArray = coursesArray.sort((a, b) =>
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
);
return sortedCoursesArray;
} catch (error) { } catch (error) {
console.error('Fetch error:', error); console.error('Fetch error:', error);
throw error; throw error;
@ -95,7 +99,7 @@ export async function fetchCourse(id: string) {
accept: "application/json" accept: "application/json"
}; };
const url = `${process.env.api_url}course/${id}`; const url = `${process.env.NEXT_PUBLIC_API_URL}course/${id}/`;
console.log('Fetching from URL:', url); console.log('Fetching from URL:', url);
try { try {
@ -120,7 +124,7 @@ export async function fetchSettings() {
const headers: HeadersInit = { const headers: HeadersInit = {
accept: "application/json" accept: "application/json"
}; };
const url = `${process.env.api_url}setting`; const url = `${process.env.NEXT_PUBLIC_API_URL}setting/`;
console.log('Fetching from URL:', url); console.log('Fetching from URL:', url);
try { try {
const response = await fetch(url, { headers }); const response = await fetch(url, { headers });