ผมต้องสร้างโปรเจ็คนี้ยังไง เอาเริ่มตั้งแต่โหลด โปรแกรม ติดตั้ง ตั้งค่า ใช้งาน
# 1. ติดตั้ง Node.js (เวอร์ชัน 18 หรือใหม่กว่า)
# ดาวน์โหลดจาก: https://nodejs.org/
node --version # ตรวจสอบเวอร์ชัน
npm --version # ตรวจสอบ npm
# 2. ติดตั้ง Git
# ดาวน์โหลดจาก: https://git-scm.com/
git --version # ตรวจสอบเวอร์ชัน
# 3. ติดตั้ง VS Code (แนะนำ)
# ดาวน์โหลดจาก: https://code.visualstudio.com/- ES7+ React/Redux/React-Native snippets
- Tailwind CSS IntelliSense
- TypeScript Importer
- Prettier - Code formatter
- Auto Rename Tag
- Bracket Pair Colorizer# สร้างโปรเจ็คใหม่
npx create-next-app@latest smart-study-booth --typescript --tailwind --eslint --app
# เข้าไปในโฟลเดอร์โปรเจ็ค
cd smart-study-booth
# เปิดโปรเจ็คใน VS Code
code .# ติดตั้ง shadcn/ui
npx shadcn@latest init
# ติดตั้ง components ที่จำเป็น
npx shadcn@latest add button card input label textarea select checkbox radio-group badge avatar tabs dialog alert progress separator table switch
# ติดตั้ง packages เพิ่มเติม
npm install lucide-react recharts next-auth @next-auth/prisma-adapter prisma @prisma/clientsmart-study-booth/
├── app/
│ ├── admin/
│ │ └── page.tsx
│ ├── api/
│ │ ├── auth/
│ │ ├── bookings/
│ │ └── rooms/
│ ├── login/
│ │ └── page.tsx
│ ├── qr-scanner/
│ │ └── page.tsx
│ ├── review/
│ │ └── page.tsx
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── components/
│ ├── ui/
│ └── custom/
├── lib/
├── prisma/
├── public/
└── types/สร้างไฟล์ app/page.tsx:
# คัดลอกโค้ดจากไฟล์ app/page.tsx ที่ v0 สร้างให้สร้างไฟล์อื่นๆ ตามลำดับ:
app/admin/page.tsxapp/qr-scanner/page.tsxapp/review/page.tsxapp/login/page.tsx# สร้าง Prisma schema
npx prisma initแก้ไขไฟล์ prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
image String?
points Int @default(0)
level String @default("Bronze")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bookings Booking[]
reviews Review[]
@@map("users")
}
model Room {
id String @id @default(cuid())
name String @unique
floor Int
capacity Int
status String @default("active")
amenities String[]
rating Float @default(0)
totalBookings Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bookings Booking[]
reviews Review[]
@@map("rooms")
}
model Booking {
id String @id @default(cuid())
userId String
roomId String
date DateTime
startTime String
endTime String
duration Int
purpose String
status String @default("upcoming")
qrScanned Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
room Room @relation(fields: [roomId], references: [id])
review Review?
@@map("bookings")
}
model Review {
id String @id @default(cuid())
userId String
roomId String
bookingId String @unique
cleanliness Int
quietness Int
facilities Int
overall Int
comment String?
issues String[]
recommend String
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
room Room @relation(fields: [roomId], references: [id])
booking Booking @relation(fields: [bookingId], references: [id])
@@map("reviews")
}สร้างไฟล์ .env.local:
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/smart_study_booth"
# NextAuth
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-secret-key-here"
# Google OAuth
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
# LINE Notify (ถ้าต้องการ)
LINE_NOTIFY_TOKEN="your-line-notify-token"lib/auth.ts:import { NextAuthOptions } from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "./prisma"
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
session: async ({ session, token }) => {
if (session?.user?.email) {
const user = await prisma.user.findUnique({
where: { email: session.user.email },
})
if (user) {
session.user.id = user.id
session.user.points = user.points
session.user.level = user.level
}
}
return session
},
},
pages: {
signIn: "/login",
},
}lib/prisma.ts:import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prismaapp/api/auth/[...nextauth]/route.ts:import NextAuth from "next-auth"
import { authOptions } from "@/lib/auth"
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }# สร้าง database migration
npx prisma migrate dev --name init
# สร้าง Prisma client
npx prisma generate
# เพิ่มข้อมูลตัวอย่าง (ถ้าต้องการ)
npx prisma db seed# รัน development server
npm run dev
# เปิดเบราว์เซอร์ไปที่
# http://localhost:3000สร้างไฟล์ app/api/rooms/route.ts:
import { NextRequest, NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
export async function GET() {
try {
const rooms = await prisma.room.findMany({
include: {
bookings: {
where: {
date: {
gte: new Date(),
},
},
},
},
})
return NextResponse.json(rooms)
} catch (error) {
return NextResponse.json({ error: "Failed to fetch rooms" }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const room = await prisma.room.create({
data: body,
})
return NextResponse.json(room)
} catch (error) {
return NextResponse.json({ error: "Failed to create room" }, { status: 500 })
}
}# ติดตั้ง Socket.io
npm install socket.io socket.io-client# รันการทดสอบ
npm run test
# ตรวจสอบ TypeScript
npm run type-check
# ตรวจสอบ ESLint
npm run lint# Build โปรเจ็ค
npm run build
# Deploy ไป Vercel
npx vercel
# หรือ push ไป GitHub แล้วเชื่อมต่อกับ Vercelหากมีปัญหาในขั้นตอนไหน สามารถถามได้เลย! ฉันจะช่วยแก้ไขปัญหาและอธิบายเพิ่มเติม
1/1Next.js 15.4.3WebpackBuild ErrorModule not found: Can't resolve 'geist/font/sans'./app/layout.tsx (2:1)Module not found: Can't resolve 'geist/font/sans' 1 | import type { Metadata } from 'next' > 2 | import { GeistSans } from 'geist/font/sans' | ^ 3 | import { GeistMono } from 'geist/font/mono' 4 | import './globals.css' 5 | https://nextjs.org/docs/messages/module-not-found
ปัญหานี้เกิดจากการที่ Next.js 15 ไม่ได้ติดตั้ง Geist font มาให้อัตโนมัติ มีวิธีแก้ไข 2 วิธี:
npm install geistapp/layout.tsximport type { Metadata } from 'next'
import { GeistSans } from 'geist/font/sans'
import { GeistMono } from 'geist/font/mono'
import './globals.css'
export const metadata: Metadata = {
title: 'Smart Study Booth',
description: 'ระบบจองพื้นที่เรียนรู้อัจฉริยะ',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="th" className={`${GeistSans.variable} ${GeistMono.variable}`}>
<body className={GeistSans.className}>
{children}
</body>
</html>
)
}แก้ไขไฟล์ app/layout.tsx:
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
})
export const metadata: Metadata = {
title: 'Smart Study Booth',
description: 'ระบบจองพื้นที่เรียนรู้อัจฉริยะ',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="th" className={inter.variable}>
<body className={inter.className}>
{children}
</body>
</html>
)
}แก้ไขไฟล์ app/layout.tsx:
import type { Metadata } from 'next'
import './globals.css'
export const metadata: Metadata = {
title: 'Smart Study Booth',
description: 'ระบบจองพื้นที่เรียนรู้อัจฉริยะ',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="th">
<body className="font-sans antialiased">
{children}
</body>
</html>
)
}และแก้ไขไฟล์ tailwind.config.ts:
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
fontFamily: {
sans: [
'system-ui',
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Open Sans',
'Helvetica Neue',
'sans-serif',
],
},
},
},
plugins: [],
}
export default configimport type { Metadata } from 'next'
import { Noto_Sans_Thai } from 'next/font/google'
import './globals.css'
const notoSansThai = Noto_Sans_Thai({
subsets: ['thai', 'latin'],
variable: '--font-noto-sans-thai',
})
export const metadata: Metadata = {
title: 'Smart Study Booth',
description: 'ระบบจองพื้นที่เรียนรู้อัจฉริยะ',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="th" className={notoSansThai.variable}>
<body className={notoSansThai.className}>
{children}
</body>
</html>
)
}# ดูว่าติดตั้ง geist หรือยัง
npm list geist
# ถ้ายังไม่มี ให้ติดตั้ง
npm install geist
# หรือใช้ yarn
yarn add geist# ล้าง Next.js cache
rm -rf .next
# ล้าง node_modules และติดตั้งใหม่
rm -rf node_modules
npm install
# รันโปรเจ็คใหม่
npm run devสำหรับโปรเจ็ค Smart Study Booth แนะนำให้ใช้ Noto Sans Thai เพราะ:
ไฟล์ app/layout.tsx ที่แนะนำ:
import type { Metadata } from 'next'
import { Noto_Sans_Thai } from 'next/font/google'
import './globals.css'
const notoSansThai = Noto_Sans_Thai({
subsets: ['thai', 'latin'],
weight: ['300', '400', '500', '600', '700'],
variable: '--font-noto-sans-thai',
})
export const metadata: Metadata = {
title: 'Smart Study Booth',
description: 'ระบบจองพื้นที่เรียนรู้อัจฉริยะ สำหรับนิสิตมหาวิทยาลัยมหาสารคาม',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="th" className={notoSansThai.variable}>
<body className={`${notoSansThai.className} antialiased`}>
{children}
</body>
</html>
)
}หลังจากแก้ไขแล้ว ให้รัน npm run dev ใหม่ ปัญหาควรหายไป! 🎉
เขียน code ที่ต้องใช้ทั้งหมดใน โปรเจ็คให้หน่อย เอา แบบโหลดไปวางทับแล้วใช้ได้เลย