Sửa lỗi backend để chạy phần đăng ký

This commit is contained in:
admin committed 2026-06-13 17:41:11 +07:00
1 parent c08aad3f68
commit 6913bbae48
70 files changed
+1289 -203

No files matched your search

+15 -5
View File
@@ -1,17 +1,27 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { LandingPage } from './LandingPage'; import { LandingPage } from './LandingPage.js';
import { TourDetailPage } from './TourDetailPage'; import { TourDetailPage } from './TourDetailPage.js';
import { ExploreMap } from './ExploreMap'; import { ExploreMap } from './ExploreMap.js';
import { SignupPage } from './SignupPage'; import { SignupPage } from './SignupPage.js';
const App = () => { const App = () => {
type View = 'landing' | 'explore' | 'detail' | 'signup'; type View = 'landing' | 'explore' | 'detail' | 'signup';
const [view, setView] = useState<View>('landing'); const [view, setView] = useState<View>('landing');
const [isInitialSetup, setIsInitialSetup] = useState(false);
useEffect(() => {
// Kiểm tra xem hệ thống đã được cài đặt chưa
fetch('http://localhost:3001/api/v1/auth/status')
.then(res => res.json())
.then(data => setIsInitialSetup(data.isInitialSetup))
.catch(() => setIsInitialSetup(false));
}, [view]);
return ( return (
<div className="app-container"> <div className="app-container">
{view === 'landing' && ( {view === 'landing' && (
<LandingPage <LandingPage
isInitialSetup={isInitialSetup}
onContinue={() => setView('explore')} onContinue={() => setView('explore')}
onGoToSignup={() => setView('signup')} onGoToSignup={() => setView('signup')}
onGoToMap={() => setView('explore')} onGoToMap={() => setView('explore')}
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react'; import React, { useState, useMemo } from 'react';
import { Wallet, Users, Info } from 'lucide-react'; import { Wallet, Users, Info } from 'lucide-react';
import { useTourStore } from './useTourStore'; import { useTourStore } from './useTourStore.js';
export const ExpenseManager = () => { export const ExpenseManager = () => {
const { legs } = useTourStore(); const { legs } = useTourStore();
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'; import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';
import L from 'leaflet'; import L from 'leaflet';
import 'leaflet/dist/leaflet.css'; import 'leaflet/dist/leaflet.css';
import { useTourStore } from './useTourStore'; import { useTourStore } from './useTourStore.js';
import { X, Navigation, Image as ImageIcon } from 'lucide-react'; import { X, Navigation, Image as ImageIcon } from 'lucide-react';
// Fix lỗi icon mặc định của Leaflet // Fix lỗi icon mặc định của Leaflet
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { format, differenceInMinutes, parseISO } from 'date-fns'; import { format, differenceInMinutes, parseISO } from 'date-fns';
import { CheckCircle2, Circle, Clock, MapPin, AlertCircle } from 'lucide-react'; import { CheckCircle2, Circle, Clock, MapPin, AlertCircle } from 'lucide-react';
import { useTourStore } from './useTourStore'; import { useTourStore } from './useTourStore.js';
const TimeVariance = ({ planned, actual }: { planned: string, actual: string | null }) => { const TimeVariance = ({ planned, actual }: { planned: string, actual: string | null }) => {
if (!actual) return null; if (!actual) return null;
+30 -4
View File
@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react'; import React, { useState, useMemo } from 'react';
import { LogIn, Compass, ArrowRight, Map as MapIcon, UserPlus } from 'lucide-react'; import { LogIn, Compass, ArrowRight, Map as MapIcon, UserPlus, ShieldCheck } from 'lucide-react';
import { LoginModal } from './LoginModal'; import { LoginModal } from './LoginModal.js';
const TRAVEL_IMAGES = [ const TRAVEL_IMAGES = [
"https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?auto=format&fit=crop&q=80", "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?auto=format&fit=crop&q=80",
@@ -15,9 +15,10 @@ interface LandingPageProps {
onContinue?: () => void; onContinue?: () => void;
onGoToSignup?: () => void; onGoToSignup?: () => void;
onGoToMap?: () => void; onGoToMap?: () => void;
isInitialSetup?: boolean;
} }
export const LandingPage: React.FC<LandingPageProps> = ({ onContinue, onGoToSignup, onGoToMap }) => { export const LandingPage: React.FC<LandingPageProps> = ({ onContinue, onGoToSignup, onGoToMap, isInitialSetup }) => {
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
// Chọn ngẫu nhiên một hình ảnh khi người dùng truy cập trang // Chọn ngẫu nhiên một hình ảnh khi người dùng truy cập trang
@@ -68,16 +69,41 @@ export const LandingPage: React.FC<LandingPageProps> = ({ onContinue, onGoToSign
<div className="flex-1 md:w-1/2 flex flex-col justify-center items-center bg-white p-8 lg:p-16 h-full overflow-y-auto"> <div className="flex-1 md:w-1/2 flex flex-col justify-center items-center bg-white p-8 lg:p-16 h-full overflow-y-auto">
<div className="max-w-md w-full space-y-12"> <div className="max-w-md w-full space-y-12">
<div className="space-y-4"> <div className="space-y-4">
{isInitialSetup ? (
<>
<div className="inline-flex items-center px-3 py-1 rounded-full bg-amber-50 text-amber-700 text-xs font-bold border border-amber-100 mb-2">
<ShieldCheck className="w-3 h-3 mr-1" /> CẤU HÌNH HỆ THỐNG LẦN ĐẦU
</div>
<h1 className="text-4xl font-extrabold text-gray-900 leading-tight">
Thiết lập Quản trị viên
</h1>
<p className="text-lg text-gray-500 leading-relaxed">
Chào mừng! Hệ thống vừa được cài đặt. Vui lòng tạo tài khoản đầu tiên để quản lý và vận hành trang web.
</p>
</>
) : (
<>
<h1 className="text-4xl font-extrabold text-gray-900 leading-tight"> <h1 className="text-4xl font-extrabold text-gray-900 leading-tight">
Lên kế hoạch cho hành trình tiếp theo Lên kế hoạch cho hành trình tiếp theo
</h1> </h1>
<p className="text-lg text-gray-500 leading-relaxed"> <p className="text-lg text-gray-500 leading-relaxed">
Khám phá các địa điểm nổi bật qua bản đồ cộng đồng hoặc đăng nhập để bắt đầu tự tạo chuyến đi cho riêng mình. Khám phá các địa điểm nổi bật qua bản đồ cộng đồng hoặc đăng nhập để bắt đầu tự tạo chuyến đi cho riêng mình.
</p> </p>
</>
)}
</div> </div>
<div className="grid grid-cols-1 gap-4"> <div className="grid grid-cols-1 gap-4">
{/* Nút Khám phá Bản đồ (Cho khách công cộng) */} {isInitialSetup && (
<button
onClick={onGoToSignup}
className="flex items-center justify-center gap-3 bg-blue-600 hover:bg-blue-700 text-white font-bold py-5 px-8 rounded-2xl transition-all shadow-xl shadow-blue-100 animate-bounce-subtle mb-2"
>
<UserPlus className="w-5 h-5" />
Bắt đầu thiết lập ngay
</button>
)}
<button <button
onClick={onGoToMap} onClick={onGoToMap}
className="flex items-center justify-center gap-3 bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-8 rounded-2xl transition-all shadow-lg shadow-indigo-100" className="flex items-center justify-center gap-3 bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-8 rounded-2xl transition-all shadow-lg shadow-indigo-100"
+3 -3
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { ItineraryTimeline } from './ItineraryTimeline'; import { ItineraryTimeline } from './ItineraryTimeline.js';
import { ExpenseManager } from './ExpenseManager'; import { ExpenseManager } from './ExpenseManager.js';
import { useTourStore } from './useTourStore'; import { useTourStore } from './useTourStore.js';
import { import {
Map as MapIcon, Map as MapIcon,
Wallet, Wallet,
+1 -1
View File
@@ -1,5 +1,5 @@
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service.js';
@Injectable() @Injectable()
export class AdminGuard implements CanActivate { export class AdminGuard implements CanActivate {
Vendored
+16 -12
View File
@@ -1,15 +1,19 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { useState, useEffect } from 'react';
const jsx_runtime_1 = require("react/jsx-runtime"); import { LandingPage } from './LandingPage.js';
const react_1 = require("react"); import { TourDetailPage } from './TourDetailPage.js';
const LandingPage_1 = require("./LandingPage"); import { ExploreMap } from './ExploreMap.js';
const TourDetailPage_1 = require("./TourDetailPage"); import { SignupPage } from './SignupPage.js';
const App = () => { const App = () => {
const [isStarted, setIsStarted] = (0, react_1.useState)(false); const [view, setView] = useState('landing');
const handleContinue = () => { const [isInitialSetup, setIsInitialSetup] = useState(false);
setIsStarted(true); useEffect(() => {
fetch('http://localhost:3001/api/v1/auth/status')
.then(res => res.json())
.then(data => setIsInitialSetup(data.isInitialSetup))
.catch(() => setIsInitialSetup(false));
}, [view]);
return (_jsxs("div", { className: "app-container", children: [view === 'landing' && (_jsx(LandingPage, { isInitialSetup: isInitialSetup, onContinue: () => setView('explore'), onGoToSignup: () => setView('signup'), onGoToMap: () => setView('explore') })), view === 'signup' && (_jsx(SignupPage, { onBack: () => setView('landing'), onSuccess: () => setView('landing') })), view === 'explore' && (_jsx(ExploreMap, { onBack: () => setView('landing') })), view === 'detail' && (_jsx(TourDetailPage, {}))] }));
}; };
return ((0, jsx_runtime_1.jsx)("div", { className: "app-container", children: !isStarted ? ((0, jsx_runtime_1.jsx)(LandingPage_1.LandingPage, { onContinue: handleContinue })) : ((0, jsx_runtime_1.jsx)(TourDetailPage_1.TourDetailPage, {})) })); export default App;
};
exports.default = App;
//# sourceMappingURL=App.js.map //# sourceMappingURL=App.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"App.js","sourceRoot":"","sources":["../App.tsx"],"names":[],"mappings":";;;AAAA,iCAAwC;AACxC,+CAA4C;AAC5C,qDAAkD;AAElD,MAAM,GAAG,GAAG,GAAG,EAAE;IAGf,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAElD,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO,CACL,gCAAK,SAAS,EAAC,eAAe,YAC3B,CAAC,SAAS,CAAC,CAAC,CAAC,CACZ,uBAAC,yBAAW,IAAC,UAAU,EAAE,cAAc,GAAI,CAC5C,CAAC,CAAC,CAAC,CACF,uBAAC,+BAAc,KAAG,CACnB,GACG,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAe,GAAG,CAAC"} {"version":3,"file":"App.js","sourceRoot":"","sources":["../App.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,GAAG,GAAG,GAAG,EAAE;IAEf,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAO,SAAS,CAAC,CAAC;IAClD,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE5D,SAAS,CAAC,GAAG,EAAE;QAEb,KAAK,CAAC,0CAA0C,CAAC;aAC9C,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;aACvB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;aACpD,KAAK,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO,CACL,eAAK,SAAS,EAAC,eAAe,aAC3B,IAAI,KAAK,SAAS,IAAI,CACrB,KAAC,WAAW,IACV,cAAc,EAAE,cAAc,EAC9B,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EACpC,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EACrC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,GACnC,CACH,EAEA,IAAI,KAAK,QAAQ,IAAI,CACpB,KAAC,UAAU,IACT,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAChC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,GACnC,CACH,EAEA,IAAI,KAAK,SAAS,IAAI,CACrB,KAAC,UAAU,IAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,GAAI,CACjD,EAEA,IAAI,KAAK,QAAQ,IAAI,CACpB,KAAC,cAAc,KAAG,CACnB,IACG,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,GAAG,CAAC"}
+11 -15
View File
@@ -1,16 +1,13 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { useState, useMemo } from 'react';
exports.ExpenseManager = void 0; import { Wallet, Users, Info } from 'lucide-react';
const jsx_runtime_1 = require("react/jsx-runtime"); import { useTourStore } from './useTourStore.js';
const react_1 = require("react"); export const ExpenseManager = () => {
const lucide_react_1 = require("lucide-react"); const { legs } = useTourStore();
const useTourStore_1 = require("./useTourStore"); const [adults, setAdults] = useState(2);
const ExpenseManager = () => { const [children, setChildren] = useState(1);
const { legs } = (0, useTourStore_1.useTourStore)(); const [discount, setDiscount] = useState(30);
const [adults, setAdults] = (0, react_1.useState)(2); const totals = useMemo(() => {
const [children, setChildren] = (0, react_1.useState)(1);
const [discount, setDiscount] = (0, react_1.useState)(30);
const totals = (0, react_1.useMemo)(() => {
const totalAmount = legs.reduce((acc, leg) => acc + leg.expenses.reduce((lAcc, exp) => lAcc + Number(exp.amount), 0), 0); const totalAmount = legs.reduce((acc, leg) => acc + leg.expenses.reduce((lAcc, exp) => lAcc + Number(exp.amount), 0), 0);
const childRateFactor = 1 - (discount / 100); const childRateFactor = 1 - (discount / 100);
const weightedCount = adults + (children * childRateFactor); const weightedCount = adults + (children * childRateFactor);
@@ -22,7 +19,6 @@ const ExpenseManager = () => {
childPrice: Math.round(childPrice) childPrice: Math.round(childPrice)
}; };
}, [legs, adults, children, discount]); }, [legs, adults, children, discount]);
return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-6 animate-in fade-in slide-in-from-bottom-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-white p-6 rounded-2xl border border-gray-100 shadow-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-6 text-blue-600", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Users, { className: "w-5 h-5" }), (0, jsx_runtime_1.jsx)("h3", { className: "font-bold", children: "C\u1EA5u h\u00ECnh th\u00E0nh vi\u00EAn" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-gray-400 block mb-1", children: "Ng\u01B0\u1EDDi l\u1EDBn" }), (0, jsx_runtime_1.jsx)("input", { type: "number", value: adults, onChange: e => setAdults(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-gray-400 block mb-1", children: "Tr\u1EBB em" }), (0, jsx_runtime_1.jsx)("input", { type: "number", value: children, onChange: e => setChildren(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-gray-400 block mb-1", children: "Gi\u1EA3m tr\u1EBB em (%)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", value: discount, onChange: e => setDiscount(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-blue-600 rounded-2xl p-6 text-white shadow-lg shadow-blue-200", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start mb-8", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-blue-100 text-sm", children: "T\u1ED5ng chi ph\u00ED chuy\u1EBFn \u0111i" }), (0, jsx_runtime_1.jsxs)("h2", { className: "text-3xl font-bold mt-1", children: [totals.total.toLocaleString(), " VND"] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "w-8 h-8 opacity-20" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-4 border-t border-blue-500 pt-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-blue-100 text-xs uppercase tracking-wider font-semibold", children: "M\u1ED7i ng\u01B0\u1EDDi l\u1EDBn" }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xl font-bold", children: [totals.adultPrice.toLocaleString(), "\u0111"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("p", { className: "text-blue-100 text-xs uppercase tracking-wider font-semibold", children: ["M\u1ED7i tr\u1EBB em (-", discount, "%)"] }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xl font-bold", children: [totals.childPrice.toLocaleString(), "\u0111"] })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-gray-400 text-xs px-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("p", { children: "Chi ph\u00ED \u0111\u01B0\u1EE3c t\u1EF1 \u0111\u1ED9ng t\u00EDnh to\u00E1n d\u1EF1a tr\u00EAn h\u00F3a \u0111\u01A1n c\u1EE7a c\u00E1c ch\u1EB7ng." })] })] })); return (_jsxs("div", { className: "space-y-6 animate-in fade-in slide-in-from-bottom-4", children: [_jsxs("div", { className: "bg-white p-6 rounded-2xl border border-gray-100 shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 mb-6 text-blue-600", children: [_jsx(Users, { className: "w-5 h-5" }), _jsx("h3", { className: "font-bold", children: "C\u1EA5u h\u00ECnh th\u00E0nh vi\u00EAn" })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "text-xs text-gray-400 block mb-1", children: "Ng\u01B0\u1EDDi l\u1EDBn" }), _jsx("input", { type: "number", value: adults, onChange: e => setAdults(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "text-xs text-gray-400 block mb-1", children: "Tr\u1EBB em" }), _jsx("input", { type: "number", value: children, onChange: e => setChildren(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "text-xs text-gray-400 block mb-1", children: "Gi\u1EA3m tr\u1EBB em (%)" }), _jsx("input", { type: "number", value: discount, onChange: e => setDiscount(Number(e.target.value)), className: "w-full p-2 bg-gray-50 rounded-lg border-none focus:ring-2 focus:ring-blue-500" })] })] })] }), _jsxs("div", { className: "bg-blue-600 rounded-2xl p-6 text-white shadow-lg shadow-blue-200", children: [_jsxs("div", { className: "flex justify-between items-start mb-8", children: [_jsxs("div", { children: [_jsx("p", { className: "text-blue-100 text-sm", children: "T\u1ED5ng chi ph\u00ED chuy\u1EBFn \u0111i" }), _jsxs("h2", { className: "text-3xl font-bold mt-1", children: [totals.total.toLocaleString(), " VND"] })] }), _jsx(Wallet, { className: "w-8 h-8 opacity-20" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4 border-t border-blue-500 pt-6", children: [_jsxs("div", { children: [_jsx("p", { className: "text-blue-100 text-xs uppercase tracking-wider font-semibold", children: "M\u1ED7i ng\u01B0\u1EDDi l\u1EDBn" }), _jsxs("p", { className: "text-xl font-bold", children: [totals.adultPrice.toLocaleString(), "\u0111"] })] }), _jsxs("div", { children: [_jsxs("p", { className: "text-blue-100 text-xs uppercase tracking-wider font-semibold", children: ["M\u1ED7i tr\u1EBB em (-", discount, "%)"] }), _jsxs("p", { className: "text-xl font-bold", children: [totals.childPrice.toLocaleString(), "\u0111"] })] })] })] }), _jsxs("div", { className: "flex items-center gap-2 text-gray-400 text-xs px-2", children: [_jsx(Info, { className: "w-4 h-4" }), _jsx("p", { children: "Chi ph\u00ED \u0111\u01B0\u1EE3c t\u1EF1 \u0111\u1ED9ng t\u00EDnh to\u00E1n d\u1EF1a tr\u00EAn h\u00F3a \u0111\u01A1n c\u1EE7a c\u00E1c ch\u1EB7ng." })] })] }));
}; };
exports.ExpenseManager = ExpenseManager;
//# sourceMappingURL=ExpenseManager.js.map //# sourceMappingURL=ExpenseManager.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"ExpenseManager.js","sourceRoot":"","sources":["../ExpenseManager.tsx"],"names":[],"mappings":"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"} {"version":3,"file":"ExpenseManager.js","sourceRoot":"","sources":["../ExpenseManager.tsx"],"names":[],"mappings":"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"}
+5
View File
@@ -0,0 +1,5 @@
import React from 'react';
import 'leaflet/dist/leaflet.css';
export declare const ExploreMap: ({ onBack }: {
onBack: () => void;
}) => React.JSX.Element;
+50
View File
@@ -0,0 +1,50 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useState } from 'react';
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { useTourStore } from './useTourStore.js';
import { X, Navigation, Image as ImageIcon } from 'lucide-react';
const DefaultIcon = L.icon({
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
});
L.Marker.prototype.options.icon = DefaultIcon;
function RecenterMap({ position }) {
const map = useMap();
useEffect(() => {
map.setView(position, map.getZoom());
}, [position, map]);
return null;
}
export const ExploreMap = ({ onBack }) => {
const { publicTours, fetchPublicTours } = useTourStore();
const [userPos, setUserPos] = useState([10.7769, 106.7009]);
useEffect(() => {
fetchPublicTours();
navigator.geolocation.getCurrentPosition((pos) => setUserPos([pos.coords.latitude, pos.coords.longitude]), () => console.log("Không thể lấy vị trí người dùng"));
}, []);
return (_jsxs("div", { className: "h-screen w-full relative", children: [_jsx("button", { onClick: onBack, className: "absolute top-6 left-6 z-[1000] bg-white p-3 rounded-full shadow-xl hover:bg-gray-50 transition-all", children: _jsx(X, { className: "w-6 h-6 text-gray-800" }) }), _jsx("div", { className: "absolute top-6 left-20 z-[1000] bg-white/90 backdrop-blur-md px-6 py-3 rounded-2xl shadow-xl border border-white/20 hidden sm:block", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Navigation, { className: "w-4 h-4 text-blue-600" }), _jsx("span", { className: "font-bold text-gray-800", children: "\u0110ang kh\u00E1m ph\u00E1 khu v\u1EF1c c\u1EE7a b\u1EA1n" })] }) }), _jsxs(MapContainer, { center: userPos, zoom: 13, className: "h-full w-full", children: [_jsx(TileLayer, { url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution: '\u00A9 OpenStreetMap contributors' }), _jsx(RecenterMap, { position: userPos }), publicTours.map((tour) => {
const place = tour.legs?.[0]?.places?.[0];
if (!place)
return null;
const tourImage = tour.photos?.[0]?.imageUrl || `https://picsum.photos/seed/${tour.id}/200/200`;
return (_jsx(Marker, { position: [place.latitude, place.longitude], icon: L.divIcon({
className: 'custom-bubble',
html: `
<div class="relative group">
<div class="w-12 h-12 rounded-full border-4 border-white shadow-lg overflow-hidden transition-transform group-hover:scale-110">
<img src="${tourImage}" class="w-full h-full object-cover" />
</div>
<div class="absolute -bottom-1 -right-1 bg-blue-600 rounded-full p-1 border-2 border-white">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
</div>
</div>
`,
iconSize: [48, 48],
}), children: _jsx(Popup, { className: "custom-popup", children: _jsxs("div", { className: "p-1 max-w-[200px]", children: [_jsx("img", { src: tourImage, className: "w-full h-32 object-cover rounded-lg mb-2" }), _jsx("h4", { className: "font-bold text-gray-900 leading-tight mb-1", children: tour.title }), _jsxs("button", { className: "text-xs font-bold text-blue-600 flex items-center gap-1", children: ["Xem chi ti\u1EBFt h\u00ECnh \u1EA3nh ", _jsx(ImageIcon, { className: "w-3 h-3" })] })] }) }) }, tour.id));
})] })] }));
};
//# sourceMappingURL=ExploreMap.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"ExploreMap.js","sourceRoot":"","sources":["../ExploreMap.tsx"],"names":[],"mappings":"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"}
+9 -13
View File
@@ -1,23 +1,19 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { format, differenceInMinutes, parseISO } from 'date-fns';
exports.ItineraryTimeline = void 0; import { CheckCircle2, Circle, Clock, MapPin, AlertCircle } from 'lucide-react';
const jsx_runtime_1 = require("react/jsx-runtime"); import { useTourStore } from './useTourStore.js';
const date_fns_1 = require("date-fns");
const lucide_react_1 = require("lucide-react");
const useTourStore_1 = require("./useTourStore");
const TimeVariance = ({ planned, actual }) => { const TimeVariance = ({ planned, actual }) => {
if (!actual) if (!actual)
return null; return null;
const diff = (0, date_fns_1.differenceInMinutes)((0, date_fns_1.parseISO)(actual), (0, date_fns_1.parseISO)(planned)); const diff = differenceInMinutes(parseISO(actual), parseISO(planned));
const isLate = diff > 0; const isLate = diff > 0;
return ((0, jsx_runtime_1.jsxs)("div", { className: `flex items-center text-xs font-medium mt-1 ${isLate ? 'text-red-500' : 'text-green-600'}`, children: [isLate ? (0, jsx_runtime_1.jsx)(lucide_react_1.AlertCircle, { className: "w-3 h-3 mr-1" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "w-3 h-3 mr-1" }), (0, jsx_runtime_1.jsx)("span", { children: isLate ? `Trễ ${diff} phút` : diff === 0 ? 'Đúng giờ' : `Sớm ${Math.abs(diff)} phút` })] })); return (_jsxs("div", { className: `flex items-center text-xs font-medium mt-1 ${isLate ? 'text-red-500' : 'text-green-600'}`, children: [isLate ? _jsx(AlertCircle, { className: "w-3 h-3 mr-1" }) : _jsx(CheckCircle2, { className: "w-3 h-3 mr-1" }), _jsx("span", { children: isLate ? `Trễ ${diff} phút` : diff === 0 ? 'Đúng giờ' : `Sớm ${Math.abs(diff)} phút` })] }));
}; };
const ItineraryTimeline = () => { export const ItineraryTimeline = () => {
const { legs } = (0, useTourStore_1.useTourStore)(); const { legs } = useTourStore();
const toggleComplete = async (placeId) => { const toggleComplete = async (placeId) => {
console.log("Toggle status for place:", placeId); console.log("Toggle status for place:", placeId);
}; };
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl mx-auto p-4 sm:p-6 bg-gray-50 min-h-screen", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold text-gray-800 mb-8 px-2", children: "L\u1ED9 tr\u00ECnh chuy\u1EBFn \u0111i" }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-8", children: legs.map((leg, legIdx) => ((0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center mb-4 px-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-blue-600 text-white text-sm font-bold px-3 py-1 rounded-full shadow-sm", children: ["Ch\u1EB7ng ", leg.sequenceNumber] }), (0, jsx_runtime_1.jsx)("div", { className: "ml-4 h-[1px] flex-1 bg-gray-200" })] }), (0, jsx_runtime_1.jsx)("div", { className: "absolute left-6 top-10 bottom-0 w-0.5 bg-gray-200 -z-0" }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-6 ml-2", children: leg.places.map((place) => ((0, jsx_runtime_1.jsxs)("div", { className: "relative flex group", children: [(0, jsx_runtime_1.jsx)("div", { className: "z-10 mt-1.5 mr-4", children: (0, jsx_runtime_1.jsx)("button", { onClick: () => toggleComplete(place.id), className: `transition-colors duration-200 ${place.isCompleted ? 'text-green-500' : 'text-gray-300 hover:text-blue-500'}`, children: place.isCompleted ? ((0, jsx_runtime_1.jsx)(lucide_react_1.CheckCircle2, { className: "w-8 h-8 bg-white rounded-full" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Circle, { className: "w-8 h-8 bg-white rounded-full fill-white" })) }) }), (0, jsx_runtime_1.jsxs)("div", { className: `flex-1 bg-white p-4 rounded-xl border transition-all duration-200 ${place.isCompleted ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: `font-semibold text-lg ${place.isCompleted ? 'text-gray-500 line-through' : 'text-gray-800'}`, children: place.name }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.MapPin, { className: "w-3 h-3 mr-1" }), (0, jsx_runtime_1.jsx)("span", { className: "truncate max-w-[200px] sm:max-w-md", children: place.address })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right flex flex-col items-end", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center text-sm font-medium text-blue-600", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "w-3 h-3 mr-1" }), (0, date_fns_1.format)((0, date_fns_1.parseISO)(place.arrivalTime), 'HH:mm')] }), place.isCompleted && place.completedAt && ((0, jsx_runtime_1.jsxs)("div", { className: "text-[10px] text-gray-400 mt-1 italic", children: ["Th\u1EF1c t\u1EBF: ", (0, date_fns_1.format)((0, date_fns_1.parseISO)(place.completedAt), 'HH:mm')] }))] })] }), (0, jsx_runtime_1.jsx)(TimeVariance, { planned: place.arrivalTime, actual: place.completedAt })] })] }, place.id))) }), leg.notes && ((0, jsx_runtime_1.jsxs)("p", { className: "ml-14 mt-4 text-sm text-gray-400 italic", children: ["* ", leg.notes] }))] }, leg.id))) })] })); return (_jsxs("div", { className: "max-w-2xl mx-auto p-4 sm:p-6 bg-gray-50 min-h-screen", children: [_jsx("h2", { className: "text-2xl font-bold text-gray-800 mb-8 px-2", children: "L\u1ED9 tr\u00ECnh chuy\u1EBFn \u0111i" }), _jsx("div", { className: "space-y-8", children: legs.map((leg, legIdx) => (_jsxs("div", { className: "relative", children: [_jsxs("div", { className: "flex items-center mb-4 px-2", children: [_jsxs("div", { className: "bg-blue-600 text-white text-sm font-bold px-3 py-1 rounded-full shadow-sm", children: ["Ch\u1EB7ng ", leg.sequenceNumber] }), _jsx("div", { className: "ml-4 h-[1px] flex-1 bg-gray-200" })] }), _jsx("div", { className: "absolute left-6 top-10 bottom-0 w-0.5 bg-gray-200 -z-0" }), _jsx("div", { className: "space-y-6 ml-2", children: leg.places.map((place) => (_jsxs("div", { className: "relative flex group", children: [_jsx("div", { className: "z-10 mt-1.5 mr-4", children: _jsx("button", { onClick: () => toggleComplete(place.id), className: `transition-colors duration-200 ${place.isCompleted ? 'text-green-500' : 'text-gray-300 hover:text-blue-500'}`, children: place.isCompleted ? (_jsx(CheckCircle2, { className: "w-8 h-8 bg-white rounded-full" })) : (_jsx(Circle, { className: "w-8 h-8 bg-white rounded-full fill-white" })) }) }), _jsxs("div", { className: `flex-1 bg-white p-4 rounded-xl border transition-all duration-200 ${place.isCompleted ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'}`, children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("div", { children: [_jsx("h3", { className: `font-semibold text-lg ${place.isCompleted ? 'text-gray-500 line-through' : 'text-gray-800'}`, children: place.name }), _jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [_jsx(MapPin, { className: "w-3 h-3 mr-1" }), _jsx("span", { className: "truncate max-w-[200px] sm:max-w-md", children: place.address })] })] }), _jsxs("div", { className: "text-right flex flex-col items-end", children: [_jsxs("div", { className: "flex items-center text-sm font-medium text-blue-600", children: [_jsx(Clock, { className: "w-3 h-3 mr-1" }), format(parseISO(place.arrivalTime), 'HH:mm')] }), place.isCompleted && place.completedAt && (_jsxs("div", { className: "text-[10px] text-gray-400 mt-1 italic", children: ["Th\u1EF1c t\u1EBF: ", format(parseISO(place.completedAt), 'HH:mm')] }))] })] }), _jsx(TimeVariance, { planned: place.arrivalTime, actual: place.completedAt })] })] }, place.id))) }), leg.notes && (_jsxs("p", { className: "ml-14 mt-4 text-sm text-gray-400 italic", children: ["* ", leg.notes] }))] }, leg.id))) })] }));
}; };
exports.ItineraryTimeline = ItineraryTimeline;
//# sourceMappingURL=ItineraryTimeline.js.map //# sourceMappingURL=ItineraryTimeline.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"ItineraryTimeline.js","sourceRoot":"","sources":["../ItineraryTimeline.tsx"],"names":[],"mappings":"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"} {"version":3,"file":"ItineraryTimeline.js","sourceRoot":"","sources":["../ItineraryTimeline.tsx"],"names":[],"mappings":"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"}
+3
View File
@@ -1,6 +1,9 @@
import React from 'react'; import React from 'react';
interface LandingPageProps { interface LandingPageProps {
onContinue?: () => void; onContinue?: () => void;
onGoToSignup?: () => void;
onGoToMap?: () => void;
isInitialSetup?: boolean;
} }
export declare const LandingPage: React.FC<LandingPageProps>; export declare const LandingPage: React.FC<LandingPageProps>;
export {}; export {};
+18 -11
View File
@@ -1,13 +1,20 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { useState, useMemo } from 'react';
exports.LandingPage = void 0; import { LogIn, Compass, ArrowRight, Map as MapIcon, UserPlus, ShieldCheck } from 'lucide-react';
const jsx_runtime_1 = require("react/jsx-runtime"); import { LoginModal } from './LoginModal.js';
const react_1 = require("react"); const TRAVEL_IMAGES = [
const lucide_react_1 = require("lucide-react"); "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?auto=format&fit=crop&q=80",
const LoginModal_1 = require("./LoginModal"); "https://images.unsplash.com/photo-1503220317375-aaad61436b1b?auto=format&fit=crop&q=80",
const LandingPage = ({ onContinue }) => { "https://images.unsplash.com/photo-1513581166391-887a96df91e7?auto=format&fit=crop&q=80",
const [isLoginModalOpen, setIsLoginModalOpen] = (0, react_1.useState)(false); "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80",
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex h-screen w-full flex-col md:flex-row overflow-hidden font-sans relative", children: [(0, jsx_runtime_1.jsxs)("div", { className: "hidden md:block md:w-1/2 relative bg-gray-900", children: [(0, jsx_runtime_1.jsx)("img", { src: "https://images.unsplash.com/photo-1503220317375-aaad61436b1b?q=80&w=2070&auto=format&fit=crop", alt: "Travel Planner Background", className: "absolute inset-0 h-full w-full object-cover opacity-70" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 bg-gradient-to-t from-blue-900/60 to-transparent" }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute bottom-16 left-16 text-white z-10", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 mb-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-white/20 backdrop-blur-md rounded-2xl", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Compass, { className: "w-8 h-8 text-white" }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-3xl font-black tracking-tighter uppercase", children: "Travel Planner" })] }), (0, jsx_runtime_1.jsx)("h2", { className: "text-5xl font-bold leading-tight max-w-lg", children: "Kh\u00E1m ph\u00E1 th\u1EBF gi\u1EDBi, l\u01B0u gi\u1EEF k\u1EC9 ni\u1EC7m." }), (0, jsx_runtime_1.jsx)("p", { className: "mt-4 text-blue-100 text-lg opacity-80 max-w-sm", children: "H\u1EE3p t\u00E1c c\u00F9ng b\u1EA1n b\u00E8 \u0111\u1EC3 l\u00EAn l\u1ECBch tr\u00ECnh ho\u00E0n h\u1EA3o nh\u1EA5t cho m\u1ECDi chuy\u1EBFn \u0111i." })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 flex flex-col justify-center items-center bg-white p-8 sm:p-12 md:p-20", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-md w-full space-y-12", children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-4xl md:text-5xl font-extrabold text-gray-900 leading-tight", children: "Ch\u00E0o m\u1EEBng b\u1EA1n \u0111\u1EBFn v\u1EDBi trang t\u1EA1o k\u1EBF ho\u1EA1ch du l\u1ECBch" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xl text-gray-600 leading-relaxed", children: "\u0110\u0103ng nh\u1EADp \u0111\u1EC3 v\u00E0o t\u1EA1o k\u1EBF ho\u1EA1ch c\u00E1 nh\u00E2n ho\u1EB7c nh\u1EA5n n\u00FAt ti\u1EBFp t\u1EE5c \u0111\u1EC3 tham quan c\u00E1c h\u00ECnh \u1EA3nh du l\u1ECBch \u0111\u01B0\u1EE3c chia s\u1EBB." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setIsLoginModalOpen(true), className: "group flex items-center justify-center gap-3 bg-blue-600 hover:bg-blue-700 text-white font-bold py-5 px-8 rounded-2xl shadow-xl shadow-blue-200 transition-all active:scale-95", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.LogIn, { className: "w-5 h-5 group-hover:translate-x-1 transition-transform" }), "\u0110\u0103ng nh\u1EADp ngay"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: onContinue, className: "flex items-center justify-center gap-3 bg-gray-50 hover:bg-gray-100 text-gray-700 font-bold py-5 px-8 rounded-2xl border border-gray-200 transition-all active:scale-95", children: ["Ti\u1EBFp t\u1EE5c tham quan \u1EA3nh", (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "w-5 h-5" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "pt-12 flex items-center gap-4 text-sm text-gray-400 border-t border-gray-100", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex -space-x-3", children: [1, 2, 3, 4].map(i => ((0, jsx_runtime_1.jsx)("img", { className: "w-10 h-10 rounded-full border-4 border-white", src: `https://i.pravatar.cc/100?img=${i + 20}`, alt: "user" }, i))) }), (0, jsx_runtime_1.jsx)("p", { className: "font-medium", children: "Tham gia c\u00F9ng +2,400 ng\u01B0\u1EDDi du l\u1ECBch kh\u00E1c" })] })] }) }), (0, jsx_runtime_1.jsx)(LoginModal_1.LoginModal, { isOpen: isLoginModalOpen, onClose: () => setIsLoginModalOpen(false) })] })); "https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?auto=format&fit=crop&q=80",
"https://images.unsplash.com/photo-1530789253388-582c481c54b0?auto=format&fit=crop&q=80"
];
export const LandingPage = ({ onContinue, onGoToSignup, onGoToMap, isInitialSetup }) => {
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
const backgroundImage = useMemo(() => {
return TRAVEL_IMAGES[Math.floor(Math.random() * TRAVEL_IMAGES.length)];
}, []);
return (_jsxs("div", { className: "flex h-screen w-full flex-col md:flex-row overflow-hidden font-sans bg-gray-50 relative", children: [_jsxs("div", { className: "absolute top-8 left-8 z-20 flex items-center gap-3 text-white md:text-white drop-shadow-lg", children: [_jsx(Compass, { className: "w-10 h-10" }), _jsx("span", { className: "text-2xl font-black tracking-tighter uppercase", children: "Travel Planner" })] }), _jsxs("div", { className: "hidden md:block md:w-1/2 relative h-full bg-blue-900", children: [_jsx("img", { src: backgroundImage, className: "w-full h-full object-cover opacity-70", alt: "Travel Background" }), _jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-black/40 to-transparent pointer-events-none" }), _jsxs("div", { className: "absolute bottom-12 left-8 z-20 flex flex-col gap-2 items-start drop-shadow-2xl", children: [_jsx("div", { className: "flex gap-2", children: _jsx("img", { src: TRAVEL_IMAGES[0], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 1" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx("img", { src: TRAVEL_IMAGES[1], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 2" }), _jsx("img", { src: TRAVEL_IMAGES[2], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 3" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("img", { src: TRAVEL_IMAGES[3], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 4" }), _jsx("img", { src: TRAVEL_IMAGES[4], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 5" }), _jsx("img", { src: TRAVEL_IMAGES[5], className: "w-14 h-14 object-cover rounded-xl border-2 border-white/50 transition-transform duration-300 hover:scale-110 cursor-pointer", alt: "thumb 6" })] })] })] }), _jsx("div", { className: "flex-1 md:w-1/2 flex flex-col justify-center items-center bg-white p-8 lg:p-16 h-full overflow-y-auto", children: _jsxs("div", { className: "max-w-md w-full space-y-12", children: [_jsx("div", { className: "space-y-4", children: isInitialSetup ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "inline-flex items-center px-3 py-1 rounded-full bg-amber-50 text-amber-700 text-xs font-bold border border-amber-100 mb-2", children: [_jsx(ShieldCheck, { className: "w-3 h-3 mr-1" }), " C\u1EA4U H\u00CCNH H\u1EC6 TH\u1ED0NG L\u1EA6N \u0110\u1EA6U"] }), _jsx("h1", { className: "text-4xl font-extrabold text-gray-900 leading-tight", children: "Thi\u1EBFt l\u1EADp Qu\u1EA3n tr\u1ECB vi\u00EAn" }), _jsx("p", { className: "text-lg text-gray-500 leading-relaxed", children: "Ch\u00E0o m\u1EEBng! H\u1EC7 th\u1ED1ng v\u1EEBa \u0111\u01B0\u1EE3c c\u00E0i \u0111\u1EB7t. Vui l\u00F2ng t\u1EA1o t\u00E0i kho\u1EA3n \u0111\u1EA7u ti\u00EAn \u0111\u1EC3 qu\u1EA3n l\u00FD v\u00E0 v\u1EADn h\u00E0nh trang web." })] })) : (_jsxs(_Fragment, { children: [_jsx("h1", { className: "text-4xl font-extrabold text-gray-900 leading-tight", children: "L\u00EAn k\u1EBF ho\u1EA1ch cho h\u00E0nh tr\u00ECnh ti\u1EBFp theo" }), _jsx("p", { className: "text-lg text-gray-500 leading-relaxed", children: "Kh\u00E1m ph\u00E1 c\u00E1c \u0111\u1ECBa \u0111i\u1EC3m n\u1ED5i b\u1EADt qua b\u1EA3n \u0111\u1ED3 c\u1ED9ng \u0111\u1ED3ng ho\u1EB7c \u0111\u0103ng nh\u1EADp \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u t\u1EF1 t\u1EA1o chuy\u1EBFn \u0111i cho ri\u00EAng m\u00ECnh." })] })) }), _jsxs("div", { className: "grid grid-cols-1 gap-4", children: [isInitialSetup && (_jsxs("button", { onClick: onGoToSignup, className: "flex items-center justify-center gap-3 bg-blue-600 hover:bg-blue-700 text-white font-bold py-5 px-8 rounded-2xl transition-all shadow-xl shadow-blue-100 animate-bounce-subtle mb-2", children: [_jsx(UserPlus, { className: "w-5 h-5" }), "B\u1EAFt \u0111\u1EA7u thi\u1EBFt l\u1EADp ngay"] })), _jsxs("button", { onClick: onGoToMap, className: "flex items-center justify-center gap-3 bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-8 rounded-2xl transition-all shadow-lg shadow-indigo-100", children: [_jsx(MapIcon, { className: "w-5 h-5" }), "Kh\u00E1m ph\u00E1 c\u00E1c h\u00E0nh tr\u00ECnh du l\u1ECBch"] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("button", { onClick: () => setIsLoginModalOpen(true), className: "flex items-center justify-center gap-2 bg-blue-50 hover:bg-blue-100 text-blue-700 font-bold py-4 rounded-2xl transition-all", children: [_jsx(LogIn, { className: "w-5 h-5" }), "\u0110\u0103ng nh\u1EADp"] }), _jsxs("button", { onClick: onGoToSignup, className: "flex items-center justify-center gap-2 bg-gray-50 hover:bLine truncated
}; };
exports.LandingPage = LandingPage;
//# sourceMappingURL=LandingPage.js.map //# sourceMappingURL=LandingPage.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"LandingPage.js","sourceRoot":"","sources":["../LandingPage.tsx"],"names":[],"mappings":";;;;AAAA,iCAAwC;AACxC,+CAAkE;AAClE,6CAA0C;AAMnC,MAAM,WAAW,GAA+B,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IACxE,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAEhE,OAAO,CACL,iCAAK,SAAS,EAAC,8EAA8E,aAE3F,iCAAK,SAAS,EAAC,+CAA+C,aAC5D,gCACE,GAAG,EAAC,+FAA+F,EACnG,GAAG,EAAC,2BAA2B,EAC/B,SAAS,EAAC,wDAAwD,GAClE,EAEF,gCAAK,SAAS,EAAC,mEAAmE,GAAG,EAErF,iCAAK,SAAS,EAAC,4CAA4C,aACzD,iCAAK,SAAS,EAAC,8BAA8B,aAC3C,gCAAK,SAAS,EAAC,8CAA8C,YAC3D,uBAAC,sBAAO,IAAC,SAAS,EAAC,oBAAoB,GAAG,GACtC,EACN,iCAAM,SAAS,EAAC,gDAAgD,+BAAsB,IAClF,EACN,+BAAI,SAAS,EAAC,2CAA2C,4FAEpD,EACL,8BAAG,SAAS,EAAC,gDAAgD,uKAEzD,IACA,IACF,EAGN,gCAAK,SAAS,EAAC,+EAA+E,YAC5F,iCAAK,SAAS,EAAC,4BAA4B,aACzC,iCAAK,SAAS,EAAC,WAAW,aACxB,+BAAI,SAAS,EAAC,iEAAiE,mHAE1E,EACL,8BAAG,SAAS,EAAC,uCAAuC,+PAEhD,IACA,EAEN,iCAAK,SAAS,EAAC,qBAAqB,aAElC,oCACE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EACxC,SAAS,EAAC,gLAAgL,aAE1L,uBAAC,oBAAK,IAAC,SAAS,EAAC,wDAAwD,GAAG,qCAErE,EAGT,oCACE,OAAO,EAAE,UAAU,EACnB,SAAS,EAAC,yKAAyK,sDAGnL,uBAAC,yBAAU,IAAC,SAAS,EAAC,SAAS,GAAG,IAC3B,IACL,EAEN,iCAAK,SAAS,EAAC,8EAA8E,aAC3F,gCAAK,SAAS,EAAC,iBAAiB,YAC7B,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACrB,gCAAa,SAAS,EAAC,8CAA8C,EAAC,GAAG,EAAE,iCAAiC,CAAC,GAAC,EAAE,EAAE,EAAE,GAAG,EAAC,MAAM,IAApH,CAAC,CAAsH,CAClI,CAAC,GACE,EACN,8BAAG,SAAS,EAAC,aAAa,iFAA4C,IAClE,IACF,GACF,EAGN,uBAAC,uBAAU,IAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAI,IAC/E,CACP,CAAC;AACJ,CAAC,CAAC;AA9EW,QAAA,WAAW,eA8EtB"} {"version":3,"file":"LandingPage.js","sourceRoot":"","sources":["../LandingPage.tsx"],"names":[],"mappings":"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"}
+1
View File
@@ -2,6 +2,7 @@ import React from 'react';
interface LoginModalProps { interface LoginModalProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
onSwitchToSignup?: () => void;
} }
export declare const LoginModal: React.FC<LoginModalProps>; export declare const LoginModal: React.FC<LoginModalProps>;
export {}; export {};
+4 -8
View File
@@ -1,12 +1,8 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { X, Mail, Lock, ArrowRight } from 'lucide-react';
exports.LoginModal = void 0; export const LoginModal = ({ isOpen, onClose, onSwitchToSignup }) => {
const jsx_runtime_1 = require("react/jsx-runtime");
const lucide_react_1 = require("lucide-react");
const LoginModal = ({ isOpen, onClose }) => {
if (!isOpen) if (!isOpen)
return null; return null;
return ((0, jsx_runtime_1.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 bg-gray-900/60 backdrop-blur-sm animate-in fade-in duration-300", onClick: onClose }), (0, jsx_runtime_1.jsx)("div", { className: "relative w-full max-w-md bg-white rounded-3xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-300", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-8 sm:p-10", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start mb-8", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-3xl font-bold text-gray-900", children: "\u0110\u0103ng nh\u1EADp" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-500 mt-2", children: "Ch\u00E0o m\u1EEBng b\u1EA1n quay tr\u1EDF l\u1EA1i!" })] }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400 hover:text-gray-600", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "w-6 h-6" }) })] }), (0, jsx_runtime_1.jsxs)("form", { className: "space-y-6", onSubmit: (e) => e.preventDefault(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-semibold text-gray-700 ml-1", children: "Email" }), (0, jsx_runtime_1.jsxs)("div", { className: "relative group", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), (0, jsx_runtime_1.jsx)("input", { type: "email", placeholder: "name@example.com", className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center px-1", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-semibold text-gray-700", children: "M\u1EADt kh\u1EA9u" }), (0, jsx_runtime_1.jsx)("button", { className: "text-xs font-bold text-blue-600 hover:text-blue-700", children: "Qu\u00EAn m\u1EADt kh\u1EA9u?" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "relative group", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), (0, jsx_runtime_1.jsx)("input", { type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), (0, jsx_runtime_1.jsxs)("button", { type: "submit", className: "w-full flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 rounded-2xl shadow-lg shadow-blue-200 transition-all active:scale-[0.98]", children: ["\u0110\u0103ng nh\u1EADp", (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { className: "w-5 h-5" })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "mt-10 pt-8 border-t border-gray-100 text-center", children: (0, jsx_runtime_1.jsxs)("p", { className: "text-gray-500", children: ["Ch\u01B0a c\u00F3 t\u00E0i kho\u1EA3n?", ' ', (0, jsx_runtime_1.jsx)("button", { className: "font-bold text-blue-600 hover:underline", children: "T\u1EA1o t\u00E0i kho\u1EA3n ngay" })] }) })] }) })] })); return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6", children: [_jsx("div", { className: "absolute inset-0 bg-gray-900/60 backdrop-blur-sm animate-in fade-in duration-300", onClick: onClose }), _jsx("div", { className: "relative w-full max-w-md bg-white rounded-3xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-300", children: _jsxs("div", { className: "p-8 sm:p-10", children: [_jsxs("div", { className: "flex justify-between items-start mb-8", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-3xl font-bold text-gray-900", children: "\u0110\u0103ng nh\u1EADp" }), _jsx("p", { className: "text-gray-500 mt-2", children: "Ch\u00E0o m\u1EEBng b\u1EA1n quay tr\u1EDF l\u1EA1i!" })] }), _jsx("button", { onClick: onClose, className: "p-2 hover:bg-gray-100 rounded-full transition-colors text-gray-400 hover:text-gray-600", children: _jsx(X, { className: "w-6 h-6" }) })] }), _jsxs("form", { className: "space-y-6", onSubmit: (e) => e.preventDefault(), children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-semibold text-gray-700 ml-1", children: "Email" }), _jsxs("div", { className: "relative group", children: [_jsx(Mail, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), _jsx("input", { type: "email", placeholder: "name@example.com", className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex justify-between items-center px-1", children: [_jsx("label", { className: "text-sm font-semibold text-gray-700", children: "M\u1EADt kh\u1EA9u" }), _jsx("button", { className: "text-xs font-bold text-blue-600 hover:text-blue-700", children: "Qu\u00EAn m\u1EADt kh\u1EA9u?" })] }), _jsxs("div", { className: "relative group", children: [_jsx(Lock, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), _jsx("input", { type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), _jsxs("button", { type: "submit", className: "w-full flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 rounded-2xl shadow-lg shadow-blue-200 transition-all active:scale-[0.98]", children: ["\u0110\u0103ng nh\u1EADp", _jsx(ArrowRight, { className: "w-5 h-5" })] })] }), _jsx("div", { className: "mt-10 pt-8 border-t border-gray-100 text-center", children: _jsxs("p", { className: "text-gray-500", children: ["Ch\u01B0a c\u00F3 t\u00E0i kho\u1EA3n?", ' ', _jsx("button", { onClick: () => { onClose(); onSwitchToSignup?.(); }, className: "font-bold text-blue-600 hover:underline", children: "T\u1EA1o t\u00E0i kho\u1EA3n ngay" })] }) })] }) })] }));
}; };
exports.LoginModal = LoginModal;
//# sourceMappingURL=LoginModal.js.map //# sourceMappingURL=LoginModal.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"LoginModal.js","sourceRoot":"","sources":["../LoginModal.tsx"],"names":[],"mappings":";;;;AACA,+CAAyD;AAOlD,MAAM,UAAU,GAA8B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;IAC3E,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,CACL,iCAAK,SAAS,EAAC,gEAAgE,aAE7E,gCACE,SAAS,EAAC,kFAAkF,EAC5F,OAAO,EAAE,OAAO,GAChB,EAGF,gCAAK,SAAS,EAAC,6GAA6G,YAC1H,iCAAK,SAAS,EAAC,aAAa,aAC1B,iCAAK,SAAS,EAAC,uCAAuC,aACpD,4CACE,+BAAI,SAAS,EAAC,kCAAkC,yCAAe,EAC/D,8BAAG,SAAS,EAAC,oBAAoB,qEAAgC,IAC7D,EACN,mCACE,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,wFAAwF,YAElG,uBAAC,gBAAC,IAAC,SAAS,EAAC,SAAS,GAAG,GAClB,IACL,EAEN,kCAAM,SAAS,EAAC,WAAW,EAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,aAC7D,iCAAK,SAAS,EAAC,WAAW,aACxB,kCAAO,SAAS,EAAC,0CAA0C,sBAAc,EACzE,iCAAK,SAAS,EAAC,gBAAgB,aAC7B,uBAAC,mBAAI,IAAC,SAAS,EAAC,mHAAmH,GAAG,EACtI,kCACE,IAAI,EAAC,OAAO,EACZ,WAAW,EAAC,kBAAkB,EAC9B,SAAS,EAAC,kJAAkJ,GAC5J,IACE,IACF,EAEN,iCAAK,SAAS,EAAC,WAAW,aACxB,iCAAK,SAAS,EAAC,wCAAwC,aACrD,kCAAO,SAAS,EAAC,qCAAqC,mCAAiB,EACvE,mCAAQ,SAAS,EAAC,qDAAqD,8CAAwB,IAC3F,EACN,iCAAK,SAAS,EAAC,gBAAgB,aAC7B,uBAAC,mBAAI,IAAC,SAAS,EAAC,mHAAmH,GAAG,EACtI,kCACE,IAAI,EAAC,UAAU,EACf,WAAW,EAAC,kDAAU,EACtB,SAAS,EAAC,kJAAkJ,GAC5J,IACE,IACF,EAEN,oCACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,gLAAgL,yCAG1L,uBAAC,yBAAU,IAAC,SAAS,EAAC,SAAS,GAAG,IAC3B,IACJ,EAEP,gCAAK,SAAS,EAAC,iDAAiD,YAC9D,+BAAG,SAAS,EAAC,eAAe,uDACP,GAAG,EACtB,mCAAQ,SAAS,EAAC,yCAAyC,kDAA4B,IACrF,GACA,IACF,GACF,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AA1EW,QAAA,UAAU,cA0ErB"} {"version":3,"file":"LoginModal.js","sourceRoot":"","sources":["../LoginModal.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQzD,MAAM,CAAC,MAAM,UAAU,GAA8B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE;IAC7F,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,CACL,eAAK,SAAS,EAAC,gEAAgE,aAE7E,cACE,SAAS,EAAC,kFAAkF,EAC5F,OAAO,EAAE,OAAO,GAChB,EAGF,cAAK,SAAS,EAAC,6GAA6G,YAC1H,eAAK,SAAS,EAAC,aAAa,aAC1B,eAAK,SAAS,EAAC,uCAAuC,aACpD,0BACE,aAAI,SAAS,EAAC,kCAAkC,yCAAe,EAC/D,YAAG,SAAS,EAAC,oBAAoB,qEAAgC,IAC7D,EACN,iBACE,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,wFAAwF,YAElG,KAAC,CAAC,IAAC,SAAS,EAAC,SAAS,GAAG,GAClB,IACL,EAEN,gBAAM,SAAS,EAAC,WAAW,EAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,aAC7D,eAAK,SAAS,EAAC,WAAW,aACxB,gBAAO,SAAS,EAAC,0CAA0C,sBAAc,EACzE,eAAK,SAAS,EAAC,gBAAgB,aAC7B,KAAC,IAAI,IAAC,SAAS,EAAC,mHAAmH,GAAG,EACtI,gBACE,IAAI,EAAC,OAAO,EACZ,WAAW,EAAC,kBAAkB,EAC9B,SAAS,EAAC,kJAAkJ,GAC5J,IACE,IACF,EAEN,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,wCAAwC,aACrD,gBAAO,SAAS,EAAC,qCAAqC,mCAAiB,EACvE,iBAAQ,SAAS,EAAC,qDAAqD,8CAAwB,IAC3F,EACN,eAAK,SAAS,EAAC,gBAAgB,aAC7B,KAAC,IAAI,IAAC,SAAS,EAAC,mHAAmH,GAAG,EACtI,gBACE,IAAI,EAAC,UAAU,EACf,WAAW,EAAC,kDAAU,EACtB,SAAS,EAAC,kJAAkJ,GAC5J,IACE,IACF,EAEN,kBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,gLAAgL,yCAG1L,KAAC,UAAU,IAAC,SAAS,EAAC,SAAS,GAAG,IAC3B,IACJ,EAEP,cAAK,SAAS,EAAC,iDAAiD,YAC9D,aAAG,SAAS,EAAC,eAAe,uDACP,GAAG,EACtB,iBACE,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,EACnD,SAAS,EAAC,yCAAyC,kDAG5C,IACP,GACA,IACF,GACF,IACF,CACP,CAAC;AACJ,CAAC,CAAC"}
+7
View File
@@ -0,0 +1,7 @@
import React from 'react';
interface SignupPageProps {
onBack: () => void;
onSuccess: () => void;
}
export declare const SignupPage: React.FC<SignupPageProps>;
export {};
+47
View File
@@ -0,0 +1,47 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from 'react';
import { User, Mail, ArrowRight, ChevronLeft, Compass } from 'lucide-react';
export const SignupPage = ({ onBack, onSuccess }) => {
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
confirmPassword: ''
});
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
if (formData.password !== formData.confirmPassword) {
setError('Mật khẩu xác nhận không khớp');
return;
}
setIsLoading(true);
try {
const response = await fetch('http://localhost:3001/api/v1/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: formData.email,
password: formData.password,
name: formData.name
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || 'Đăng ký thất bại');
}
alert(data.isAdmin ? 'Đăng ký thành công! Bạn là Quản trị viên hệ thống.' : 'Đăng ký tài khoản thành công!');
onSuccess();
}
catch (err) {
setError(err.message);
}
finally {
setIsLoading(false);
}
};
return (_jsxs("div", { className: "flex h-screen w-full overflow-hidden font-sans bg-white", children: [_jsxs("div", { className: "hidden lg:block lg:w-1/2 relative bg-blue-900", children: [_jsx("img", { src: "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?q=80&w=2070&auto=format&fit=crop", alt: "Travel background", className: "absolute inset-0 h-full w-full object-cover opacity-50" }), _jsx("div", { className: "absolute inset-0 bg-gradient-to-br from-blue-600/40 to-transparent" }), _jsxs("div", { className: "absolute top-12 left-12 flex items-center gap-2 text-white z-10", children: [_jsx(Compass, { className: "w-8 h-8" }), _jsx("span", { className: "text-2xl font-black uppercase tracking-tighter", children: "Travel Planner" })] }), _jsxs("div", { className: "absolute bottom-20 left-12 text-white z-10 max-w-md", children: [_jsx("h2", { className: "text-4xl font-bold mb-4", children: "B\u1EAFt \u0111\u1EA7u h\u00E0nh tr\u00ECnh c\u1EE7a ri\u00EAng b\u1EA1n." }), _jsx("p", { className: "text-blue-100 opacity-80", children: "T\u1EA1o t\u00E0i kho\u1EA3n \u0111\u1EC3 l\u01B0u l\u1EA1i nh\u1EEFng k\u1EBF ho\u1EA1ch du l\u1ECBch tuy\u1EC7t v\u1EDDi nh\u1EA5t c\u00F9ng b\u1EA1n b\u00E8." })] })] }), _jsx("div", { className: "flex-1 flex flex-col justify-center px-8 sm:px-16 lg:px-24 py-12 overflow-y-auto", children: _jsxs("div", { className: "max-w-md w-full mx-auto", children: [_jsxs("button", { onClick: onBack, className: "flex items-center text-gray-400 hover:text-blue-600 mb-8 transition-colors font-medium", children: [_jsx(ChevronLeft, { className: "w-5 h-5" }), " Quay l\u1EA1i"] }), _jsxs("div", { className: "mb-10", children: [_jsx("h1", { className: "text-3xl font-extrabold text-gray-900", children: "T\u1EA1o t\u00E0i kho\u1EA3n m\u1EDBi" }), _jsx("p", { className: "text-gray-500 mt-2 font-medium", children: "Kh\u00E1m ph\u00E1 c\u00E1c t\u00EDnh n\u0103ng l\u1EADp k\u1EBF ho\u1EA1ch chuy\u00EAn nghi\u1EC7p." })] }), error && (_jsx("div", { className: "mb-6 p-4 bg-red-50 text-red-600 rounded-2xl text-sm font-bold animate-in fade-in slide-in-from-top-1", children: error })), _jsxs("form", { className: "space-y-5", onSubmit: handleSubmit, children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-sm font-bold text-gray-700 ml-1", children: "H\u1ECD v\u00E0 t\u00EAn" }), _jsxs("div", { className: "relative group", children: [_jsx(User, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), _jsx("input", { required: true, type: "text", placeholder: "Nguy\u1EC5n V\u0103n A", value: formData.name, onChange: (e) => setFormData({ ...formData, name: e.target.value }), className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-sm font-bold text-gray-700 ml-1", children: "Email" }), _jsxs("div", { className: "relative group", children: [_jsx(Mail, { className: "absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 group-focus-within:text-blue-500 transition-colors" }), _jsx("input", { required: true, type: "email", placeholder: "email@example.com", value: formData.email, onChange: (e) => setFormData({ ...formData, email: e.target.value }), className: "w-full pl-12 pr-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-sm font-bold text-gray-700 ml-1", children: "M\u1EADt kh\u1EA9u" }), _jsx("input", { required: true, type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", value: formData.password, onChange: (e) => setFormData({ ...formData, password: e.target.value }), className: "w-full px-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-sm font-bold text-gray-700 ml-1", children: "X\u00E1c nh\u1EADn" }), _jsx("input", { required: true, type: "password", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", value: formData.confirmPassword, onChange: (e) => setFormData({ ...formData, confirmPassword: e.target.value }), className: "w-full px-4 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 focus:bg-white outline-none transition-all" })] })] }), _jsxs("button", { disabled: isLoading, type: "submit", className: "w-full flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white font-bold py-5 rounded-2xl shadow-xl shadow-blue-100 transition-all active:scale-[0.98] disabled:oLine truncated
};
//# sourceMappingURL=SignupPage.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"SignupPage.js","sourceRoot":"","sources":["../SignupPage.tsx"],"names":[],"mappings":"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"}
+16 -20
View File
@@ -1,24 +1,21 @@
"use strict"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
Object.defineProperty(exports, "__esModule", { value: true }); import { useState, useEffect } from 'react';
exports.TourDetailPage = void 0; import { ItineraryTimeline } from './ItineraryTimeline.js';
const jsx_runtime_1 = require("react/jsx-runtime"); import { ExpenseManager } from './ExpenseManager.js';
const react_1 = require("react"); import { useTourStore } from './useTourStore.js';
const ItineraryTimeline_1 = require("./ItineraryTimeline"); import { Map as MapIcon, Wallet, Image as ImageIcon, Calendar, Users, ChevronLeft } from 'lucide-react';
const ExpenseManager_1 = require("./ExpenseManager"); export const TourDetailPage = () => {
const useTourStore_1 = require("./useTourStore"); const [activeTab, setActiveTab] = useState('plan');
const lucide_react_1 = require("lucide-react"); const { currentTour, fetchTour, userRole } = useTourStore();
const TourDetailPage = () => { useEffect(() => {
const [activeTab, setActiveTab] = (0, react_1.useState)('plan');
const { currentTour, fetchTour, userRole } = (0, useTourStore_1.useTourStore)();
(0, react_1.useEffect)(() => {
fetchTour(1); fetchTour(1);
}, [fetchTour]); }, [fetchTour]);
const tabs = [ const tabs = [
{ id: 'plan', label: 'Lộ trình', icon: lucide_react_1.Map, visible: ['OWNER', 'EDITOR', 'MEMBER_PLAN_ONLY'].includes(userRole || '') }, { id: 'plan', label: 'Lộ trình', icon: MapIcon, visible: ['OWNER', 'EDITOR', 'MEMBER_PLAN_ONLY'].includes(userRole || '') },
{ id: 'expense', label: 'Chi phí', icon: lucide_react_1.Wallet, visible: ['OWNER', 'EDITOR'].includes(userRole || '') }, { id: 'expense', label: 'Chi phí', icon: Wallet, visible: ['OWNER', 'EDITOR'].includes(userRole || '') },
{ id: 'photo', label: 'Ảnh', icon: lucide_react_1.Image, visible: true }, { id: 'photo', label: 'Ảnh', icon: ImageIcon, visible: true },
].filter(t => t.visible); ].filter(t => t.visible);
(0, react_1.useEffect)(() => { useEffect(() => {
if (userRole === 'MEMBER_PHOTO_ONLY') { if (userRole === 'MEMBER_PHOTO_ONLY') {
setActiveTab('photo'); setActiveTab('photo');
} }
@@ -29,9 +26,8 @@ const TourDetailPage = () => {
members: 5, members: 5,
budget: "2.500.000 VND" budget: "2.500.000 VND"
}; };
return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen bg-white", children: [(0, jsx_runtime_1.jsxs)("div", { className: "sticky top-0 z-30 bg-white border-b border-gray-100 px-4 py-3 flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("button", { className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronLeft, { className: "w-6 h-6 text-gray-600" }) }), (0, jsx_runtime_1.jsx)("h1", { className: "text-lg font-bold text-gray-800 truncate px-4", children: tourInfo.title }), (0, jsx_runtime_1.jsx)("div", { className: "w-10" }), " "] }), (0, jsx_runtime_1.jsx)("div", { className: "bg-blue-600 text-white p-6 pb-20", children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl mx-auto space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-wrap gap-4 text-sm opacity-90", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Calendar, { className: "w-4 h-4 mr-1.5" }), tourInfo.date] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Users, { className: "w-4 h-4 mr-1.5" }), tourInfo.members, " th\u00E0nh vi\u00EAn"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-baseline gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-3xl font-bold", children: tourInfo.budget }), (0, jsx_runtime_1.jsx)("span", { className: "text-blue-100 text-sm", children: "d\u1EF1 ki\u1EBFn" })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl mx-auto -mt-12 px-4 pb-24", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-white rounded-2xl shadow-xl border border-gray-100 p-1 flex mb-6", children: tabs.map((tab) => ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setActiveTab(tab.id), className: `flex-1 flex items-center justify-center py-3 rounded-xl text-sm font-semibold transition-all ${activeTab === tab.id return (_jsxs("div", { className: "min-h-screen bg-white", children: [_jsxs("div", { className: "sticky top-0 z-30 bg-white border-b border-gray-100 px-4 py-3 flex items-center justify-between", children: [_jsx("button", { className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: _jsx(ChevronLeft, { className: "w-6 h-6 text-gray-600" }) }), _jsx("h1", { className: "text-lg font-bold text-gray-800 truncate px-4", children: tourInfo.title }), _jsx("div", { className: "w-10" }), " "] }), _jsx("div", { className: "bg-blue-600 text-white p-6 pb-20", children: _jsxs("div", { className: "max-w-2xl mx-auto space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-4 text-sm opacity-90", children: [_jsxs("div", { className: "flex items-center", children: [_jsx(Calendar, { className: "w-4 h-4 mr-1.5" }), tourInfo.date] }), _jsxs("div", { className: "flex items-center", children: [_jsx(Users, { className: "w-4 h-4 mr-1.5" }), tourInfo.members, " th\u00E0nh vi\u00EAn"] })] }), _jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-3xl font-bold", children: tourInfo.budget }), _jsx("span", { className: "text-blue-100 text-sm", children: "d\u1EF1 ki\u1EBFn" })] })] }) }), _jsxs("div", { className: "max-w-2xl mx-auto -mt-12 px-4 pb-24", children: [_jsx("div", { className: "bg-white rounded-2xl shadow-xl border border-gray-100 p-1 flex mb-6", children: tabs.map((tab) => (_jsxs("button", { onClick: () => setActiveTab(tab.id), className: `flex-1 flex items-center justify-center py-3 rounded-xl text-sm font-semibold transition-all ${activeTab === tab.id
? 'bg-blue-50 text-blue-600 shadow-sm' ? 'bg-blue-50 text-blue-600 shadow-sm'
: 'text-gray-400 hover:text-gray-600'}`, children: [(0, jsx_runtime_1.jsx)(tab.icon, { className: `w-4 h-4 mr-2 ${activeTab === tab.id ? 'animate-pulse' : ''}` }), tab.label] }, tab.id))) }), (0, jsx_runtime_1.jsxs)("div", { className: "transition-opacity duration-300", children: [activeTab === 'plan' && ((0, jsx_runtime_1.jsx)("div", { className: "animate-in fade-in slide-in-from-bottom-4", children: (0, jsx_runtime_1.jsx)(ItineraryTimeline_1.ItineraryTimeline, {}) })), activeTab === 'expense' && ((0, jsx_runtime_1.jsx)("div", { className: "animate-in fade-in slide-in-from-bottom-4", children: (0, jsx_runtime_1.jsx)(ExpenseManager_1.ExpenseManager, {}) })), activeTab === 'photo' && ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-3 gap-2 animate-in fade-in", children: [1, 2, 3, 4, 5, 6].map((i) => ((0, jsx_runtime_1.jsxs)("div", { className: "aspect-square bg-gray-100 rounded-lg overflow-hidden relative group", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" }), (0, jsx_runtime_1.jsx)("img", { src: `https://picsum.photos/seed/${i + 10}/400/400`, alt: "Tour photo", className: "w-full h-full object-cover" })] }, i))) }))] })] }), (0, jsx_runtime_1.jsx)("div", { className: "fixed bottom-6 left-1/2 -translate-x-1/2 z-40", children: (0, jsx_runtime_1.jsx)("button", { className: "bg-blue-600 text-white px-6 py-3 rounded-full shadow-lg hover:bg-blue-700 transition-all flex items-center font-bold", children: activeTab === 'plan' ? 'Thêm địa điểm' : activeTab === 'expense' ? 'Thêm chi phí' : 'Đăng ảnh' }) })] })); : 'text-gray-400 hover:text-gray-600'}`, children: [_jsx(tab.icon, { className: `w-4 h-4 mr-2 ${activeTab === tab.id ? 'animate-pulse' : ''}` }), tab.label] }, tab.id))) }), _jsxs("div", { className: "transition-opacity duration-300", children: [activeTab === 'plan' && (_jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4", children: _jsx(ItineraryTimeline, {}) })), activeTab === 'expense' && (_jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4", children: _jsx(ExpenseManager, {}) })), activeTab === 'photo' && (_jsx("div", { className: "grid grid-cols-3 gap-2 animate-in fade-in", children: [1, 2, 3, 4, 5, 6].map((i) => (_jsxs("div", { className: "aspect-square bg-gray-100 rounded-lg overflow-hidden relative group", children: [_jsx("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" }), _jsx("img", { src: `https://picsum.photos/seed/${i + 10}/400/400`, alt: "Tour photo", className: "w-full h-full object-cover" })] }, i))) }))] })] }), _jsx("div", { className: "fixed bottom-6 left-1/2 -translate-x-1/2 z-40", children: _jsx("button", { className: "bg-blue-600 text-white px-6 py-3 rounded-full shadow-lg hover:bg-blue-700 transition-all flex items-center font-bold", children: activeTab === 'plan' ? 'Thêm địa điểm' : activeTab === 'expense' ? 'Thêm chi phí' : 'Đăng ảnh' }) })] }));
}; };
exports.TourDetailPage = TourDetailPage;
//# sourceMappingURL=TourDetailPage.js.map //# sourceMappingURL=TourDetailPage.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"TourDetailPage.js","sourceRoot":"","sources":["../TourDetailPage.tsx"],"names":[],"mappings":";;;;AAAA,iCAAmD;AACnD,2DAAwD;AACxD,qDAAkD;AAClD,iDAA8C;AAC9C,+CAOsB;AAEf,MAAM,cAAc,GAAG,GAAG,EAAE;IACjC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAA+B,MAAM,CAAC,CAAC;IACjF,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAA,2BAAY,GAAE,CAAC;IAE5D,IAAA,iBAAS,EAAC,GAAG,EAAE;QAEb,SAAS,CAAC,CAAC,CAAC,CAAC;IACf,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAGhB,MAAM,IAAI,GAAG;QACX,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,kBAAO,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;QAC3H,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,qBAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;QACxG,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAS,EAAE,OAAO,EAAE,IAAI,EAAE;KAC9D,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAGzB,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,QAAQ,KAAK,mBAAmB,EAAE,CAAC;YACrC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAGf,MAAM,QAAQ,GAAG;QACf,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,4BAA4B;QACzD,IAAI,EAAE,wBAAwB;QAC9B,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,eAAe;KACxB,CAAC;IAEF,OAAO,CACL,iCAAK,SAAS,EAAC,uBAAuB,aAEpC,iCAAK,SAAS,EAAC,iGAAiG,aAC9G,mCAAQ,SAAS,EAAC,sDAAsD,YACtE,uBAAC,0BAAW,IAAC,SAAS,EAAC,uBAAuB,GAAG,GAC1C,EACT,+BAAI,SAAS,EAAC,+CAA+C,YAC1D,QAAQ,CAAC,KAAK,GACZ,EACL,gCAAK,SAAS,EAAC,MAAM,GAAG,SACpB,EAGN,gCAAK,SAAS,EAAC,kCAAkC,YAC/C,iCAAK,SAAS,EAAC,6BAA6B,aAC1C,iCAAK,SAAS,EAAC,yCAAyC,aACtD,iCAAK,SAAS,EAAC,mBAAmB,aAChC,uBAAC,uBAAQ,IAAC,SAAS,EAAC,gBAAgB,GAAG,EACtC,QAAQ,CAAC,IAAI,IACV,EACN,iCAAK,SAAS,EAAC,mBAAmB,aAChC,uBAAC,oBAAK,IAAC,SAAS,EAAC,gBAAgB,GAAG,EACnC,QAAQ,CAAC,OAAO,6BACb,IACF,EACN,iCAAK,SAAS,EAAC,2BAA2B,aACxC,iCAAM,SAAS,EAAC,oBAAoB,YAAE,QAAQ,CAAC,MAAM,GAAQ,EAC7D,iCAAM,SAAS,EAAC,uBAAuB,kCAAe,IAClD,IACF,GACF,EAGN,iCAAK,SAAS,EAAC,qCAAqC,aAElD,gCAAK,SAAS,EAAC,qEAAqE,YACjF,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACjB,oCAEE,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAS,CAAC,EAC1C,SAAS,EAAE,gGACT,SAAS,KAAK,GAAG,CAAC,EAAE;gCAClB,CAAC,CAAC,oCAAoC;gCACtC,CAAC,CAAC,mCACN,EAAE,aAEF,uBAAC,GAAG,CAAC,IAAI,IAAC,SAAS,EAAE,gBAAgB,SAAS,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,GAAI,EACrF,GAAG,CAAC,KAAK,KATL,GAAG,CAAC,EAAE,CAUJ,CACV,CAAC,GACE,EAGN,iCAAK,SAAS,EAAC,iCAAiC,aAC7C,SAAS,KAAK,MAAM,IAAI,CACvB,gCAAK,SAAS,EAAC,2CAA2C,YACxD,uBAAC,qCAAiB,KAAG,GACjB,CACP,EAEA,SAAS,KAAK,SAAS,IAAI,CAC1B,gCAAK,SAAS,EAAC,2CAA2C,YACxD,uBAAC,+BAAc,KAAG,GACd,CACP,EAEA,SAAS,KAAK,OAAO,IAAI,CACxB,gCAAK,SAAS,EAAC,2CAA2C,YACvD,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC7B,iCAAa,SAAS,EAAC,qEAAqE,aAC1F,gCAAK,SAAS,EAAC,uEAAuE,GAAG,EACzF,gCACE,GAAG,EAAE,8BAA8B,CAAC,GAAG,EAAE,UAAU,EACnD,GAAG,EAAC,YAAY,EAChB,SAAS,EAAC,4BAA4B,GACtC,KANM,CAAC,CAOL,CACP,CAAC,GACE,CACP,IACG,IACF,EAGN,gCAAK,SAAS,EAAC,+CAA+C,YAC5D,mCAAQ,SAAS,EAAC,sHAAsH,YACrI,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,GACxF,GACL,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AA3HW,QAAA,cAAc,kBA2HzB"} {"version":3,"file":"TourDetailPage.js","sourceRoot":"","sources":["../TourDetailPage.tsx"],"names":[],"mappings":"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"}
+7
View File
@@ -0,0 +1,7 @@
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { PrismaService } from './prisma.service.js';
export declare class AdminGuard implements CanActivate {
private prisma;
constructor(prisma: PrismaService);
canActivate(context: ExecutionContext): Promise<boolean>;
}
+37
View File
@@ -0,0 +1,37 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Injectable, ForbiddenException } from '@nestjs/common';
import { PrismaService } from './prisma.service.js';
let AdminGuard = class AdminGuard {
constructor(prisma) {
this.prisma = prisma;
}
async canActivate(context) {
const request = context.switchToHttp().getRequest();
const user = request.user;
if (!user || !user.id) {
throw new ForbiddenException('Yêu cầu xác thực không hợp lệ. Vui lòng đăng nhập.');
}
const dbUser = await this.prisma.user.findUnique({
where: { id: user.id },
select: { isAdmin: true },
});
if (!dbUser || !dbUser.isAdmin) {
throw new ForbiddenException('Truy cập bị từ chối. Bạn không có quyền quản trị viên hệ thống.');
}
return true;
}
};
AdminGuard = __decorate([
Injectable(),
__metadata("design:paramtypes", [PrismaService])
], AdminGuard);
export { AdminGuard };
//# sourceMappingURL=admin.guard.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"admin.guard.js","sourceRoot":"","sources":["../admin.guard.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAiC,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG7C,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAGpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACtB,MAAM,IAAI,kBAAkB,CAAC,oDAAoD,CAAC,CAAC;QACrF,CAAC;QAGD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;YACtB,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,MAAM,IAAI,kBAAkB,CAAC,iEAAiE,CAAC,CAAC;QAClG,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAzBY,UAAU;IADtB,UAAU,EAAE;qCAEiB,aAAa;GAD9B,UAAU,CAyBtB"}
+5 -9
View File
@@ -1,17 +1,14 @@
"use strict"; import { BadRequestException } from '@nestjs/common';
Object.defineProperty(exports, "__esModule", { value: true }); export class ExpenseEngine {
exports.ExpenseEngine = void 0;
const common_1 = require("@nestjs/common");
class ExpenseEngine {
calculateSplit(totalCost, adultCount, childCount, childDiscountPercent) { calculateSplit(totalCost, adultCount, childCount, childDiscountPercent) {
if (totalCost < 0 || adultCount < 0 || childCount < 0 || childDiscountPercent < 0) { if (totalCost < 0 || adultCount < 0 || childCount < 0 || childDiscountPercent < 0) {
throw new common_1.BadRequestException('Dữ liệu đầu vào không được là số âm.'); throw new BadRequestException('Dữ liệu đầu vào không được là số âm.');
} }
if (childDiscountPercent > 100) { if (childDiscountPercent > 100) {
throw new common_1.BadRequestException('Phần trăm giảm giá không thể vượt quá 100%.'); throw new BadRequestException('Phần trăm giảm giá không thể vượt quá 100%.');
} }
if (adultCount + childCount === 0) { if (adultCount + childCount === 0) {
throw new common_1.BadRequestException('Số lượng người tham gia phải lớn hơn 0.'); throw new BadRequestException('Số lượng người tham gia phải lớn hơn 0.');
} }
const discountRate = childDiscountPercent / 100; const discountRate = childDiscountPercent / 100;
const childRateFactor = 1 - discountRate; const childRateFactor = 1 - discountRate;
@@ -34,5 +31,4 @@ class ExpenseEngine {
}; };
} }
} }
exports.ExpenseEngine = ExpenseEngine;
//# sourceMappingURL=expense-engine.service.js.map //# sourceMappingURL=expense-engine.service.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"expense-engine.service.js","sourceRoot":"","sources":["../expense-engine.service.ts"],"names":[],"mappings":";;;AAAA,2CAAqD;AAMrD,MAAa,aAAa;IAQxB,cAAc,CACZ,SAAiB,EACjB,UAAkB,EAClB,UAAkB,EAClB,oBAA4B;QAG5B,IAAI,SAAS,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,oBAAoB,GAAG,CAAC,EAAE,CAAC;YAClF,MAAM,IAAI,4BAAmB,CAAC,sCAAsC,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,oBAAoB,GAAG,GAAG,EAAE,CAAC;YAC/B,MAAM,IAAI,4BAAmB,CAAC,6CAA6C,CAAC,CAAC;QAC/E,CAAC;QAGD,IAAI,UAAU,GAAG,UAAU,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,4BAAmB,CAAC,yCAAyC,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,YAAY,GAAG,oBAAoB,GAAG,GAAG,CAAC;QAChD,MAAM,eAAe,GAAG,CAAC,GAAG,YAAY,CAAC;QAQzC,MAAM,wBAAwB,GAAG,UAAU,GAAG,CAAC,UAAU,GAAG,eAAe,CAAC,CAAC;QAE7E,MAAM,aAAa,GAAG,SAAS,GAAG,wBAAwB,CAAC;QAC3D,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,CAAC;QAGtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE3C,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC1C,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC5C,MAAM,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;QAE9C,OAAO;YACL,WAAW,EAAE,SAAS;YACtB,WAAW,EAAE,SAAS;YACtB,QAAQ;YACR,QAAQ;YACR,kBAAkB,EAAE,SAAS,GAAG,SAAS;YACzC,WAAW;YACX,aAAa;SACd,CAAC;IACJ,CAAC;CACF;AA5DD,sCA4DC"} {"version":3,"file":"expense-engine.service.js","sourceRoot":"","sources":["../expense-engine.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAMrD,MAAM,OAAO,aAAa;IAQxB,cAAc,CACZ,SAAiB,EACjB,UAAkB,EAClB,UAAkB,EAClB,oBAA4B;QAG5B,IAAI,SAAS,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,oBAAoB,GAAG,CAAC,EAAE,CAAC;YAClF,MAAM,IAAI,mBAAmB,CAAC,sCAAsC,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,oBAAoB,GAAG,GAAG,EAAE,CAAC;YAC/B,MAAM,IAAI,mBAAmB,CAAC,6CAA6C,CAAC,CAAC;QAC/E,CAAC;QAGD,IAAI,UAAU,GAAG,UAAU,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,mBAAmB,CAAC,yCAAyC,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,YAAY,GAAG,oBAAoB,GAAG,GAAG,CAAC;QAChD,MAAM,eAAe,GAAG,CAAC,GAAG,YAAY,CAAC;QAQzC,MAAM,wBAAwB,GAAG,UAAU,GAAG,CAAC,UAAU,GAAG,eAAe,CAAC,CAAC;QAE7E,MAAM,aAAa,GAAG,SAAS,GAAG,wBAAwB,CAAC;QAC3D,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,CAAC;QAGtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE3C,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC1C,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC5C,MAAM,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;QAE9C,OAAO;YACL,WAAW,EAAE,SAAS;YACtB,WAAW,EAAE,SAAS;YACtB,QAAQ;YACR,QAAQ;YACR,kBAAkB,EAAE,SAAS,GAAG,SAAS;YACzC,WAAW;YACX,aAAa;SACd,CAAC;IACJ,CAAC;CACF"}
+1
View File
@@ -0,0 +1 @@
import './index.css';
+7
View File
@@ -0,0 +1,7 @@
import { jsx as _jsx } from "react/jsx-runtime";
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App.js';
ReactDOM.createRoot(document.getElementById('root')).render(_jsx(React.StrictMode, { children: _jsx(App, {}) }));
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,kBAAkB,CAAC;AACxC,OAAO,aAAa,CAAC;AACrB,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAE,CAAC,CAAC,MAAM,CAC1D,KAAC,KAAK,CAAC,UAAU,cACf,KAAC,GAAG,KAAG,GACU,CACpB,CAAC"}
+4
View File
@@ -0,0 +1,4 @@
declare const JwtAuthGuard_base: import("@nestjs/passport").Type<import("@nestjs/passport").IAuthGuard>;
export declare class JwtAuthGuard extends JwtAuthGuard_base {
}
export {};
+15
View File
@@ -0,0 +1,15 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
let JwtAuthGuard = class JwtAuthGuard extends AuthGuard('jwt') {
};
JwtAuthGuard = __decorate([
Injectable()
], JwtAuthGuard);
export { JwtAuthGuard };
//# sourceMappingURL=jwt-auth.guard.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGtC,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,SAAS,CAAC,KAAK,CAAC;CAAG,CAAA;AAAxC,YAAY;IADxB,UAAU,EAAE;GACA,YAAY,CAA4B"}
+19
View File
@@ -0,0 +1,19 @@
import { Strategy } from 'passport-jwt';
import { PrismaService } from './prisma.service.js';
declare const JwtStrategy_base: new (...args: [opt: import("passport-jwt").StrategyOptionsWithRequest] | [opt: import("passport-jwt").StrategyOptionsWithoutRequest]) => Strategy & {
validate(...args: any[]): unknown;
};
export declare class JwtStrategy extends JwtStrategy_base {
private prisma;
constructor(prisma: PrismaService);
validate(payload: any): Promise<{
id: number;
name: string | null;
email: string;
passwordHash: string;
avatar: string | null;
createdAt: Date;
isAdmin: boolean;
}>;
}
export {};
+38
View File
@@ -0,0 +1,38 @@
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PrismaService } from './prisma.service.js';
let JwtStrategy = class JwtStrategy extends PassportStrategy(Strategy) {
constructor(prisma) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET || 'super-secret',
});
this.prisma = prisma;
}
async validate(payload) {
const user = await this.prisma.user.findUnique({
where: { id: payload.sub },
});
if (!user) {
throw new UnauthorizedException('Người dùng không tồn tại hoặc phiên làm việc hết hạn');
}
return user;
}
};
JwtStrategy = __decorate([
Injectable(),
__metadata("design:paramtypes", [PrismaService])
], JwtStrategy);
export { JwtStrategy };
//# sourceMappingURL=jwt.strategy.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"jwt.strategy.js","sourceRoot":"","sources":["../jwt.strategy.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG7C,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,gBAAgB,CAAC,QAAQ,CAAC;IACzD,YAAoB,MAAqB;QACvC,KAAK,CAAC;YACJ,cAAc,EAAE,UAAU,CAAC,2BAA2B,EAAE;YACxD,gBAAgB,EAAE,KAAK;YACvB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc;SACtD,CAAC,CAAC;QALe,WAAM,GAAN,MAAM,CAAe;IAMzC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAY;QACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,qBAAqB,CAAC,sDAAsD,CAAC,CAAC;QAC1F,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AApBY,WAAW;IADvB,UAAU,EAAE;qCAEiB,aAAa;GAD9B,WAAW,CAoBvB"}
+76 -18
View File
@@ -1,4 +1,3 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -11,29 +10,82 @@ var __metadata = (this && this.__metadata) || function (k, v) {
var __param = (this && this.__param) || function (paramIndex, decorator) { var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); } return function (target, key) { decorator(target, key, paramIndex); }
}; };
Object.defineProperty(exports, "__esModule", { value: true }); import { NestFactory } from '@nestjs/core';
const core_1 = require("@nestjs/core"); import { Module, Controller, Get, Post, Body, Param, ParseIntPipe, NotFoundException, BadRequestException } from '@nestjs/common';
const common_1 = require("@nestjs/common"); import { PrismaService } from './prisma.service.js';
const prisma_service_1 = require("./prisma.service"); import 'dotenv/config';
require("dotenv/config"); import * as bcrypt from 'bcrypt';
let AppController = class AppController { let AppController = class AppController {
getHello() { getHello() {
return 'Travel Planning API is running!'; return 'Travel Planning API is running!';
} }
}; };
__decorate([ __decorate([
(0, common_1.Get)(), Get(),
__metadata("design:type", Function), __metadata("design:type", Function),
__metadata("design:paramtypes", []), __metadata("design:paramtypes", []),
__metadata("design:returntype", String) __metadata("design:returntype", String)
], AppController.prototype, "getHello", null); ], AppController.prototype, "getHello", null);
AppController = __decorate([ AppController = __decorate([
(0, common_1.Controller)() Controller()
], AppController); ], AppController);
let AuthController = class AuthController {
constructor(prisma) {
this.prisma = prisma;
}
async getStatus() {
const userCount = await this.prisma.user.count();
return { isInitialSetup: userCount === 0 };
}
async signup(body) {
const { email, password, name } = body;
const existingUser = await this.prisma.user.findUnique({ where: { email } });
if (existingUser)
throw new BadRequestException('Email đã được sử dụng');
const userCount = await this.prisma.user.count();
const shouldBeAdmin = userCount === 0;
const passwordHash = await bcrypt.hash(password, 10);
return this.prisma.user.create({
data: { email, passwordHash, name, isAdmin: shouldBeAdmin },
select: { id: true, email: true, name: true, isAdmin: true }
});
}
};
__decorate([
Get('status'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], AuthController.prototype, "getStatus", null);
__decorate([
Post('signup'),
__param(0, Body()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "signup", null);
AuthController = __decorate([
Controller('v1/auth'),
__metadata("design:paramtypes", [PrismaService])
], AuthController);
let TourController = class TourController { let TourController = class TourController {
constructor(prisma) { constructor(prisma) {
this.prisma = prisma; this.prisma = prisma;
} }
async getPublicTours() {
return this.prisma.tour.findMany({
take: 20,
include: {
photos: { take: 1 },
legs: {
take: 1,
include: {
places: { take: 1 }
}
}
}
});
}
async getTourDetails(id) { async getTourDetails(id) {
const tour = await this.prisma.tour.findUnique({ const tour = await this.prisma.tour.findUnique({
where: { id }, where: { id },
@@ -50,32 +102,38 @@ let TourController = class TourController {
}, },
}); });
if (!tour) if (!tour)
throw new common_1.NotFoundException(`Không tìm thấy Tour với ID ${id}`); throw new NotFoundException(`Không tìm thấy Tour với ID ${id}`);
return tour; return tour;
} }
}; };
__decorate([ __decorate([
(0, common_1.Get)(':id'), Get('explore'),
__param(0, (0, common_1.Param)('id', common_1.ParseIntPipe)), __metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], TourController.prototype, "getPublicTours", null);
__decorate([
Get(':id'),
__param(0, Param('id', ParseIntPipe)),
__metadata("design:type", Function), __metadata("design:type", Function),
__metadata("design:paramtypes", [Number]), __metadata("design:paramtypes", [Number]),
__metadata("design:returntype", Promise) __metadata("design:returntype", Promise)
], TourController.prototype, "getTourDetails", null); ], TourController.prototype, "getTourDetails", null);
TourController = __decorate([ TourController = __decorate([
(0, common_1.Controller)('api/v1/tours'), Controller('v1/tours'),
__metadata("design:paramtypes", [prisma_service_1.PrismaService]) __metadata("design:paramtypes", [PrismaService])
], TourController); ], TourController);
let AppModule = class AppModule { let AppModule = class AppModule {
}; };
AppModule = __decorate([ AppModule = __decorate([
(0, common_1.Module)({ Module({
controllers: [AppController, TourController], controllers: [AppController, AuthController, TourController],
providers: [prisma_service_1.PrismaService], providers: [PrismaService],
exports: [prisma_service_1.PrismaService] exports: [PrismaService]
}) })
], AppModule); ], AppModule);
async function bootstrap() { async function bootstrap() {
const app = await core_1.NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
app.enableCors(); app.enableCors();
app.setGlobalPrefix('api'); app.setGlobalPrefix('api');
const port = process.env.PORT || 3001; const port = process.env.PORT || 3001;
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,uCAA2C;AAC3C,2CAAiG;AACjG,qDAAiD;AACjD,yBAAuB;AAGvB,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEjB,QAAQ;QACN,OAAO,iCAAiC,CAAC;IAC3C,CAAC;CACF,CAAA;AAHC;IADC,IAAA,YAAG,GAAE;;;;6CAGL;AAJG,aAAa;IADlB,IAAA,mBAAU,GAAE;GACP,aAAa,CAKlB;AAED,IACM,cAAc,GADpB,MACM,cAAc;IAClB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGvC,AAAN,KAAK,CAAC,cAAc,CAA4B,EAAU;QACxD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE;oBACJ,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;oBAClC,OAAO,EAAE;wBACP,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE;wBAC7C,QAAQ,EAAE,IAAI;qBACf;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,0BAAiB,CAAC,8BAA8B,EAAE,EAAE,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAnBO;IADL,IAAA,YAAG,EAAC,KAAK,CAAC;IACW,WAAA,IAAA,cAAK,EAAC,IAAI,EAAE,qBAAY,CAAC,CAAA;;;;oDAkB9C;AAtBG,cAAc;IADnB,IAAA,mBAAU,EAAC,cAAc,CAAC;qCAEG,8BAAa;GADrC,cAAc,CAuBnB;AAOD,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,SAAS;IALd,IAAA,eAAM,EAAC;QACN,WAAW,EAAE,CAAC,aAAa,EAAE,cAAc,CAAC;QAC5C,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACI,SAAS,CAAG;AAElB,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChD,GAAG,CAAC,UAAU,EAAE,CAAC;IACjB,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;IACtC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;AACnE,CAAC;AACD,SAAS,EAAE,CAAC"} {"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAClI,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,eAAe,CAAC;AACvB,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEjB,QAAQ;QACN,OAAO,iCAAiC,CAAC;IAC3C,CAAC;CACF,CAAA;AAHC;IADC,GAAG,EAAE;;;;6CAGL;AAJG,aAAa;IADlB,UAAU,EAAE;GACP,aAAa,CAKlB;AAED,IACM,cAAc,GADpB,MACM,cAAc;IAClB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGvC,AAAN,KAAK,CAAC,SAAS;QACb,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACjD,OAAO,EAAE,cAAc,EAAE,SAAS,KAAK,CAAC,EAAE,CAAC;IAC7C,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CAAS,IAAS;QAC5B,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QAGvC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,YAAY;YAAE,MAAM,IAAI,mBAAmB,CAAC,uBAAuB,CAAC,CAAC;QAGzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,aAAa,GAAG,SAAS,KAAK,CAAC,CAAC;QAGtC,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAErD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YAC7B,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE;YAC3D,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;SAC7D,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAzBO;IADL,GAAG,CAAC,QAAQ,CAAC;;;;+CAIb;AAGK;IADL,IAAI,CAAC,QAAQ,CAAC;IACD,WAAA,IAAI,EAAE,CAAA;;;;4CAkBnB;AA5BG,cAAc;IADnB,UAAU,CAAC,SAAS,CAAC;qCAEQ,aAAa;GADrC,cAAc,CA6BnB;AAED,IACM,cAAc,GADpB,MACM,cAAc;IAClB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAGvC,AAAN,KAAK,CAAC,cAAc;QAElB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC/B,IAAI,EAAE,EAAE;YACR,OAAO,EAAE;gBACP,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;gBACnB,IAAI,EAAE;oBACJ,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE;wBACP,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;qBACpB;iBACF;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,cAAc,CAA4B,EAAU;QACxD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE;oBACJ,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;oBAClC,OAAO,EAAE;wBACP,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE;wBAC7C,QAAQ,EAAE,IAAI;qBACf;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,EAAE,EAAE,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AApCO;IADL,GAAG,CAAC,SAAS,CAAC;;;;oDAed;AAGK;IADL,GAAG,CAAC,KAAK,CAAC;IACW,WAAA,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;;;;oDAkB9C;AAvCG,cAAc;IADnB,UAAU,CAAC,UAAU,CAAC;qCAEO,aAAa;GADrC,cAAc,CAwCnB;AAOD,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,SAAS;IALd,MAAM,CAAC;QACN,WAAW,EAAE,CAAC,aAAa,EAAE,cAAc,EAAE,cAAc,CAAC;QAC5D,SAAS,EAAE,CAAC,aAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,aAAa,CAAC;KACzB,CAAC;GACI,SAAS,CAAG;AAElB,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChD,GAAG,CAAC,UAAU,EAAE,CAAC;IACjB,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;IACtC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;AACnE,CAAC;AACD,SAAS,EAAE,CAAC"}
+3 -5
View File
@@ -1,8 +1,6 @@
"use strict"; import { defineConfig } from '@prisma/config';
Object.defineProperty(exports, "__esModule", { value: true }); import 'dotenv/config';
const config_1 = require("@prisma/config"); export default defineConfig({
require("dotenv/config");
exports.default = (0, config_1.defineConfig)({
datasource: { datasource: {
url: process.env.DATABASE_URL, url: process.env.DATABASE_URL,
}, },
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"prisma.config.js","sourceRoot":"","sources":["../prisma.config.ts"],"names":[],"mappings":";;AAAA,2CAA8C;AAC9C,yBAAuB;AAEvB,kBAAe,IAAA,qBAAY,EAAC;IAC1B,UAAU,EAAE;QAEV,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;KAC9B;CACF,CAAC,CAAC"} {"version":3,"file":"prisma.config.js","sourceRoot":"","sources":["../prisma.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,eAAe,CAAC;AAEvB,eAAe,YAAY,CAAC;IAC1B,UAAU,EAAE;QAEV,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;KAC9B;CACF,CAAC,CAAC"}
+11 -14
View File
@@ -1,4 +1,3 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -8,17 +7,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
var __metadata = (this && this.__metadata) || function (k, v) { var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}; };
Object.defineProperty(exports, "__esModule", { value: true }); import { Injectable } from '@nestjs/common';
exports.PrismaService = void 0; import { PrismaClient } from '@prisma/client';
const common_1 = require("@nestjs/common"); import { PrismaPg } from '@prisma/adapter-pg';
const client_1 = require("@prisma/client"); import { Pool } from 'pg';
const adapter_pg_1 = require("@prisma/adapter-pg"); import 'dotenv/config';
const pg_1 = require("pg"); let PrismaService = class PrismaService extends PrismaClient {
require("dotenv/config");
let PrismaService = class PrismaService extends client_1.PrismaClient {
constructor() { constructor() {
const pool = new pg_1.Pool({ connectionString: process.env.DATABASE_URL }); const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new adapter_pg_1.PrismaPg(pool); const adapter = new PrismaPg(pool);
super({ adapter }); super({ adapter });
this.pool = pool; this.pool = pool;
} }
@@ -28,9 +25,9 @@ let PrismaService = class PrismaService extends client_1.PrismaClient {
await this.pool.end(); await this.pool.end();
} }
}; };
exports.PrismaService = PrismaService; PrismaService = __decorate([
exports.PrismaService = PrismaService = __decorate([ Injectable(),
(0, common_1.Injectable)(),
__metadata("design:paramtypes", []) __metadata("design:paramtypes", [])
], PrismaService); ], PrismaService);
export { PrismaService };
//# sourceMappingURL=prisma.service.js.map //# sourceMappingURL=prisma.service.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../prisma.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA2E;AAC3E,2CAA8C;AAC9C,mDAA8C;AAC9C,2BAA0B;AAC1B,yBAAuB;AAGhB,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAG7C;QACE,MAAM,IAAI,GAAG,IAAI,SAAI,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,qBAAQ,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,YAAY,KAAK,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC/C,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;CACF,CAAA;AAfY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;;GACA,aAAa,CAezB"} {"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAiC,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,eAAe,CAAC;AAGhB,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,YAAY;IAG7C;QACE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,YAAY,KAAK,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC/C,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;CACF,CAAA;AAfY,aAAa;IADzB,UAAU,EAAE;;GACA,aAAa,CAezB"}
+1 -1
View File
@@ -1,5 +1,5 @@
import { CanActivate, ExecutionContext } from '@nestjs/common'; import { CanActivate, ExecutionContext } from '@nestjs/common';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service.js';
export declare class TourRoleGuard implements CanActivate { export declare class TourRoleGuard implements CanActivate {
private prisma; private prisma;
constructor(prisma: PrismaService); constructor(prisma: PrismaService);
+9 -12
View File
@@ -1,4 +1,3 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -8,10 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
var __metadata = (this && this.__metadata) || function (k, v) { var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}; };
Object.defineProperty(exports, "__esModule", { value: true }); import { Injectable, ForbiddenException } from '@nestjs/common';
exports.TourRoleGuard = void 0; import { PrismaService } from './prisma.service.js';
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("./prisma.service");
let TourRoleGuard = class TourRoleGuard { let TourRoleGuard = class TourRoleGuard {
constructor(prisma) { constructor(prisma) {
this.prisma = prisma; this.prisma = prisma;
@@ -22,7 +19,7 @@ let TourRoleGuard = class TourRoleGuard {
const tourId = parseInt(request.params.id || request.params.tourId); const tourId = parseInt(request.params.id || request.params.tourId);
const path = request.url; const path = request.url;
if (!user || isNaN(tourId)) { if (!user || isNaN(tourId)) {
throw new common_1.ForbiddenException("Thông tin xác thực hoặc mã Tour không hợp lệ."); throw new ForbiddenException("Thông tin xác thực hoặc mã Tour không hợp lệ.");
} }
const membership = await this.prisma.tourMember.findUnique({ const membership = await this.prisma.tourMember.findUnique({
where: { where: {
@@ -33,7 +30,7 @@ let TourRoleGuard = class TourRoleGuard {
}, },
}); });
if (!membership) { if (!membership) {
throw new common_1.ForbiddenException("Bạn không phải là thành viên của tour này."); throw new ForbiddenException("Bạn không phải là thành viên của tour này.");
} }
request.tourMembership = membership; request.tourMembership = membership;
const role = membership.role; const role = membership.role;
@@ -41,14 +38,14 @@ let TourRoleGuard = class TourRoleGuard {
const isExpensePath = path.includes('/expenses'); const isExpensePath = path.includes('/expenses');
if ((role === 'MEMBER_PHOTO_ONLY' || role === 'VIEWER_EXTERNAL') && if ((role === 'MEMBER_PHOTO_ONLY' || role === 'VIEWER_EXTERNAL') &&
(isPlanPath || isExpensePath)) { (isPlanPath || isExpensePath)) {
throw new common_1.ForbiddenException("Bạn không có quyền xem kế hoạch và chi phí của tour này."); throw new ForbiddenException("Bạn không có quyền xem kế hoạch và chi phí của tour này.");
} }
return true; return true;
} }
}; };
exports.TourRoleGuard = TourRoleGuard; TourRoleGuard = __decorate([
exports.TourRoleGuard = TourRoleGuard = __decorate([ Injectable(),
(0, common_1.Injectable)(), __metadata("design:paramtypes", [PrismaService])
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], TourRoleGuard); ], TourRoleGuard);
export { TourRoleGuard };
//# sourceMappingURL=rbac.middleware.js.map //# sourceMappingURL=rbac.middleware.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"rbac.middleware.js","sourceRoot":"","sources":["../rbac.middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+F;AAC/F,qDAAiD;AAG1C,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAG1B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;QAEzB,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,2BAAkB,CAAC,+CAA+C,CAAC,CAAC;QAChF,CAAC;QAOD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;YACzD,KAAK,EAAE;gBACL,aAAa,EAAE;oBACb,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,2BAAkB,CAAC,4CAA4C,CAAC,CAAC;QAC7E,CAAC;QAGD,OAAO,CAAC,cAAc,GAAG,UAAU,CAAC;QAEpC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;QAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAGjD,IACE,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,iBAAiB,CAAC;YAC5D,CAAC,UAAU,IAAI,aAAa,CAAC,EAC7B,CAAC;YACD,MAAM,IAAI,2BAAkB,CAAC,0DAA0D,CAAC,CAAC;QAC3F,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAlDY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAEiB,8BAAa;GAD9B,aAAa,CAkDzB"} {"version":3,"file":"rbac.middleware.js","sourceRoot":"","sources":["../rbac.middleware.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAiC,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG7C,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAG1B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;QAEzB,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,kBAAkB,CAAC,+CAA+C,CAAC,CAAC;QAChF,CAAC;QAOD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;YACzD,KAAK,EAAE;gBACL,aAAa,EAAE;oBACb,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,kBAAkB,CAAC,4CAA4C,CAAC,CAAC;QAC7E,CAAC;QAGD,OAAO,CAAC,cAAc,GAAG,UAAU,CAAC;QAEpC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;QAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAGjD,IACE,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,iBAAiB,CAAC;YAC5D,CAAC,UAAU,IAAI,aAAa,CAAC,EAC7B,CAAC;YACD,MAAM,IAAI,kBAAkB,CAAC,0DAA0D,CAAC,CAAC;QAC3F,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAlDY,aAAa;IADzB,UAAU,EAAE;qCAEiB,aAAa;GAD9B,aAAa,CAkDzB"}
+7 -9
View File
@@ -1,12 +1,10 @@
"use strict"; import { PrismaClient } from '@prisma/client';
Object.defineProperty(exports, "__esModule", { value: true }); import { PrismaPg } from '@prisma/adapter-pg';
const client_1 = require("@prisma/client"); import { Pool } from 'pg';
const adapter_pg_1 = require("@prisma/adapter-pg"); import 'dotenv/config';
const pg_1 = require("pg"); const pool = new Pool({ connectionString: process.env.DATABASE_URL });
require("dotenv/config"); const adapter = new PrismaPg(pool);
const pool = new pg_1.Pool({ connectionString: process.env.DATABASE_URL }); const prisma = new PrismaClient({ adapter });
const adapter = new adapter_pg_1.PrismaPg(pool);
const prisma = new client_1.PrismaClient({ adapter });
async function main() { async function main() {
console.log('--- Đang xóa dữ liệu cũ... ---'); console.log('--- Đang xóa dữ liệu cũ... ---');
await prisma.expense.deleteMany(); await prisma.expense.deleteMany();
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../seed.ts"],"names":[],"mappings":"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"} {"version":3,"file":"seed.js","sourceRoot":"","sources":["../seed.ts"],"names":[],"mappings":"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"}
+8
View File
@@ -0,0 +1,8 @@
declare const _default: {
content: string[];
theme: {
extend: {};
};
plugins: any[];
};
export default _default;
+12
View File
@@ -0,0 +1,12 @@
export default {
content: [
"./index.html",
"./*.{js,ts,jsx,tsx}",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
};
//# sourceMappingURL=tailwind.config.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"tailwind.config.js","sourceRoot":"","sources":["../tailwind.config.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,OAAO,EAAE;QACP,cAAc;QACd,qBAAqB;QACrB,4BAA4B;KAC7B;IACD,KAAK,EAAE;QACL,MAAM,EAAE,EAAE;KACX;IACD,OAAO,EAAE,EAAE;CACK,CAAA"}
+1 -1
View File
@@ -1 +1 @@
{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../node_modules/typescript/lib/lib.scripthost.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/typescript/lib/lib.es2021.full.d.ts","../node_modules/@types/react/global.d.ts","../node_modules/csstype/index.d.ts","../node_modules/@types/prop-types/index.d.ts","../node_modules/@types/react/index.d.ts","../node_modules/@types/react/jsx-runtime.d.ts","../node_modules/lucide-react/dist/lucide-react.d.ts","../LoginModal.tsx","../LandingPage.tsx","../node_modules/date-fns/typings.d.ts","../node_modules/zustand/vanilla.d.ts","../node_modules/zustand/react.d.ts","../node_modules/zustand/index.d.ts","../useTourStore.ts","../ItineraryTimeline.tsx","../ExpenseManager.tsx","../TourDetailPage.tsx","../App.tsx","../node_modules/reflect-metadata/index.d.ts","../node_modules/@nestjs/common/decorators/core/bind.decorator.d.ts","../node_modules/@nestjs/common/interfaces/abstract.interface.d.ts","../node_modules/@nestjs/common/interfaces/controllers/controller-metadata.interface.d.ts","../node_modules/@nestjs/common/interfaces/controllers/controller.interface.d.ts","../node_modules/@nestjs/common/interfaces/features/arguments-host.interface.d.ts","../node_modules/@nestjs/common/interfaces/exceptions/exception-filter.interface.d.ts","../node_modules/rxjs/dist/types/internal/Subscription.d.ts","../node_modules/rxjs/dist/types/internal/Subscriber.d.ts","../node_modules/rxjs/dist/types/internal/Operator.d.ts","../node_modules/rxjs/dist/types/internal/Observable.d.ts","../node_modules/rxjs/dist/types/internal/types.d.ts","../node_modules/rxjs/dist/types/internal/operators/audit.d.ts","../node_modules/rxjs/dist/types/internal/operators/auditTime.d.ts","../node_modules/rxjs/dist/types/internal/operators/buffer.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferCount.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferTime.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferToggle.d.ts","../node_modules/rxjs/dist/types/internal/operators/bLine truncated {"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../node_modules/typescript/lib/lib.scripthost.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/typescript/lib/lib.es2021.full.d.ts","../node_modules/@types/react/global.d.ts","../node_modules/csstype/index.d.ts","../node_modules/@types/prop-types/index.d.ts","../node_modules/@types/react/index.d.ts","../node_modules/@types/react/jsx-runtime.d.ts","../node_modules/lucide-react/dist/lucide-react.d.ts","../LoginModal.tsx","../LandingPage.tsx","../node_modules/date-fns/typings.d.ts","../node_modules/zustand/esm/vanilla.d.mts","../node_modules/zustand/esm/react.d.mts","../node_modules/zustand/esm/index.d.mts","../useTourStore.ts","../ItineraryTimeline.tsx","../ExpenseManager.tsx","../TourDetailPage.tsx","../node_modules/@types/geojson/index.d.ts","../node_modules/@types/leaflet/index.d.ts","../node_modules/react-leaflet/lib/hooks.d.ts","../node_modules/react-leaflet/lib/AttributionControl.d.ts","../node_modules/@react-leaflet/core/lib/attribution.d.ts","../node_modules/@react-leaflet/core/lib/context.d.ts","../node_modules/@react-leaflet/core/lib/element.d.ts","../node_modules/@react-leaflet/core/lib/events.d.ts","../node_modules/@react-leaflet/core/lib/layer.d.ts","../node_modules/@react-leaflet/core/lib/path.d.ts","../node_modules/@react-leaflet/core/lib/circle.d.ts","../node_modules/@react-leaflet/core/lib/div-overlay.d.ts","../node_modules/@react-leaflet/core/lib/component.d.ts","../node_modules/@react-leaflet/core/lib/control.d.ts","../node_modules/@react-leaflet/core/lib/dom.d.ts","../node_modules/@react-leaflet/core/lib/generic.d.ts","../node_modules/@react-leaflet/core/lib/grid-layer.d.ts","../node_modules/@react-leaflet/core/lib/media-overlay.d.ts","../node_modules/@react-leaflet/core/lib/pane.d.ts","../node_modules/@react-leaflet/core/lib/index.d.ts","../node_modules/react-leaflet/lib/Circle.d.ts","../node_modules/react-leaflet/lib/CircleMarker.d.ts","../node_modules/react-leaflet/lib/LayerGroup.d.ts","../node_modules/react-leaflet/lib/FeatureLine truncated
+2
View File
@@ -1,11 +1,13 @@
interface TourState { interface TourState {
currentTour: any; currentTour: any;
legs: any[]; legs: any[];
publicTours: any[];
userRole: string | null; userRole: string | null;
setTour: (tour: any) => void; setTour: (tour: any) => void;
updateLegs: (legs: any[]) => void; updateLegs: (legs: any[]) => void;
optimizeRouting: () => Promise<void>; optimizeRouting: () => Promise<void>;
fetchTour: (id: number) => Promise<void>; fetchTour: (id: number) => Promise<void>;
fetchPublicTours: () => Promise<void>;
} }
export declare const useTourStore: import("zustand").UseBoundStore<import("zustand").StoreApi<TourState>>; export declare const useTourStore: import("zustand").UseBoundStore<import("zustand").StoreApi<TourState>>;
export {}; export {};
+8 -5
View File
@@ -1,10 +1,8 @@
"use strict"; import { create } from 'zustand';
Object.defineProperty(exports, "__esModule", { value: true }); export const useTourStore = create((set, get) => ({
exports.useTourStore = void 0;
const zustand_1 = require("zustand");
exports.useTourStore = (0, zustand_1.create)((set, get) => ({
currentTour: null, currentTour: null,
legs: [], legs: [],
publicTours: [],
userRole: null, userRole: null,
setTour: (tour) => set({ currentTour: tour }), setTour: (tour) => set({ currentTour: tour }),
updateLegs: (legs) => set({ legs }), updateLegs: (legs) => set({ legs }),
@@ -15,6 +13,11 @@ exports.useTourStore = (0, zustand_1.create)((set, get) => ({
const role = data.members?.find((m) => m.userId === currentUserId)?.role || 'VIEWER_EXTERNAL'; const role = data.members?.find((m) => m.userId === currentUserId)?.role || 'VIEWER_EXTERNAL';
set({ currentTour: data, legs: data.legs || [], userRole: role }); set({ currentTour: data, legs: data.legs || [], userRole: role });
}, },
fetchPublicTours: async () => {
const response = await fetch(`http://localhost:3001/api/v1/tours/explore`);
const data = await response.json();
set({ publicTours: data });
},
optimizeRouting: async () => { optimizeRouting: async () => {
}, },
})); }));
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"useTourStore.js","sourceRoot":"","sources":["../useTourStore.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AAYpB,QAAA,YAAY,GAAG,IAAA,gBAAM,EAAY,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3D,WAAW,EAAE,IAAI;IACjB,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IACnC,SAAS,EAAE,KAAK,EAAE,EAAU,EAAE,EAAE;QAE9B,MAAM,aAAa,GAAG,CAAC,CAAC;QACxB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,sCAAsC,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,EAAE,IAAI,IAAI,iBAAiB,CAAC;QACnG,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,eAAe,EAAE,KAAK,IAAI,EAAE;IAE5B,CAAC;CACF,CAAC,CAAC,CAAC"} {"version":3,"file":"useTourStore.js","sourceRoot":"","sources":["../useTourStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAcjC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAY,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3D,WAAW,EAAE,IAAI;IACjB,IAAI,EAAE,EAAE;IACR,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC7C,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IACnC,SAAS,EAAE,KAAK,EAAE,EAAU,EAAE,EAAE;QAE9B,MAAM,aAAa,GAAG,CAAC,CAAC;QACxB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,sCAAsC,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,EAAE,IAAI,IAAI,iBAAiB,CAAC;QACnG,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,gBAAgB,EAAE,KAAK,IAAI,EAAE;QAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,eAAe,EAAE,KAAK,IAAI,EAAE;IAE5B,CAAC;CACF,CAAC,CAAC,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;
+11
View File
@@ -0,0 +1,11 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3002,
strictPort: true,
host: true,
},
});
//# sourceMappingURL=vite.config.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"vite.config.js","sourceRoot":"","sources":["../vite.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,MAAM,sBAAsB,CAAC;AAEzC,eAAe,YAAY,CAAC;IAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,MAAM,EAAE;QACN,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,IAAI;KACX;CACF,CAAC,CAAC"}
+1 -1
View File
@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import App from './App'; import App from './App.js';
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
+1 -1
View File
@@ -1,7 +1,7 @@
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt'; import { ExtractJwt, Strategy } from 'passport-jwt';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service.js';
@Injectable() @Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) { export class JwtStrategy extends PassportStrategy(Strategy) {
+7 -1
View File
@@ -1,6 +1,6 @@
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { Module, Controller, Get, Post, Body, Param, ParseIntPipe, NotFoundException, BadRequestException } from '@nestjs/common'; import { Module, Controller, Get, Post, Body, Param, ParseIntPipe, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service.js';
import 'dotenv/config'; import 'dotenv/config';
import * as bcrypt from 'bcrypt'; import * as bcrypt from 'bcrypt';
@@ -16,6 +16,12 @@ class AppController {
class AuthController { class AuthController {
constructor(private prisma: PrismaService) {} constructor(private prisma: PrismaService) {}
@Get('status')
async getStatus() {
const userCount = await this.prisma.user.count();
return { isInitialSetup: userCount === 0 };
}
@Post('signup') @Post('signup')
async signup(@Body() body: any) { async signup(@Body() body: any) {
const { email, password, name } = body; const { email, password, name } = body;
+724 -12
View File
File diff suppressed because it is too large. Load diff
+6 -1
View File
@@ -15,13 +15,17 @@
"dependencies": { "dependencies": {
"@nestjs/common": "^11.1.26", "@nestjs/common": "^11.1.26",
"@nestjs/core": "^11.1.26", "@nestjs/core": "^11.1.26",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.26", "@nestjs/platform-express": "^11.1.26",
"@prisma/adapter-pg": "^7.8.0", "@prisma/adapter-pg": "^7.8.0",
"@prisma/client": "^7.8.0", "@prisma/client": "^7.8.0",
"bcrypt": "^5.1.1",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"dotenv": "^16.3.0", "dotenv": "^16.3.0",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lucide-react": "^0.284.0", "lucide-react": "^0.284.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"pg": "^8.11.0", "pg": "^8.11.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@@ -36,6 +40,7 @@
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/leaflet": "^1.9.12", "@types/leaflet": "^1.9.12",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@types/passport-jwt": "^4.0.1",
"@types/pg": "^8.10.0", "@types/pg": "^8.10.0",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@vitejs/plugin-react": "^6.0.2", "@vitejs/plugin-react": "^6.0.2",
@@ -44,7 +49,7 @@
"prisma": "^7.8.0", "prisma": "^7.8.0",
"tailwindcss": "^4.3.1", "tailwindcss": "^4.3.1",
"tsx": "^4.0.0", "tsx": "^4.0.0",
"typescript": "^5.0.0", "typescript": "^5.7.0",
"vite": "^8.0.16" "vite": "^8.0.16"
} }
} }
+1 -1
View File
@@ -1,5 +1,5 @@
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service.js';
@Injectable() @Injectable()
export class TourRoleGuard implements CanActivate { export class TourRoleGuard implements CanActivate {
+1
View File
@@ -151,3 +151,4 @@ model Photo {
place Place? @relation(fields: [placeId], references: [id], onDelete: SetNull) place Place? @relation(fields: [placeId], references: [id], onDelete: SetNull)
uploader User @relation(fields: [uploaderId], references: [id]) uploader User @relation(fields: [uploaderId], references: [id])
} }
+4 -4
View File
@@ -1,11 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"ignoreDeprecations": "6.0", "ignoreDeprecations": "5.0",
"module": "ESNext", "module": "NodeNext",
"moduleResolution": "node", "moduleResolution": "NodeNext",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true, "rewriteRelativeImportExtensions": true,
"noEmit": true, "noEmit": false,
"jsx": "react-jsx", "jsx": "react-jsx",
"declaration": true, "declaration": true,
"removeComments": true, "removeComments": true,