probooking
    • ProBookingCenter
    • Data Dictionary
    • Database Diagram
    • Code Issues
    • Page
    • Plan
    • Online vs Source Code

    Plan

    Pro Booking Center (PBC) - Next.js 16 Migration Guide#

    เอกสารสำหรับใช้เป็น reference ในการย้ายระบบจาก Laravel 8 + Vue 2 ไปเป็น Next.js 16 + TypeScript + HeroUI

    1. Executive Summary#

    หัวข้อระบบปัจจุบันระบบเป้าหมาย
    FrameworkLaravel 8.75 (PHP 7.3/8.0)Next.js 16 (App Router)
    FrontendVue 2.6 + Vuex + Blade SSRReact 19 + TypeScript 5.x
    CSSBootstrap 5 + Tailwind + Custom SCSSHeroUI v2 + Tailwind CSS v4
    StateVuex 3.6Zustand / TanStack Query v5
    HTTPAxios → /rest/*fetch / TanStack Query v5
    AuthJWT + Sanctum + Fortify 2FAAuth.js v5 (next-auth v5) + JWT
    Database5 MySQL DBs via Eloquent ORMPrisma ORM 6 (multi-schema)
    PDFDomPDF, mPDF, TCPDF, FPDFPuppeteer / @react-pdf
    ExcelMaatwebsite/ExcelExcelJS / SheetJS
    ChartsApexCharts + Chart.jsRecharts / Tremor
    IconsMaterial Symbols (Google Fonts)Iconify (@iconify/react)
    i18nvue-i18n (TH/EN)next-intl v4
    BuildLaravel Mix (Webpack)Turbopack (built-in Next.js 16)

    ขนาดของระบบ#

    Componentจำนวน
    Controllers133
    Models (Eloquent)190
    Routes300+
    Blade Templates200+
    Vue Components170+
    Database Migrations153
    Database Tables150+
    Middleware14
    Queue Jobs8
    Route Files15

    Next.js 16 — Key Features ที่ใช้ในโปรเจคนี้#

    Featureคำอธิบายใช้กับ
    React 19useActionState, useOptimistic, Server Components เป็น defaultทุกหน้า
    Turbopack (Stable)Dev server เร็วกว่า Webpack 10x, ไม่ต้อง config เพิ่มDevelopment
    Server Actions (Stable)เรียก server functions จาก client ได้โดยตรง ไม่ต้องสร้าง API routeBooking forms, Payment submit
    Partial Prerendering (PPR)Static shell + streaming dynamic parts — โหลดเร็วมากDashboard, Booking list
    next/afterรัน code หลังส่ง response (logging, analytics) ไม่ block userActivity logging, audit trail
    Enhanced Cachinguse cache directive, cacheLife, cacheTag — ควบคุม cache ละเอียดขึ้นSeries list, Country list, Settings
    forbidden() / unauthorized()จัดการ 401/403 ด้วย built-in functions + forbidden.tsx, unauthorized.tsxRBAC middleware
    Metadata APIDynamic metadata สำหรับ SEO ต่อหน้าทุกหน้า
    Parallel Routes & Intercepting RoutesModal routes, split layoutsBooking detail modal, Payment popup
    Server Components + StreamingSSR ที่ส่ง HTML ทีละส่วน ผู้ใช้เห็นข้อมูลเร็วขึ้นData tables, Reports
    Edge RuntimeMiddleware รันที่ Edge ใกล้ผู้ใช้Auth middleware, RBAC
    Tailwind CSS v4Built-in support, ไม่ต้อง config PostCSSStyling ทั้งโปรเจค

    ตัวอย่าง: Server Actions ใน Booking Form#

    // app/(dashboard)/booking/create/[busId]/page.tsx
    import { createBooking } from './actions';
    
    export default function CreateBookingPage() {
      return (
        <form action={createBooking}>
          <input name="book_cus_name" placeholder="ชื่อลูกค้า" required />
          <input name="book_pax" type="number" min={1} placeholder="จำนวนคน" />
          <button type="submit">สร้าง Booking</button>
        </form>
      );
    }

    ตัวอย่าง: Partial Prerendering (PPR) ใน Dashboard#

    // app/(dashboard)/dashboard/page.tsx
    import { Suspense } from 'react';
    import { SalesChart } from './sales-chart';
    import { BookingSummary } from './booking-summary';
    import { Skeleton } from '@heroui/react';
    
    // Static shell renders immediately, dynamic parts stream in
    export default function DashboardPage() {
      return (
        <div className="grid grid-cols-2 gap-4">
          {/* Static: renders at build time */}
          <h1>Dashboard</h1>
    
          {/* Dynamic: streams in when ready */}
          <Suspense fallback={<Skeleton className="h-64 w-full rounded-lg" />}>
            <SalesChart />
          </Suspense>
    
          <Suspense fallback={<Skeleton className="h-48 w-full rounded-lg" />}>
            <BookingSummary />
          </Suspense>
        </div>
      );
    }

    ตัวอย่าง: use cache สำหรับ Static Data#

    ตัวอย่าง: forbidden() / unauthorized() (Next.js 16)#

    // app/(dashboard)/payment/page.tsx
    import { auth } from '@/auth';
    import { forbidden, unauthorized } from 'next/navigation';
    
    export default async function PaymentPage() {
      const session = await auth();
    
      if (!session) unauthorized();   // → renders unauthorized.tsx (401)
      if (!['payment', 'admin'].includes(session.user.scope)) {
        forbidden();                  // → renders forbidden.tsx (403)
      }
    
      return <PaymentList />;
    }
    // app/forbidden.tsx — custom 403 page
    export default function Forbidden() {
      return (
        <div className="flex h-screen items-center justify-center">
          <div className="text-center">
            <h1 className="text-4xl font-bold">403</h1>
            <p className="mt-2 text-gray-500">คุณไม่มีสิทธิ์เข้าถึงหน้านี้</p>
          </div>
        </div>
      );
    }

    2. Infrastructure Diagram#

    2.1 Current Architecture (Laravel)#

    2.2 Rendering Model#

    2.3 Target Architecture (Next.js 16)#


    3. System Flowcharts#

    3.1 Booking Lifecycle (Core Business Flow)#

    Status Constants (จาก Booking.php):
    CodeStatusคำอธิบาย
    00STATUS_WAITINGรอดำเนินการ
    05STATUS_WAITINGรอ
    10STATUS_INVOICEออก Invoice แล้ว
    20STATUS_DEPOSIT_PARTมัดจำบางส่วน
    25STATUS_DEPOSIT_FULLมัดจำเต็ม
    30STATUS_FULL_PARTชำระเต็มบางส่วน
    35STATUS_FULL_PAYชำระเต็มจำนวน
    40STATUS_CANCELยกเลิก
    50STATUS_WAITING_WLจอง Waitlist
    55STATUS_PAYแจ้งชำระ
    60STATUS_CANCEL_PAYปฏิเสธชำระ
    Cancellation Rules:
    status_cancel = 1 → ยกเลิกก่อน 30 วัน (คืนเงินได้)
    status_cancel = 2 → ยกเลิกก่อน 10 วัน (คืนบางส่วน)
    status_cancel = 3 → ไม่คืนเงิน

    3.2 Payment Approval Flow#


    3.3 Tour/Series/Period Data Hierarchy#


    3.4 Authentication & Authorization Flow#

    3.5 Role-Based Access Control (RBAC)#

    Middleware Mapping (จาก Kernel.php):
    Middleware AliasClassใช้กับ Route Group
    authAuthenticateทุก route ที่ต้อง login
    adminAdminRoleAccess (scope: admin)User management, Reports
    operationAdminRoleAccess (scope: operation)Tour/Period/Bus management
    paymentAdminRoleAccess (scope: payment)Payment/Invoice/Receipt
    salesAdminRoleAccess (scope: sales)Sales dashboard
    ticketAdminRoleAccess (scope: ticket)Ticket management
    eventAdminRoleAccess (scope: event)Event/Email
    settingsAdminRoleAccess (scope: settings)Settings/Agency
    two-factor.confirmedEnsureTwoFactorIsConfirmed2FA protected routes

    4. Module Summary & Route Mapping#

    4.1 Route File → Next.js App Router Mapping#

    Laravel Route FilePrefixNext.js Directoryจำนวน Routes
    web.php/app/(dashboard)/~25
    booking.php/bookingapp/(dashboard)/booking/~20
    payment.php/paymentapp/(dashboard)/payment/~10
    invoice.php/invoiceapp/(dashboard)/invoice/~10
    ticket.php/ticketsapp/(dashboard)/tickets/~25
    incentive.php/incentiveapp/(dashboard)/incentive/~15
    reports.php/reportsapp/(dashboard)/reports/~25
    agency.php/agencyapp/(dashboard)/agency/~30
    setting.php/settingapp/(dashboard)/settings/~30
    tour.php/seriesapp/(dashboard)/tours/~5
    web-api.php/rest/*app/api/~80+
    api.php/api/*app/api/~20

    4.2 Detailed Route Mapping by Module#

    Dashboard Module#

    Laravel RouteMethodControllerNext.js Path
    /GETHomeController@indexapp/(dashboard)/page.tsx
    /dashboardGETHomeController@dashboardapp/(dashboard)/dashboard/page.tsx
    /sales/dashboardGETHomeController@salesDashboardapp/(dashboard)/sales-dashboard/page.tsx
    /admin/dashboardGETHomeController@AdminDashboardapp/(dashboard)/admin-dashboard/page.tsx

    Booking Module#

    Laravel RouteMethodControllerNext.js Path
    /bookingGETBookingController@indexapp/(dashboard)/booking/page.tsx
    /booking/create/bus/{bus}GETBookingController@createapp/(dashboard)/booking/create/[busId]/page.tsx
    /booking/{booking}GETBookingController@showapp/(dashboard)/booking/[id]/page.tsx
    /booking/{booking}/overviewGETBookingController@overviewapp/(dashboard)/booking/[id]/overview/page.tsx
    /booking/{id}/paymentGETBookingController@paymentapp/(dashboard)/booking/[id]/payment/page.tsx
    /booking/{id}/passportGETBookingController@passportapp/(dashboard)/booking/[id]/passport/page.tsx
    /booking/{id}/travelerGETBookingController@travelerapp/(dashboard)/booking/[id]/traveler/page.tsx
    /booking/allGETAllBookingController@indexapp/(dashboard)/booking/all/page.tsx
    /my-bookingGETMyBookingController@indexapp/(dashboard)/my-booking/page.tsx
    /order/{order}GETBookingController@detailapp/(dashboard)/order/[id]/page.tsx

    Tour/Series Module#

    Laravel RouteMethodControllerNext.js Path
    /series-tourGETSeriesTourController@indexapp/(dashboard)/series-tour/page.tsx
    /tours/createGETManageTourController@createapp/(dashboard)/tours/create/page.tsx
    /tours/{tour}/editGETManageTourController@editapp/(dashboard)/tours/[id]/edit/page.tsx
    /tours/{tour}/period/createGETTourPeriodController@createapp/(dashboard)/tours/[id]/period/create/page.tsx
    /tours/{tour}/period/{period}/editGETTourPeriodController@editapp/(dashboard)/tours/[id]/period/[periodId]/edit/page.tsx

    Period/Booking Module#

    Laravel RouteMethodControllerNext.js Path
    /periodGETPeriodBookingController@indexapp/(dashboard)/period/page.tsx
    /period/popup/{id}GETPeriodBookingController@showPopupcomponent (modal)
    /period/detail/bus/{busId}/busno/{busNo}/period/{periodId}GETPeriodBookingController@detailapp/(dashboard)/period/[periodId]/bus/[busId]/page.tsx
    /period/rushtoSellGETPeriodBookingController@rushToSellapp/(dashboard)/period/rush-to-sell/page.tsx

    Payment Module#

    Laravel RouteMethodControllerNext.js Path
    /paymentGETPaymentController@indexapp/(dashboard)/payment/page.tsx
    /payment/createGETPaymentController@createapp/(dashboard)/payment/create/page.tsx
    /payment/{id}/editGETPaymentController@editapp/(dashboard)/payment/[id]/edit/page.tsx
    /payment-dueGETPaymentDueController@indexapp/(dashboard)/payment-due/page.tsx

    Invoice Module#

    Laravel RouteMethodControllerNext.js Path
    /invoices/allGETInvoicesController@indexapp/(dashboard)/invoices/page.tsx
    /invoices/requestGETInvoicesController@requestListapp/(dashboard)/invoices/request/page.tsx
    /invoice/listGETInvoiceController@listapp/(dashboard)/invoice/list/page.tsx
    /invoice/{InvoiceReport}GETInvoiceController@showapp/(dashboard)/invoice/[id]/page.tsx
    /invoicetaxGETInvoiceTaxController@indexapp/(dashboard)/invoice-tax/page.tsx
    /receiptGETReceiptController@indexapp/(dashboard)/receipt/page.tsx

    Ticket Module#

    Laravel RouteMethodControllerNext.js Path
    /ticket/seriesGETSeriesTicketController@indexapp/(dashboard)/ticket/series/page.tsx
    /ticket/pre-saleGETPreSaleTicketController@indexapp/(dashboard)/ticket/pre-sale/page.tsx
    /ticket/ticketGETManageTicketController@indexapp/(dashboard)/ticket/manage/page.tsx
    /tickets/series/{id}/period/{period}/editGETPeriodTicketController@editapp/(dashboard)/ticket/series/[id]/period/[periodId]/edit/page.tsx

    Incentive Module#

    Laravel RouteMethodControllerNext.js Path
    /incentiveGETIncentiveController@indexapp/(dashboard)/incentive/page.tsx
    /incentive/createGETIncentiveController@createapp/(dashboard)/incentive/create/page.tsx
    /incentive/{id}/editGETIncentiveController@editapp/(dashboard)/incentive/[id]/edit/page.tsx
    /incentive/{id}/programGETIncentiveProgramController@showapp/(dashboard)/incentive/[id]/program/page.tsx
    /incentive/{id}/paymentGETIncentivePaymentController@indexapp/(dashboard)/incentive/[id]/payment/page.tsx
    /incentive/{id}/travelerGETIncentiveTravelerController@indexapp/(dashboard)/incentive/[id]/traveler/page.tsx

    Reports Module#

    Laravel RouteMethodControllerNext.js Path
    /reports/invoiceGET-app/(dashboard)/reports/invoice/page.tsx
    /reports/paymentGET-app/(dashboard)/reports/payment/page.tsx
    /reports/periodGET-app/(dashboard)/reports/period/page.tsx
    /reports/costingGET-app/(dashboard)/reports/costing/page.tsx
    /reports/incomeGET-app/(dashboard)/reports/income/page.tsx
    /reports/estimatesGET-app/(dashboard)/reports/estimates/page.tsx
    /reports/commissionGET-app/(dashboard)/reports/commission/page.tsx
    /reports/agencyGET-app/(dashboard)/reports/agency/page.tsx
    /reports/invoice-full-paymentGET-app/(dashboard)/reports/invoice-full-payment/page.tsx

    Agency Module#

    Laravel RouteMethodControllerNext.js Path
    /agency/salesGETAgencyController@indexapp/(dashboard)/agency/sales/page.tsx
    /agency/sales/createGETAgencyController@createapp/(dashboard)/agency/sales/create/page.tsx
    /agency/sales/edit/{id}GETAgencyController@editapp/(dashboard)/agency/sales/[id]/edit/page.tsx
    /agencyGETCompanyController@indexapp/(dashboard)/agency/company/page.tsx
    /agency/createGETCompanyController@createapp/(dashboard)/agency/company/create/page.tsx
    /agency/{id}/editGETCompanyController@editapp/(dashboard)/agency/company/[id]/edit/page.tsx

    Settings Module#

    Laravel RouteMethodControllerNext.js Path
    /setting/airlineGETAirlineController@indexapp/(dashboard)/settings/airline/page.tsx
    /setting/routeGETRouteController@indexapp/(dashboard)/settings/route/page.tsx
    /setting/countryGETCountryController@indexapp/(dashboard)/settings/country/page.tsx
    /setting/cityGETCityController@indexapp/(dashboard)/settings/city/page.tsx
    /setting/bankGETBankController@indexapp/(dashboard)/settings/bank/page.tsx
    /setting/bannerGETBannerController@indexapp/(dashboard)/settings/banner/page.tsx
    /setting/flashsale-bannerGETFlashsaleBannerController@indexapp/(dashboard)/settings/flashsale-banner/page.tsx
    /setting/land-operationGETLandOperationController@indexapp/(dashboard)/settings/land-operation/page.tsx
    /setting/promotionGETPromotionController@indexapp/(dashboard)/settings/promotion/page.tsx

    Event Module#

    Laravel RouteMethodControllerNext.js Path
    /eventGETEventController@indexapp/(dashboard)/event/page.tsx
    /event/emailGETEventEmailController@indexapp/(dashboard)/event/email/page.tsx

    Auth Module#

    Laravel RouteMethodControllerNext.js Path
    /auth/token/loginGETTokenLoginController@loginapp/(auth)/login/page.tsx
    /auth/jwtGETTokenLoginController@loginByJwtapp/api/auth/jwt/route.ts
    /two-factor-formGETHomeController@twoFactorLoginFormapp/(auth)/two-factor/page.tsx
    /accountGETAccountController@indexapp/(dashboard)/account/page.tsx

    5. Database Architecture#

    5.1 Database Connections#

    5.2 Tables by Database#

    Database: pbc_center (~120 tables)#

    Users & Auth
    TableคำอธิบายKey Columns
    usersผู้ใช้ระบบid, name, email, password
    rolesบทบาทid, name, scope
    user_role_permitsสิทธิ์ตาม roleuser_id, role_id
    user_country_permitsสิทธิ์ตามประเทศuser_id, country_id
    personal_access_tokensSanctum tokenstokenable_id, token
    Agency
    TableคำอธิบายKey Columns
    agenciesตัวแทนขายagen_id, name, company_id
    agency_companiesบริษัทตัวแทนid, name, tax_id, address
    agency_access_tokensAPI tokenagency_id, token
    agency_credit_shellsเครดิตagency_id, amount, type
    agency_email_mastersEmail configagency_id, email
    agency_company_verificationsการยืนยันบริษัทcompany_id, status
    Series & Tours
    TableคำอธิบายKey Columns
    seriesแพ็คเกจทัวร์ser_id, name, country_id, airline_id, price
    series_programesโปรแกรมรายวันid, ser_id, day, description
    series_location_citiesเมืองในทัวร์id, ser_id, city_id
    Periods
    TableคำอธิบายKey Columns
    periodsรอบเดินทางper_id, ser_id, start_date, end_date, seats, status
    tour_period_lockseatsล็อคที่นั่งid, per_id, seats, agency_id
    period_leadersหัวหน้าทัวร์id, per_id, user_id
    Bookings
    TableคำอธิบายKey Columns
    bookingsการจองbook_id, per_id, bus_id, agen_id, status, total
    booking_detailsผู้เดินทางid, book_id, name, passport, room_type
    booking_listsรายการจองid, book_id, description, amount
    booking_logsประวัติid, book_id, action, user_id
    booking_activitiesกิจกรรมid, book_id, type, data
    booking_guaranteesหลักประกันid, book_id, file, status
    booking_refundsคืนเงินid, book_id, amount, reason
    booking_deductsหักเงินid, book_id, amount
    booking_extralistsรายการเสริมid, book_id, name, price
    booking_send_mailsส่ง emailid, book_id, type, sent_at
    booking_visa_eusวีซ่า EUid, book_id, status
    booking_visa_rejectsวีซ่าถูกปฏิเสธid, book_id, country
    Payments
    TableคำอธิบายKey Columns
    paymentsการชำระpay_id, book_id, amount, type, status
    payment_methodsวิธีชำระid, name
    payment_transitionsการเปลี่ยนสถานะid, pay_id, from, to, user_id
    bankbooksบัญชีธนาคารid, book_id, bank, account
    Invoices & Receipts
    TableคำอธิบายKey Columns
    invoicesใบแจ้งหนี้id, book_id, invoice_no, amount
    invoices_detailsรายการใน invoiceid, invoice_id, description, amount
    invoices_requestsคำขอ invoiceid, invoice_id, status
    receiptsใบเสร็จid, book_id, receipt_no
    receipt_temporariesใบเสร็จชั่วคราวid, book_id
    Transport
    TableคำอธิบายKey Columns
    bus_listsรถ/กรุ๊ปbus_id, per_id, bus_no, seats, price
    tour_busesรถทัวร์id, tour_id, bus_no
    tour_lockseatsล็อคที่นั่งid, bus_id, seats
    Costing
    TableคำอธิบายKey Columns
    cost_seatsต้นทุนต่อที่นั่งid, per_id, type, amount
    cost_seat_ordersคำสั่งต้นทุนid, cost_seat_id
    cost_seat_order_detailsรายละเอียดid, order_id
    cost_seat_logsประวัติid, cost_seat_id
    cost_seat_averagesค่าเฉลี่ยid, per_id
    estimatesประมาณการid, per_id, type
    estimate_detailsรายละเอียดประมาณการid, estimate_id
    Locations
    Tableคำอธิบาย
    location_countriesประเทศ
    location_citiesเมือง
    location_provincesจังหวัด
    location_districtsอำเภอ
    countriesประเทศ (master)
    provincesจังหวัด (master)
    amphursอำเภอ (master)
    districtsตำบล (master)
    Others
    Tableคำอธิบาย
    airlinesสายการบิน
    passportsหนังสือเดินทาง
    room_detailsห้องพัก
    prefix_numbersเลขลำดับ
    activitieslog กิจกรรม
    notificationsการแจ้งเตือน
    approvesการอนุมัติ
    bannersแบนเนอร์เว็บ
    flashsale_bannersแบนเนอร์ Flash Sale
    land_operationsLand Operations
    country_land_operationsLand Ops ตามประเทศ
    promotions_bookingsโปรโมชัน

    Database: pbc_incentive (~15 tables)#

    Tableคำอธิบาย
    incentive_quotationsใบเสนอราคา Incentive
    incentive_quotation_detailsรายละเอียด
    incentive_quotation_programsโปรแกรม
    incentive_quotation_flightsเที่ยวบิน
    incentive_quotation_passportsหนังสือเดินทาง
    incentive_quotation_pricesราคา
    incentive_quotation_paymentsการชำระ
    incentive_quotation_payment_duesกำหนดชำระ
    incentive_quotation_room_listsห้องพัก
    incentive_payment_cencelsยกเลิกชำระ
    incentive_payment_cancel_typesประเภทยกเลิก
    invoice_reportsรายงาน Invoice
    invoice_report_detailsรายละเอียด
    invoice_tax_reportsรายงานภาษี
    costing_reportsรายงานต้นทุน

    Database: pbc_ticket (~30 tables)#

    Tableคำอธิบาย
    ticketตั๋วหลัก
    ticket_seriesSeries ตั๋ว
    ticket_periodPeriod ตั๋ว
    ticket_bookingจองตั๋ว
    ticket_flightเที่ยวบิน
    ticket_paymentชำระตั๋ว
    ticket_payment_realชำระจริง
    ticket_payment_otherชำระอื่นๆ
    ticket_invoice_historyประวัติ Invoice
    ticket_refundคืนตั๋ว
    ticket_tax_refundคืนภาษี
    guaranteesหลักประกัน
    ticket_quatationsใบเสนอราคา
    ticket_namelistรายชื่อ
    ticket_deduct_bookหักจองตั๋ว
    ticket_lockseatล็อคที่นั่ง

    Database: pbc_report (~10 tables)#

    Tableคำอธิบาย
    invoice_full_paymentsInvoice ชำระเต็ม
    invoice_full_payment_detailsรายละเอียด
    report_booking_monthliesรายงานรายเดือน
    reports_booking_yearly_activitiesรายงานรายปี
    reports_periods_monthliesPeriod รายเดือน
    reports_periods_yearly_activitiesPeriod รายปี
    sales_booking_dailiesยอดขายรายวัน

    Database: pbc_costing (~5 tables)#

    Tableคำอธิบาย
    costing_series_reportsรายงานต้นทุน Series
    costing_series_typesประเภทต้นทุน
    costing_series_type_listsรายการต้นทุน
    costing_series_type_list_detailsรายละเอียด
    costing_series_pricesราคาต้นทุน

    5.3 Cross-Database Relationships#

    pbc_incentive.invoice_reports.book_id      → pbc_center.bookings.book_id
    pbc_incentive.costing_reports.per_id       → pbc_center.periods.per_id
    pbc_report.invoice_full_payments.book_id   → pbc_center.bookings.book_id
    pbc_report.sales_booking_dailies.ser_id    → pbc_center.series.ser_id
    pbc_costing.costing_series_reports.ser_id  → pbc_center.series.ser_id
    วิธีจัดการใน Laravel ปัจจุบัน:

    6. Architecture Analysis: Next.js ต่อ DB ตรง vs มี API#

    Option A: Next.js + Prisma ต่อ Database ตรง#

    ข้อดีข้อเสีย
    Codebase เดียว ไม่ต้อง hop networkต้อง rewrite business logic 190 models ทั้งหมด
    Server Components query ได้ตรงCross-DB JOIN ทำยากใน Prisma
    Deploy ง่าย (1 service)PDF/Excel generation ต้อง rewrite ทั้งหมด
    Queue jobs ต้องใช้ BullMQ/Inngest แทน
    ไม่สามารถ migrate แบบค่อยเป็นค่อยไป

    Option B: Next.js Frontend + Laravel API (Headless) ✅ แนะนำเป็น Transition#

    ข้อดีข้อเสีย
    มี API พร้อมแล้ว 80+ endpoints ใน web-api.phpต้อง maintain 2 codebases
    Cross-DB relationships ทำงานได้ทันทีLaravel ต้อง run ต่อไป
    PDF/Excel/Queue ไม่ต้อง rewriteNetwork hop เพิ่ม latency
    ย้ายได้ทีละหน้า (gradual migration)
    Business logic ไม่ต้อง rewrite
    Team แยกทำ frontend/backend ได้

    Option C: Next.js Full Stack (เป้าหมายสุดท้าย)#

    ข้อดีข้อเสีย
    Codebase เดียว (TypeScript ทั้งหมด)ต้อง rewrite API ทั้งหมด
    Type-safe end-to-endCross-DB ต้อง handle เอง
    Modern toolingใช้เวลามาก

    Option D: Next.js Frontend + Node.js TypeScript API (แยก Backend)#

    แทนที่จะใช้ Next.js API Routes (Option C) ให้แยก backend เป็น Node.js TypeScript server ต่างหาก ทำให้ scale ได้อิสระ
    ข้อดีข้อเสีย
    TypeScript ทั้ง stack — type-safe end-to-endต้อง rewrite business logic ทั้งหมด
    แยก scale ได้ — frontend/backend scale อิสระ2 services ต้อง deploy + manage
    Backend ไม่ผูกกับ framework — เปลี่ยน frontend ไม่กระทบเวลา develop นานกว่า Option B
    ทีมใช้ภาษาเดียว — ไม่ต้องสลับ PHP/TSต้องเรียนรู้ ecosystem ใหม่ (Prisma, BullMQ)
    tRPC ได้ — type-safe API โดยไม่ต้องเขียน RESTCross-DB ต้อง handle เอง
    Modern tooling — testing, linting, CI/CD เหมือนกัน
    Monorepo ได้ — ใช้ Turborepo shared types

    Tech Stack แนะนำ#

    LayerLaravel (ปัจจุบัน)Node.js TypeScript (ใหม่)
    RuntimePHP 7.3/8.0Node.js 20 LTS
    FrameworkLaravel 8.75Fastify 5 หรือ Express 5 + ts-rest
    ORMEloquent (190 models)Prisma (type-safe, multi-schema)
    ValidationLaravel ValidationZod (runtime + type inference)
    AuthJWT + Sanctumjsonwebtoken + passport.js หรือ lucia-auth
    Queue/JobsLaravel Queue (8 jobs)BullMQ + Redis
    PDFDomPDF, mPDF, TCPDF, FPDFPuppeteer หรือ @react-pdf
    ExcelMaatwebsite/ExcelExcelJS
    EmailLaravel MailNodemailer + React Email
    File UploadLaravel Storage (local)multer + S3 SDK
    CacheLaravel Cacheioredis
    LoggingLaravel LogPino (structured JSON logs)
    TestingPHPUnitVitest + Supertest
    API Docs—Swagger/OpenAPI (auto-gen จาก Zod)

    Project Structure (Monorepo with Turborepo)#

    pbc-monorepo/
    ├── apps/
    │   ├── web/                          # Next.js Frontend
    │   │   ├── app/
    │   │   │   ├── (auth)/
    │   │   │   ├── (dashboard)/
    │   │   │   └── api/auth/            # Auth.js v5 only
    │   │   ├── components/
    │   │   ├── hooks/
    │   │   ├── stores/
    │   │   └── package.json
    │   │
    │   └── api/                          # Node.js TypeScript Backend
    │       ├── src/
    │       │   ├── index.ts              # Entry point
    │       │   ├── app.ts                # Fastify/Express setup
    │       │   ├── config/
    │       │   │   ├── database.ts       # Prisma client instances
    │       │   │   ├── redis.ts          # Redis connection
    │       │   │   ├── auth.ts           # JWT config
    │       │   │   └── env.ts            # Zod-validated env vars
    │       │   │
    │       │   ├── modules/              # Feature modules
    │       │   │   ├── auth/
    │       │   │   │   ├── auth.controller.ts
    │       │   │   │   ├── auth.service.ts
    │       │   │   │   ├── auth.schema.ts    # Zod schemas
    │       │   │   │   └── auth.routes.ts
    │       │   │   │
    │       │   │   ├── booking/
    │       │   │   │   ├── booking.controller.ts
    │       │   │   │   ├── booking.service.ts
    │       │   │   │   ├── booking.schema.ts
    │       │   │   │   ├── booking.routes.ts
    │       │   │   │   └── booking.constants.ts  # Status codes
    │       │   │   │
    │       │   │   ├── payment/
    │       │   │   │   ├── payment.controller.ts
    │       │   │   │   ├── payment.service.ts
    │       │   │   │   ├── payment.schema.ts
    │       │   │   │   └── payment.routes.ts
    │       │   │   │
    │       │   │   ├── series/
    │       │   │   ├── period/
    │       │   │   ├── agency/
    │       │   │   ├── ticket/
    │       │   │   ├── incentive/
    │       │   │   ├── invoice/
    │       │   │   ├── report/
    │       │   │   └── setting/
    │       │   │
    │       │   ├── jobs/                 # Background jobs (BullMQ)
    │       │   │   ├── export-booking.job.ts
    │       │   │   ├── export-invoice.job.ts
    │       │   │   ├── export-costing.job.ts
    │       │   │   ├── send-email.job.ts
    │       │   │   └── queue.ts          # Queue setup
    │       │   │
    │       │   ├── services/             # Shared services
    │       │   │   ├── pdf.service.ts    # Puppeteer PDF
    │       │   │   ├── excel.service.ts  # ExcelJS
    │       │   │   ├── email.service.ts  # Nodemailer
    │       │   │   ├── storage.service.ts # S3 file upload
    │       │   │   └── prefix-number.service.ts
    │       │   │
    │       │   ├── middleware/
    │       │   │   ├── auth.middleware.ts
    │       │   │   ├── rbac.middleware.ts
    │       │   │   └── error-handler.ts
    │       │   │
    │       │   └── utils/
    │       │       ├── thai-date.ts
    │       │       ├── currency.ts
    │       │       └── pagination.ts
    │       │
    │       ├── prisma/
    │       │   ├── schema-center.prisma
    │       │   ├── schema-incentive.prisma
    │       │   ├── schema-ticket.prisma
    │       │   ├── schema-report.prisma
    │       │   └── schema-costing.prisma
    │       │
    │       ├── tests/
    │       │   ├── booking.test.ts
    │       │   ├── payment.test.ts
    │       │   └── setup.ts
    │       │
    │       ├── tsconfig.json
    │       ├── Dockerfile
    │       └── package.json
    │
    ├── packages/
    │   ├── shared-types/                 # Shared TypeScript types
    │   │   ├── src/
    │   │   │   ├── booking.ts           # Booking types + status enums
    │   │   │   ├── payment.ts
    │   │   │   ├── series.ts
    │   │   │   ├── agency.ts
    │   │   │   ├── ticket.ts
    │   │   │   └── index.ts
    │   │   └── package.json
    │   │
    │   ├── shared-utils/                 # Shared utilities
    │   │   ├── src/
    │   │   │   ├── format-currency.ts
    │   │   │   ├── format-date.ts
    │   │   │   ├── thai-months.ts
    │   │   │   └── status-labels.ts
    │   │   └── package.json
    │   │
    │   └── api-client/                   # Type-safe API client
    │       ├── src/
    │       │   ├── client.ts             # Axios/fetch wrapper
    │       │   └── endpoints.ts          # All API endpoints
    │       └── package.json
    │
    ├── turbo.json
    ├── package.json
    └── docker-compose.yml

    Laravel → Node.js TypeScript: แมปรายตัว#

    Controller → Module Pattern#
    Eloquent Model → Prisma Schema#
    // Prisma: prisma/schema-center.prisma
    datasource db {
      provider = "mysql"
      url      = env("DATABASE_CENTER_URL")
    }
    
    model Booking {
      book_id                 BigInt             @id @default(autoincrement())
      book_code               String             @unique
      invoice_code            String?
      agen_id                 BigInt?
      user_id                 BigInt?
      per_id                  BigInt?
      bus_id                  BigInt?
      book_total              Decimal?           @db.Decimal(10, 2)
      book_amountgrandtotal   Decimal?           @db.Decimal(18, 2)
      book_pax                Int?               @db.SmallInt
      status                  Int?               @db.SmallInt
      status_cancel           Int?               @db.SmallInt @default(0)
      book_date               DateTime?
      book_cus_name           String?
      book_cus_tel            String?
      is_vat                  Int?               @db.SmallInt
      create_date             DateTime?
      update_date             DateTime?
      remark                  String?            @db.Text
      created_at              DateTime?
      updated_at              DateTime?
    
      // Relations (same database)
      period                  Period?            @relation(fields: [per_id], references: [per_id])
      bus                     BusList?           @relation(fields: [bus_id], references: [bus_id])
      agency                  Agency?            @relation(fields: [agen_id], references: [agen_id])
      user                    User?              @relation(fields: [user_id], references: [id])
      details                 BookingDetails[]
      additionals             BookingAdditional[]
      payments                Payment[]
      passports               Passport[]
      history                 BookingHistory[]
    
      @@map("booking")
    }
    Cross-Database Query (5 DBs)#
    Laravel Queue Jobs → BullMQ#
    Laravel Mail → Nodemailer + React Email#
    PDF Generation#

    Docker Compose (Development)#

    Migration Phases (Option D)#

    Phaseสิ่งที่ทำผลลัพธ์
    1Setup Turborepo, Prisma schemas ทั้ง 5 DBs, Auth (JWT), Middleware (RBAC)โครงสร้างพร้อม
    2ย้าย Series/Period/Booking API (core business)60% ของ traffic
    3ย้าย Payment/Invoice/Agency APIระบบการเงินทำงานบน Node.js
    4ย้าย Ticket/Incentive/Reports/Settings APIAPI ครบ
    5ย้าย Background jobs (BullMQ), PDF (Puppeteer), Excel (ExcelJS), Email (Nodemailer)ปลด PHP dependencies
    6สร้าง/ย้าย Next.js frontend (ใช้ API ใหม่)Frontend ใหม่
    7Integration testing, performance testing, ปลด LaravelLaravel-free

    เปรียบเทียบทุก Option#

    เกณฑ์Option B (Next.js + Laravel)Option C (Next.js Full Stack)Option D (Next.js + Node.js API)
    ภาษาTypeScript + PHPTypeScript onlyTypeScript only
    Deploy2 services (Next.js + PHP)1 service2-3 services (web + api + worker)
    Scaleแยก scale ได้scale เดียวแยก scale ได้ดีที่สุด
    Type Safetyไม่ end-to-endend-to-endend-to-end (tRPC/shared-types)
    Cross-DBEloquent (ง่าย)Prisma (ยาก)Prisma (ยาก แต่ handle ได้)
    PDF/ExcelPHP (พร้อมใช้)ต้อง rewriteต้อง rewrite
    QueueLaravel Queue (พร้อม)BullMQBullMQ
    เวลาย้ายเร็วสุด ~20 สัปดาห์~36 สัปดาห์~36 สัปดาห์
    Maintenance2 ภาษา 2 ecosystem1 ภาษา 1 ecosystem1 ภาษา แต่ยืดหยุ่นกว่า
    เหมาะกับย้ายเร็ว ใช้ของเดิมทีมเล็ก deploy ง่ายทีมโต ระบบใหญ่ long-term

    🏆 คำแนะนำ (อัพเดท)#

    เลือกตามบริบท:
    ต้องการเร็ว ทีมมี PHP dev → Option B (ใช้ Laravel API ที่มีอยู่)
    ต้องการ full TypeScript ทีมเล็ก → Option C (Next.js full stack)
    ต้องการ full TypeScript ระบบใหญ่ ทีมโต → Option D (Node.js API แยก) — เหมาะกับ PBC ที่มี 5 databases, 150+ tables, 8 queue jobs
    เหตุผล:
    1.
    มี REST API พร้อมแล้ว - web-api.php มี 80+ endpoints ที่ Vue ใช้อยู่ Next.js ใช้ได้เลย
    2.
    Cross-DB ซับซ้อน - มี 5 databases ที่ reference กัน Eloquent จัดการได้ดีกว่า Prisma
    3.
    PDF/Excel ยังจำเป็น - มี 4 PDF engines + export jobs ที่ทำงานใน PHP rewrite ใช้เวลามาก
    4.
    ลดความเสี่ยง - ย้ายทีละส่วน ทดสอบได้ ไม่ต้อง big bang migration
    5.
    Option D ดีที่สุด long-term - TypeScript ทั้ง stack, scale ได้, shared types, monorepo

    7. HeroUI Component Mapping#

    7.1 Layout Components#

    ปัจจุบัน (Bootstrap/Vue)HeroUI/Reactหน้าที่ใช้
    Bootstrap NavbarNavbarHeader (ทุกหน้า)
    Custom Sidebar (Vue)Custom Sidebar + ListboxSidebar navigation
    Bootstrap Containerdiv with TailwindLayout wrapper
    Bootstrap CardCard, CardHeader, CardBodyDashboard widgets, Detail views
    Bootstrap ModalModal, ModalContentBooking cancel, Agency select, etc.
    Bootstrap TabsTabs, TabTicket tabs, Period tabs

    7.2 Form Components#

    ปัจจุบันHeroUIหน้าที่ใช้
    Bootstrap Form InputInputทุก form
    Bootstrap SelectSelect, SelectItemDropdown ทั่วไป
    vue-multiselectAutocompleteAgency select, Country select
    selectizeAutocompleteTag inputs, Search
    Bootstrap TextareaTextareaหมายเหตุ, โปรแกรม
    Bootstrap CheckboxCheckbox, CheckboxGroupFilter, Settings
    Bootstrap RadioRadioGroup, Radioประเภทห้อง, ช่องทางชำระ
    Bootstrap SwitchSwitchToggle status
    vue2-datepickerDatePicker (custom)วันเดินทาง, วันชำระ
    daterangepicker (jQuery)react-date-range + PopoverReport filters

    7.3 Data Display Components#

    ปัจจุบันHeroUIหน้าที่ใช้
    Bootstrap Table + DataTableTable with useAsyncListรายการ Booking, Payment, Period
    Bootstrap PaginationPaginationทุกหน้า list
    Bootstrap BadgeChipStatus badges
    Bootstrap AlertAlert หรือ toast libraryแจ้งเตือน
    SweetAlert2Modal (confirm) + Sonner (toast)Confirm actions, Success/Error
    Bootstrap ProgressProgressUpload progress
    Bootstrap SpinnerSpinnerLoading states
    Bootstrap TooltipTooltipHelp text
    ApexChartsRecharts / TremorDashboard charts

    7.4 Action Components#

    ปัจจุบันHeroUIหน้าที่ใช้
    Bootstrap ButtonButtonทุก action
    Bootstrap DropdownDropdown, DropdownMenuAction menus
    Bootstrap BreadcrumbBreadcrumbsNavigation path
    Dragula (drag & drop)@dnd-kit/coreBanner sorting

    7.5 Iconify — ระบบ Icon#

    ระบบปัจจุบันใช้ Material Design Icons (Google Fonts Material Symbols) ผ่าน CSS class ใน Vue components ระบบใหม่แนะนำใช้ Iconify เพราะรวม icon sets กว่า 200,000+ ไอคอนในที่เดียว รองรับ Material Design, Lucide, Tabler, FontAwesome และอื่นๆ

    ติดตั้ง#

    การใช้งานพื้นฐาน#

    import { Icon } from '@iconify/react';
    
    // Material Design Icons (เหมือนระบบเดิม)
    <Icon icon="mdi:home" width={24} />
    <Icon icon="mdi:account-circle" width={24} />
    <Icon icon="mdi:calendar-month" width={24} />
    <Icon icon="mdi:credit-card" width={24} />
    
    // เปลี่ยนสี
    <Icon icon="mdi:check-circle" className="text-green-500" width={20} />
    <Icon icon="mdi:close-circle" className="text-red-500" width={20} />
    
    // ใช้ร่วมกับ HeroUI Button
    import { Button } from '@heroui/react';
    
    <Button startContent={<Icon icon="mdi:plus" width={20} />}>
      สร้าง Booking
    </Button>
    
    <Button color="danger" startContent={<Icon icon="mdi:delete" width={20} />}>
      ยกเลิก
    </Button>

    Mapping จาก Material Symbols (ระบบเดิม) → Iconify#

    ระบบเดิม (Material Symbols)Iconify (mdi set)ใช้ในหน้า
    <span class="material-symbols-outlined">home</span><Icon icon="mdi:home" />Sidebar
    <span class="material-symbols-outlined">person</span><Icon icon="mdi:account" />User menu
    <span class="material-symbols-outlined">flight</span><Icon icon="mdi:airplane" />Series/Tour
    <span class="material-symbols-outlined">event</span><Icon icon="mdi:calendar" />Period
    <span class="material-symbols-outlined">receipt_long</span><Icon icon="mdi:receipt" />Invoice
    <span class="material-symbols-outlined">payments</span><Icon icon="mdi:cash-multiple" />Payment
    <span class="material-symbols-outlined">confirmation_number</span><Icon icon="mdi:ticket-outline" />Ticket
    <span class="material-symbols-outlined">settings</span><Icon icon="mdi:cog" />Settings
    <span class="material-symbols-outlined">search</span><Icon icon="mdi:magnify" />Search bars
    <span class="material-symbols-outlined">edit</span><Icon icon="mdi:pencil" />Edit buttons
    <span class="material-symbols-outlined">delete</span><Icon icon="mdi:delete" />Delete buttons
    <span class="material-symbols-outlined">visibility</span><Icon icon="mdi:eye" />View detail
    <span class="material-symbols-outlined">download</span><Icon icon="mdi:download" />Export
    <span class="material-symbols-outlined">print</span><Icon icon="mdi:printer" />Print
    <span class="material-symbols-outlined">notifications</span><Icon icon="mdi:bell" />Notification
    <span class="material-symbols-outlined">check_circle</span><Icon icon="mdi:check-circle" />Approved status
    <span class="material-symbols-outlined">cancel</span><Icon icon="mdi:close-circle" />Cancelled status
    <span class="material-symbols-outlined">schedule</span><Icon icon="mdi:clock-outline" />Pending status
    <span class="material-symbols-outlined">add</span><Icon icon="mdi:plus" />Create buttons
    <span class="material-symbols-outlined">arrow_back</span><Icon icon="mdi:arrow-left" />Back navigation
    <span class="material-symbols-outlined">filter_list</span><Icon icon="mdi:filter" />Filter
    <span class="material-symbols-outlined">sort</span><Icon icon="mdi:sort" />Sort
    <span class="material-symbols-outlined">upload_file</span><Icon icon="mdi:upload" />Upload slip
    <span class="material-symbols-outlined">description</span><Icon icon="mdi:file-document" />Document
    <span class="material-symbols-outlined">group</span><Icon icon="mdi:account-group" />Agency
    <span class="material-symbols-outlined">bar_chart</span><Icon icon="mdi:chart-bar" />Reports
    <span class="material-symbols-outlined">luggage</span><Icon icon="mdi:bag-suitcase" />Tour/Travel
    <span class="material-symbols-outlined">hotel</span><Icon icon="mdi:bed" />Room
    <span class="material-symbols-outlined">passport</span><Icon icon="mdi:passport" />Passport

    สร้าง Icon Component Wrapper (แนะนำ)#

    // components/ui/app-icon.tsx
    import { Icon, IconProps } from '@iconify/react';
    
    // กำหนด icon names ที่ใช้บ่อยในระบบ
    const iconMap = {
      // Navigation
      home: 'mdi:home',
      dashboard: 'mdi:view-dashboard',
      back: 'mdi:arrow-left',
    
      // Actions
      add: 'mdi:plus',
      edit: 'mdi:pencil',
      delete: 'mdi:delete',
      save: 'mdi:content-save',
      search: 'mdi:magnify',
      filter: 'mdi:filter',
      sort: 'mdi:sort',
      download: 'mdi:download',
      upload: 'mdi:upload',
      print: 'mdi:printer',
      refresh: 'mdi:refresh',
    
      // Status
      approved: 'mdi:check-circle',
      pending: 'mdi:clock-outline',
      cancelled: 'mdi:close-circle',
      rejected: 'mdi:alert-circle',
      warning: 'mdi:alert',
    
      // Business
      booking: 'mdi:book-open-variant',
      payment: 'mdi:cash-multiple',
      invoice: 'mdi:receipt',
      ticket: 'mdi:ticket-outline',
      tour: 'mdi:bag-suitcase',
      flight: 'mdi:airplane',
      hotel: 'mdi:bed',
      passport: 'mdi:passport',
      visa: 'mdi:card-account-details',
      calendar: 'mdi:calendar',
      bus: 'mdi:bus',
    
      // Users & Agency
      user: 'mdi:account',
      users: 'mdi:account-group',
      agency: 'mdi:domain',
      sales: 'mdi:account-tie',
      settings: 'mdi:cog',
    
      // Misc
      notification: 'mdi:bell',
      email: 'mdi:email',
      phone: 'mdi:phone',
      document: 'mdi:file-document',
      image: 'mdi:image',
      chart: 'mdi:chart-bar',
      money: 'mdi:currency-thb',
      eye: 'mdi:eye',
      eyeOff: 'mdi:eye-off',
      close: 'mdi:close',
      menu: 'mdi:menu',
      more: 'mdi:dots-vertical',
      link: 'mdi:link',
      copy: 'mdi:content-copy',
      info: 'mdi:information',
    } as const;
    
    type AppIconName = keyof typeof iconMap;
    
    interface AppIconProps extends Omit<IconProps, 'icon'> {
      name: AppIconName;
    }
    
    export function AppIcon({ name, width = 24, ...props }: AppIconProps) {
      return <Icon icon={iconMap[name]} width={width} {...props} />;
    }

    ใช้งาน AppIcon#

    import { AppIcon } from '@/components/ui/app-icon';
    
    // ใช้ชื่อที่เข้าใจง่าย แทน icon string
    <AppIcon name="booking" />
    <AppIcon name="approved" className="text-green-500" />
    <AppIcon name="cancelled" className="text-red-500" width={20} />
    <AppIcon name="money" className="text-blue-500" />
    
    // ใน Sidebar
    <SidebarItem icon={<AppIcon name="dashboard" />} label="Dashboard" href="/dashboard" />
    <SidebarItem icon={<AppIcon name="booking" />} label="Booking" href="/booking" />
    <SidebarItem icon={<AppIcon name="payment" />} label="Payment" href="/payment" />
    <SidebarItem icon={<AppIcon name="ticket" />} label="Ticket" href="/ticket" />
    
    // ใน Status Chip
    function StatusChip({ status }: { status: number }) {
      const config: Record<number, { icon: AppIconName; color: string; label: string }> = {
        0:  { icon: 'pending',   color: 'warning', label: 'รอดำเนินการ' },
        1:  { icon: 'approved',  color: 'success', label: 'อนุมัติ' },
        9:  { icon: 'rejected',  color: 'danger',  label: 'ปฏิเสธ' },
        40: { icon: 'cancelled', color: 'default', label: 'ยกเลิก' },
      };
    
      const { icon, color, label } = config[status] ?? config[0];
    
      return (
        <Chip color={color} startContent={<AppIcon name={icon} width={16} />}>
          {label}
        </Chip>
      );
    }

    Icon Sets ที่แนะนำ (เลือกใช้ผ่าน Iconify ได้ทั้งหมด)#

    Icon SetPrefixจำนวนเหมาะกับ
    Material Design Iconsmdi:7,000+ใกล้เคียงระบบเดิมที่สุด
    Lucidelucide:1,500+สวย minimal modern
    Tabler Iconstabler:5,000+stroke-based สวยงาม
    Phosphor Iconsph:7,000+หลากหลาย weight
    Solarsolar:7,000+modern, สวยมาก

    Offline Mode (ไม่ต้องโหลด icon จาก CDN)#

    // ใช้ addCollection เพื่อ bundle icons ใน build
    import { addCollection } from '@iconify/react';
    import mdiIcons from '@iconify-json/mdi/icons.json';
    
    addCollection(mdiIcons);
    หรือใช้ tree-shaking เพื่อ bundle เฉพาะ icons ที่ใช้:
    // import เฉพาะ icon ที่ต้องการ (tree-shakeable)
    import homeIcon from '@iconify-icons/mdi/home';
    import accountIcon from '@iconify-icons/mdi/account';
    
    import { Icon } from '@iconify/react/dist/offline';
    
    <Icon icon={homeIcon} />
    <Icon icon={accountIcon} />

    ค้นหา Icons#

    Iconify Search: icon-sets.iconify.design
    Icones: icones.js.org — UI สวยกว่า ค้นหาง่าย

    8. Migration Phases#

    Phase 1: Foundation (สัปดาห์ 1-4)#

    เป้าหมาย: Setup โปรเจค Next.js และ core infrastructure
    สร้าง Next.js project:
    ├── app/
    │   ├── (auth)/           # Login, 2FA
    │   │   ├── login/
    │   │   └── two-factor/
    │   ├── (dashboard)/      # Main layout with sidebar
    │   │   ├── layout.tsx    # Sidebar + Header
    │   │   └── page.tsx      # Home redirect
    │   └── api/
    │       └── auth/         # Auth.js v5
    ├── components/
    │   ├── ui/               # HeroUI wrappers
    │   ├── layout/           # Header, Sidebar
    │   └── shared/           # Reusable components
    ├── lib/
    │   ├── api-client.ts     # Axios → Laravel /rest/*
    │   ├── auth.ts           # Auth.js config
    │   └── utils.ts          # Helpers
    ├── types/                # TypeScript types
    └── middleware.ts          # Auth + RBAC middleware
    Tasks:
    Init Next.js 16 + React 19 + TypeScript 5.x + Tailwind CSS v4 + HeroUI v2
    Setup Auth.js v5 (next-auth v5) — auth.ts config, Edge middleware, JWT callbacks
    สร้าง API client เชื่อมต่อ Laravel /rest/*
    สร้าง Layout (Sidebar + Header) ด้วย Server Components + Streaming
    Implement RBAC middleware (auth, role scope, country) ผ่าน Auth.js v5 middleware
    Setup Iconify (@iconify/react + @iconify-json/mdi) และสร้าง AppIcon wrapper
    สร้าง shared components (DataTable, StatusChip, DateRangePicker)
    Setup next-intl v4 (Thai/English)
    ใช้ Turbopack เป็น default dev server (built-in Next.js 16)

    Phase 2: Dashboard & Read-Only Pages (สัปดาห์ 5-8)#

    Tasks:
    Sales Dashboard (charts, metrics)
    Admin Dashboard
    Series/Tour listing
    Period listing with booking summary
    My Booking page
    Rush to Sell page
    All Booking listing
    Payment Due listing

    Phase 3: Booking Module (สัปดาห์ 9-14)#

    Tasks:
    Booking creation flow (multi-step form)
    เลือก Period → เลือก Bus → กรอกข้อมูลผู้เดินทาง → Room type → Add-ons
    Booking detail/overview page
    Booking payment tab
    Booking passport tab (image upload)
    Booking traveler tab
    Booking cancel flow
    Booking refund/deduct
    Booking email notifications
    Print quotation

    Phase 4: Payment & Invoice (สัปดาห์ 15-18)#

    Tasks:
    Payment list (filter, search, pagination)
    Create payment (upload slip, select channel)
    Payment approval workflow (approve/reject)
    Invoice list and detail
    Invoice tax report
    Receipt management
    Invoice full payment

    Phase 5: Operations & Ticket (สัปดาห์ 19-22)#

    Tasks:
    Tour CRUD (create, edit, delete series)
    Period CRUD (create, copy, edit)
    Bus management (add, edit, delete)
    Cost seat management
    Estimate management
    Series program management
    Ticket series/pre-sale/manage
    Ticket payment and refund
    Ticket invoice

    Phase 6: Reports & Costing (สัปดาห์ 23-26)#

    Tasks:
    Invoice reports
    Payment reports (with export)
    Period reports (with export)
    Costing reports (series, booking, hold ticket)
    Income reports
    Estimates reports
    Commission reports
    Agency reports
    Period monthly reports
    Invoice full payment reports
    Excel export (ExcelJS)
    PDF generation (Puppeteer / keep Laravel)

    Phase 7: Settings & Admin (สัปดาห์ 27-30)#

    Tasks:
    Agency management (sales, company, credit shell, API tokens)
    Settings CRUD (airline, route, country, city, bank)
    Banner management (web, flashsale)
    Promotion management
    Land operation management
    User management (CRUD, role, country permissions)
    Incentive trips module
    Event/Email management

    Phase 8: API Migration & Cleanup (สัปดาห์ 31-36)#

    Tasks:
    Setup Prisma multi-schema (5 databases)
    Migrate API endpoints ทีละ module (Settings → Reports → Payment → Booking)
    Background job migration (BullMQ/Inngest)
    PDF migration (Puppeteer)
    Performance testing
    Remove Laravel dependency
    Production deployment

    9. Technical Considerations#

    9.1 PDF Generation#

    ปัจจุบันใช้กับแนวทาง
    DomPDFInvoice, ReceiptPuppeteer หรือ keep Laravel service
    mPDFBooking quotationPuppeteer (รองรับ Thai fonts ดีกว่า)
    TCPDFReportsPuppeteer
    FPDF/FPDICustom templates@react-pdf/renderer
    แนะนำ: Phase 1-6 ใช้ Laravel PDF service ต่อ → Phase 7+ ย้ายเป็น Puppeteer

    9.2 Excel Export#

    ปัจจุบันแนวทาง
    Maatwebsite/ExcelExcelJS (server-side)
    PhpSpreadsheetExcelJS
    fast-excelSheetJS (client-side สำหรับ export เล็ก)
    Queue Jobs → Background Processing:
    ExportBooking → BullMQ job
    ExportInvoiceFullPaymentJob → BullMQ job
    ExportCostingByCountryJob → BullMQ job
    ExportCostingZipJob → BullMQ job
    ExportIncomeReportAll → BullMQ job

    9.3 State Management#

    Vuex ModuleReact EquivalentLibrary
    authAuth.js v5 sessionAuth.js v5 (next-auth v5)
    uiUI storeZustand
    checkoutBooking form stateZustand
    agency-modalModal stateZustand
    seriesServer stateTanStack Query
    salesDashboardServer stateTanStack Query
    page-bookingServer stateTanStack Query
    report/*Server stateTanStack Query
    หลักการ:
    Server State (data จาก API) → TanStack Query (caching, refetching)
    Client State (UI, forms) → Zustand (lightweight)
    Auth State → Auth.js v5

    9.4 Authentication Migration (Auth.js v5)#

    Auth.js v5 (next-auth v5) มีการเปลี่ยนแปลงจาก v4 หลายจุด:
    ย้าย config ไปอยู่ที่ auth.ts ที่ root (ไม่ใช่ app/api/auth/ แล้ว)
    ใช้ auth() helper แทน getServerSession()
    Middleware ใช้ auth export ตรง ไม่ต้อง withAuth
    Edge-compatible ทำงานกับ Next.js 16 middleware ได้เลย

    9.5 RBAC Middleware (Next.js 16)#

    Next.js 16 middleware ทำงานที่ Edge Runtime — Auth.js v5 รองรับ Edge ได้ทันที

    9.6 API Client Setup#

    9.7 Internationalization#

    9.8 File Upload Strategy#

    ปัจจุบันแนวทาง
    Laravel Storage (local)S3-compatible + presigned URLs
    Passport imagesUpload via presigned URL → store path in DB
    Payment slipsUpload via presigned URL
    Guarantee documentsUpload via presigned URL

    10. Next.js Project Structure (แนะนำ)#

    nextjs-pbc/
    ├── app/
    │   ├── (auth)/
    │   │   ├── login/page.tsx
    │   │   └── two-factor/page.tsx
    │   ├── (dashboard)/
    │   │   ├── layout.tsx                    # Sidebar + Header + Auth guard
    │   │   ├── page.tsx                      # Home / redirect to dashboard
    │   │   ├── dashboard/page.tsx
    │   │   ├── sales-dashboard/page.tsx
    │   │   ├── admin-dashboard/page.tsx
    │   │   ├── booking/
    │   │   │   ├── page.tsx                  # Booking list
    │   │   │   ├── all/page.tsx              # All bookings
    │   │   │   ├── create/[busId]/page.tsx   # Create booking
    │   │   │   └── [id]/
    │   │   │       ├── page.tsx              # Booking detail
    │   │   │       ├── overview/page.tsx
    │   │   │       ├── payment/page.tsx
    │   │   │       ├── passport/page.tsx
    │   │   │       └── traveler/page.tsx
    │   │   ├── my-booking/page.tsx
    │   │   ├── order/[id]/page.tsx
    │   │   ├── series-tour/page.tsx
    │   │   ├── tours/
    │   │   │   ├── create/page.tsx
    │   │   │   └── [id]/
    │   │   │       ├── edit/page.tsx
    │   │   │       └── period/
    │   │   │           ├── create/page.tsx
    │   │   │           └── [periodId]/edit/page.tsx
    │   │   ├── period/
    │   │   │   ├── page.tsx
    │   │   │   ├── rush-to-sell/page.tsx
    │   │   │   └── [periodId]/bus/[busId]/page.tsx
    │   │   ├── payment/
    │   │   │   ├── page.tsx
    │   │   │   ├── create/page.tsx
    │   │   │   └── [id]/edit/page.tsx
    │   │   ├── payment-due/page.tsx
    │   │   ├── invoice/
    │   │   │   ├── page.tsx
    │   │   │   ├── list/page.tsx
    │   │   │   ├── [id]/page.tsx
    │   │   │   └── request/page.tsx
    │   │   ├── invoice-tax/page.tsx
    │   │   ├── invoices/
    │   │   │   ├── page.tsx
    │   │   │   └── request/page.tsx
    │   │   ├── receipt/page.tsx
    │   │   ├── ticket/
    │   │   │   ├── series/page.tsx
    │   │   │   ├── pre-sale/page.tsx
    │   │   │   ├── manage/page.tsx
    │   │   │   └── series/[id]/period/[periodId]/edit/page.tsx
    │   │   ├── incentive/
    │   │   │   ├── page.tsx
    │   │   │   ├── create/page.tsx
    │   │   │   └── [id]/
    │   │   │       ├── edit/page.tsx
    │   │   │       ├── program/page.tsx
    │   │   │       ├── payment/page.tsx
    │   │   │       └── traveler/page.tsx
    │   │   ├── reports/
    │   │   │   ├── invoice/page.tsx
    │   │   │   ├── payment/page.tsx
    │   │   │   ├── period/page.tsx
    │   │   │   ├── costing/page.tsx
    │   │   │   ├── income/page.tsx
    │   │   │   ├── estimates/page.tsx
    │   │   │   ├── commission/page.tsx
    │   │   │   ├── agency/page.tsx
    │   │   │   └── invoice-full-payment/page.tsx
    │   │   ├── agency/
    │   │   │   ├── sales/
    │   │   │   │   ├── page.tsx
    │   │   │   │   ├── create/page.tsx
    │   │   │   │   └── [id]/edit/page.tsx
    │   │   │   └── company/
    │   │   │       ├── page.tsx
    │   │   │       ├── create/page.tsx
    │   │   │       └── [id]/edit/page.tsx
    │   │   ├── settings/
    │   │   │   ├── airline/page.tsx
    │   │   │   ├── route/page.tsx
    │   │   │   ├── country/page.tsx
    │   │   │   ├── city/page.tsx
    │   │   │   ├── bank/page.tsx
    │   │   │   ├── banner/page.tsx
    │   │   │   ├── flashsale-banner/page.tsx
    │   │   │   ├── land-operation/page.tsx
    │   │   │   └── promotion/page.tsx
    │   │   ├── event/
    │   │   │   ├── page.tsx
    │   │   │   └── email/page.tsx
    │   │   └── account/page.tsx
    │   ├── api/
    │   │   ├── auth/[...nextauth]/route.ts   # Auth.js v5 route handler
    │   │   └── ... (Phase 8: migrate from Laravel)
    │   ├── forbidden.tsx                      # 403 page (Next.js 16)
    │   ├── unauthorized.tsx                   # 401 page (Next.js 16)
    │   └── layout.tsx
    ├── auth.ts                        # Auth.js v5 config (root level)
    ├── components/
    │   ├── ui/                        # HeroUI v2 wrapper components
    │   │   └── app-icon.tsx           # Iconify wrapper
    │   ├── layout/
    │   │   ├── header.tsx
    │   │   ├── sidebar.tsx
    │   │   └── breadcrumb.tsx
    │   ├── booking/                   # Booking-specific components
    │   ├── payment/                   # Payment-specific components
    │   ├── tour/                      # Tour-specific components
    │   └── shared/
    │       ├── data-table.tsx         # Reusable table with pagination
    │       ├── status-chip.tsx        # Status badge with Iconify
    │       ├── date-range-picker.tsx
    │       ├── confirm-modal.tsx
    │       └── file-upload.tsx
    ├── hooks/
    │   ├── use-bookings.ts            # TanStack Query v5 hooks
    │   ├── use-payments.ts
    │   ├── use-series.ts
    │   └── use-auth.ts
    ├── lib/
    │   ├── api-client.ts              # fetch wrapper → Laravel /rest/*
    │   ├── data.ts                    # 'use cache' data fetching functions
    │   ├── utils.ts                   # Helpers
    │   └── constants.ts               # Status codes, Thai months
    ├── stores/
    │   ├── ui-store.ts                # Zustand: sidebar, modals
    │   └── checkout-store.ts          # Zustand: booking form
    ├── types/
    │   ├── booking.ts
    │   ├── payment.ts
    │   ├── series.ts
    │   ├── period.ts
    │   ├── agency.ts
    │   ├── ticket.ts
    │   ├── incentive.ts
    │   ├── user.ts
    │   └── next-auth.d.ts            # Auth.js v5 type extensions
    ├── messages/
    │   ├── th.json                    # Thai translations (next-intl v4)
    │   └── en.json                    # English translations
    ├── prisma/                        # Phase 8
    │   ├── schema-center.prisma
    │   ├── schema-incentive.prisma
    │   ├── schema-ticket.prisma
    │   ├── schema-report.prisma
    │   └── schema-costing.prisma
    ├── middleware.ts                   # Auth.js v5 edge middleware + RBAC
    ├── next.config.ts                 # Turbopack is default in Next.js 16
    ├── tsconfig.json
    └── package.json

    11. Existing REST API Endpoints (พร้อมใช้กับ Next.js)#

    API ที่มีอยู่แล้วใน web-api.php (prefix: /rest) สามารถใช้ได้ทันทีกับ Next.js:

    Dashboard APIs#

    GET /rest/dashboard/sales - ข้อมูล Sales Dashboard
    GET /rest/dashboard/country - ข้อมูลตามประเทศ
    GET /rest/dashboard/admin - ข้อมูล Admin Dashboard
    GET /rest/dashboard/sales/top - Top Sales data

    Booking APIs#

    GET /rest/allbooking - รายการ Booking ทั้งหมด
    GET /rest/allbooking/init - Initialize Booking page
    GET /rest/allbooking/summarize - สรุป Booking
    POST /rest/allbooking/save - สร้าง Booking
    PUT /rest/allbooking/update - อัพเดท Booking
    DELETE /rest/allbooking/{id} - ลบ Booking
    GET /rest/allbooking/export - Export Booking

    Payment APIs#

    GET /rest/payments - รายการ Payment
    GET /rest/payments/intit - Initialize
    GET /rest/payments/analytics - วิเคราะห์
    GET /rest/payments/export - Export

    Report APIs#

    GET /rest/report/payments - รายงาน Payment
    GET /rest/report/payments/init - Initialize
    GET /rest/report/payments/summarize - สรุป
    GET /rest/report/payments/export - Export
    GET /rest/report/period - รายงาน Period
    GET /rest/report/actual-estimates - Actual vs Estimates
    GET /rest/costing - รายงาน Costing
    GET /rest/income - รายงาน Income
    GET /rest/estimates-reports - รายงาน Estimates
    GET /rest/report/committion - รายงาน Commission

    Series/Period APIs#

    GET /rest/series - รายการ Series
    POST /rest/series/save - สร้าง Series
    PUT /rest/series/update - อัพเดท Series
    GET /rest/period - รายการ Period
    POST /rest/period/save - สร้าง Period

    Invoice APIs#

    GET /rest/invoices - รายการ Invoice
    POST /rest/invoices/update/{id} - อัพเดท Invoice

    Notification APIs#

    GET /rest/notifications - รายการแจ้งเตือน
    POST /rest/notifications/markAsRead - อ่านแล้ว
    POST /rest/notifications/approve - อนุมัติ

    Authentication APIs#

    POST /rest/auth/confirmPassword - ยืนยันรหัสผ่าน

    สรุป#

    โปรเจค PBC เป็นระบบขนาดใหญ่ที่มีความซับซ้อนสูง การ migrate ไป Next.js 16 ควรทำแบบ Phased Approach:
    1.
    ใช้ Laravel เป็น API ก่อน (มี 80+ endpoints พร้อมใช้)
    2.
    สร้าง Next.js 16 frontend ด้วย HeroUI v2 + React 19 ทีละ module
    3.
    ค่อยๆ ย้าย API ไปอยู่ใน Node.js TypeScript (Option D) หรือ Next.js API routes (Option C)
    4.
    ปลด Laravel เมื่อ migration เสร็จสมบูรณ์

    Stack สรุป#

    Next.js 16 + React 19 + TypeScript 5.x
    ├── UI:       HeroUI v2 + Tailwind CSS v4 + Iconify
    ├── Auth:     Auth.js v5 (next-auth v5) + JWT
    ├── State:    Zustand + TanStack Query v5
    ├── i18n:     next-intl v4
    ├── Build:    Turbopack (built-in)
    ├── ORM:      Prisma 6 (multi-schema)
    ├── Cache:    use cache + cacheTag + cacheLife
    └── Backend:  Node.js TypeScript (Fastify) หรือ Laravel API (transition)
    ประมาณการระยะเวลา: 36 สัปดาห์ (9 เดือน) สำหรับ migration ทั้งหมด
    Modified at 2026-03-25 09:08:55
    Previous
    Page
    Next
    Online vs Source Code
    Built with