Sửa hiển thị điểm đầu và điểm cuối của chặng
This commit is contained in:
Vendored
+5
-1
@@ -9,19 +9,23 @@ export const AddLocationModal = ({ isOpen, onClose, tourId }) => {
|
||||
latitude: 10.7769,
|
||||
longitude: 106.7009,
|
||||
type: 'VISIT',
|
||||
legId: '',
|
||||
plannedStart: '',
|
||||
plannedEnd: ''
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const legs = useTourStore(state => state.legs);
|
||||
const addLocation = useTourStore(state => state.addLocation);
|
||||
if (!isOpen)
|
||||
return null;
|
||||
const currentLegId = formData.legId || (legs.length > 0 ? legs[0].id : '');
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await addLocation(tourId, {
|
||||
...formData,
|
||||
legId: currentLegId,
|
||||
latitude: parseFloat(formData.latitude),
|
||||
longitude: parseFloat(formData.longitude),
|
||||
});
|
||||
@@ -34,6 +38,6 @@ export const AddLocationModal = ({ isOpen, onClose, tourId }) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return (_jsxs("div", { className: "fixed inset-0 z-[2000] flex items-center justify-center p-4", children: [_jsx("div", { className: "absolute inset-0 bg-gray-900/60 backdrop-blur-sm", onClick: onClose }), _jsxs("div", { className: "relative w-full max-w-lg bg-white rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] overflow-y-auto", children: [_jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsxs("h2", { className: "text-2xl font-bold text-gray-900 flex items-center gap-2", children: [_jsx(MapPin, { className: "w-6 h-6 text-blue-600" }), " Th\u00EAm \u0111\u1ECBa \u0111i\u1EC3m m\u1EDBi"] }), _jsx("button", { onClick: onClose, className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: _jsx(X, { className: "w-6 h-6 text-gray-400" }) })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "T\u00EAn \u0111\u1ECBa \u0111i\u1EC3m" }), _jsx("input", { required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.name, onChange: e => setFormData({ ...formData, name: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "\u0110\u1ECBa ch\u1EC9" }), _jsx("input", { className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.address, onChange: e => setFormData({ ...formData, address: e.target.value }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "V\u0129 \u0111\u1ED9" }), _jsx("input", { type: "number", step: "any", required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.latitude, onChange: e => setFormData({ ...formData, latitude: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "Kinh \u0111\u1ED9" }), _jsx("input", { type: "number", step: "any", required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.longitude, onChange: e => setFormData({ ...formData, longitude: e.target.value }) })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "Lo\u1EA1i" }), _jsxs("select", { className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.type, onChange: e => setFormData({ ...formData, type: e.target.value }), children: [_jsx("option", { value: "VISIT", children: "Tham quan" }), _jsx("option", { value: "EAT", children: "\u0102n u\u1ED1ng" }), _jsx("option", { value: "REST", children: "Ngh\u1EC9 ng\u01A1i" }), _jsx("option", { value: "MOVE", children: "Di chuy\u1EC3n" })] })] }), _jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "B\u1EAFt \u0111\u1EA7u" }), _jsx("input", { type: "datetime-local", className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.plannedStart, onChange: e => setFormData({ ...formData, plannedStart: e.target.value }) })] }) }), _jsx("button", { disabled: isLoading, className: "w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-2xl flex items-center justify-center gap-2 mt-4", children: isLoading ? _jsx(Loader2, { className: "w-5 h-5 animate-spin" }) : 'Thêm địa điểm' })] })] })] }));
|
||||
return (_jsxs("div", { className: "fixed inset-0 z-[2000] flex items-center justify-center p-4", children: [_jsx("div", { className: "absolute inset-0 bg-gray-900/60 backdrop-blur-sm", onClick: onClose }), _jsxs("div", { className: "relative w-full max-w-lg bg-white rounded-3xl shadow-2xl overflow-hidden p-8 max-h-[90vh] overflow-y-auto", children: [_jsxs("div", { className: "flex justify-between items-center mb-6", children: [_jsxs("h2", { className: "text-2xl font-bold text-gray-900 flex items-center gap-2", children: [_jsx(MapPin, { className: "w-6 h-6 text-blue-600" }), " Th\u00EAm \u0111\u1ECBa \u0111i\u1EC3m m\u1EDBi"] }), _jsx("button", { onClick: onClose, className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: _jsx(X, { className: "w-6 h-6 text-gray-400" }) })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "T\u00EAn \u0111\u1ECBa \u0111i\u1EC3m" }), _jsx("input", { required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.name, onChange: e => setFormData({ ...formData, name: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "\u0110\u1ECBa ch\u1EC9" }), _jsx("input", { className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.address, onChange: e => setFormData({ ...formData, address: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "G\u00E1n v\u00E0o ch\u1EB7ng" }), _jsx("select", { required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: currentLegId, onChange: e => setFormData({ ...formData, legId: e.target.value }), children: legs.map(leg => (_jsxs("option", { value: leg.id, children: ["Ch\u1EB7ng ", leg.sequence, ": ", leg.note || 'Không có tên'] }, leg.id))) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "V\u0129 \u0111\u1ED9" }), _jsx("input", { type: "number", step: "any", required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.latitude, onChange: e => setFormData({ ...formData, latitude: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "Kinh \u0111\u1ED9" }), _jsx("input", { type: "number", step: "any", required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.longitude, onChange: e => setFormData({ ...formData, longitude: e.target.value }) })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "Lo\u1EA1i" }), _jsxs("select", { className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.type, onChange: e => setFormData({ ...formData, type: e.target.value }), children: [_jsx("option", { value: "VISIT", children: "Tham quan" }), _jsx("option", { value: "EAT", children: "\u0102n u\u1ED1ng" }), _jsx("option", { value: "REST", children: "Ngh\u1EC9 ng\u01A1i" }), _jsx("option", { value: "MOVE", children: "Di chuy\u1EC3n" })] })] }), _jsx("div", { className: "grid grid-cols-2 gap-4", children: _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-bold text-gray-700 mb-1", children: "B\u1EAFt \u0111\u1EA7u" }), _jsx("input", { type: "datetime-local", className: "w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none", value: formData.plannedStart, onChange: e => setFormData({ ...formData, plannedStart: e.target.value }) })] }) }), _jsx("button", { disabled: isLoading, className: "w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-2xl flex items-center justify-center gap-2 mt-4", children: isLoading ? _jsx(Loader2, { className: "w-5 h-5 animate-spin" }) : 'Thêm địa điểm' })] })] })] }));
|
||||
};
|
||||
//# sourceMappingURL=AddLocationModal.js.map
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"AddLocationModal.js","sourceRoot":"","sources":["../AddLocationModal.tsx"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"AddLocationModal.js","sourceRoot":"","sources":["../AddLocationModal.tsx"],"names":[],"mappings":"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"}
|
||||
Vendored
+10
-3
@@ -1,6 +1,6 @@
|
||||
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
||||
import { format, differenceInMinutes, parseISO } from 'date-fns';
|
||||
import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation, Edit2, Trash2, Plus } from 'lucide-react';
|
||||
import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation, Edit2, Trash2, Plus, List } from 'lucide-react';
|
||||
import { useTourStore } from './useTourStore.js';
|
||||
const TimeVariance = ({ planned, actual }) => {
|
||||
if (!actual)
|
||||
@@ -25,7 +25,7 @@ const formatTravelTime = (minutes) => {
|
||||
return mins > 0 ? `${hours} giờ ${mins} phút` : `${hours} giờ`;
|
||||
};
|
||||
export const ItineraryTimeline = () => {
|
||||
const { currentTour, legs, optimizeRouting, userRole, activeLegId, setActiveLegId, addLeg, updateLeg, deleteLeg } = useTourStore();
|
||||
const { currentTour, legs, optimizeRouting, userRole, activeLegId, setActiveLegId, addLeg, updateLeg, deleteLeg, initializeLegs } = useTourStore();
|
||||
const toggleComplete = async (locationId) => {
|
||||
console.log("Toggle status for location:", locationId);
|
||||
};
|
||||
@@ -35,6 +35,13 @@ export const ItineraryTimeline = () => {
|
||||
await addLeg(currentTour.id, { note });
|
||||
}
|
||||
};
|
||||
const handleDeclareLegs = async () => {
|
||||
const countStr = window.prompt("Chuyến đi này bạn muốn chia làm bao nhiêu chặng?", "3");
|
||||
const count = parseInt(countStr || "0");
|
||||
if (count > 0 && currentTour) {
|
||||
await initializeLegs(currentTour.id, count);
|
||||
}
|
||||
};
|
||||
const handleEditLeg = async (leg) => {
|
||||
const note = window.prompt("Sửa ghi chú chặng:", leg.note || "");
|
||||
if (note !== null) {
|
||||
@@ -54,7 +61,7 @@ export const ItineraryTimeline = () => {
|
||||
const activeLeg = legs.find(l => l.id === activeLegId) || legs[0];
|
||||
return (_jsxs("div", { className: "max-w-2xl mx-auto p-0 bg-gray-50 min-h-screen", children: [_jsx("div", { className: "sticky top-[136px] z-20 bg-gray-50/80 backdrop-blur-sm pb-4 mb-4", children: _jsxs("div", { className: "flex overflow-x-auto gap-2 px-2 no-scrollbar py-2", children: [legs.map((leg) => (_jsxs("button", { onClick: () => setActiveLegId(leg.id), className: `flex-shrink-0 px-6 py-2.5 rounded-2xl text-xs font-black uppercase tracking-widest transition-all border ${activeLegId === leg.id
|
||||
? 'bg-blue-600 text-white border-blue-600 shadow-lg shadow-blue-100 scale-105'
|
||||
: 'bg-white text-gray-400 border-gray-100 hover:border-gray-200'}`, children: ["Ch\u1EB7ng ", leg.sequence] }, leg.id))), ['OWNER', 'MANAGER'].includes(userRole || '') && (_jsx("button", { onClick: handleAddLeg, className: "flex-shrink-0 p-2.5 rounded-2xl bg-white text-blue-600 border border-dashed border-blue-200 hover:bg-blue-50 transition-all flex items-center justify-center w-12", children: _jsx(Plus, { className: "w-5 h-5" }) }))] }) }), _jsx("div", { className: "px-2", children: activeLeg && (_jsx("div", { className: "relative animate-in fade-in slide-in-from-right-4 duration-300", children: (() => {
|
||||
: 'bg-white text-gray-400 border-gray-100 hover:border-gray-200'}`, children: ["Ch\u1EB7ng ", leg.sequence] }, leg.id))), ['OWNER', 'MANAGER'].includes(userRole || '') && (_jsxs("button", { onClick: handleDeclareLegs, className: "flex-shrink-0 px-4 py-2.5 rounded-2xl bg-blue-50 text-blue-600 border border-dashed border-blue-200 hover:bg-blue-100 transition-all flex items-center gap-2 text-xs font-bold", title: "Khai b\u00E1o nhanh s\u1ED1 l\u01B0\u1EE3ng ch\u1EB7ng", children: [_jsx(List, { className: "w-4 h-4" }), " Khai b\u00E1o s\u1ED1 ch\u1EB7ng"] })), ['OWNER', 'MANAGER'].includes(userRole || '') && (_jsx("button", { onClick: handleAddLeg, className: "flex-shrink-0 p-2.5 rounded-2xl bg-white text-blue-600 border border-dashed border-blue-200 hover:bg-blue-50 transition-all flex items-center justify-center w-12", children: _jsx(Plus, { className: "w-5 h-5" }) }))] }) }), _jsx("div", { className: "px-2", children: activeLeg && (_jsx("div", { className: "relative animate-in fade-in slide-in-from-right-4 duration-300", children: (() => {
|
||||
const leg = activeLeg;
|
||||
const totalDwellMinutes = leg.locations.reduce((acc, loc) => {
|
||||
if (loc.plannedStart && loc.plannedEnd) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+8
@@ -89,6 +89,7 @@ export const TourDetailPage = ({ onBack }) => {
|
||||
const setMapCenter = useTourStore(state => state.setMapCenter);
|
||||
const updateTourStartPoint = useTourStore(state => state.updateTourStartPoint);
|
||||
const updateTourEndPoint = useTourStore(state => state.updateTourEndPoint);
|
||||
const initializeLegs = useTourStore(state => state.initializeLegs);
|
||||
const addLocation = useTourStore(state => state.addLocation);
|
||||
const [mapZoom] = useState(initialViewState?.zoom || 13);
|
||||
const allLocations = useMemo(() => legs.flatMap(l => l.locations), [legs]);
|
||||
@@ -108,6 +109,13 @@ export const TourDetailPage = ({ onBack }) => {
|
||||
fetchTour(publicTours[0].id);
|
||||
}
|
||||
}, [publicTours, currentTour, fetchTour]);
|
||||
const handleDeclareLegs = async () => {
|
||||
const countStr = window.prompt("Chuyến đi này bạn muốn chia làm bao nhiêu chặng?", "3");
|
||||
const count = parseInt(countStr || "0");
|
||||
if (count > 0 && currentTour) {
|
||||
await initializeLegs(currentTour.id, count);
|
||||
}
|
||||
};
|
||||
const handleMapAction = async (action, latlng) => {
|
||||
console.log(`[FRONTEND] Triggered ${action} at:`, { lat: latlng.lat, lng: latlng.lng });
|
||||
const currentLegs = useTourStore.getState().legs;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+42
@@ -202,6 +202,39 @@ let TourController = class TourController {
|
||||
}
|
||||
});
|
||||
}
|
||||
async initializeLegs(tourId, body) {
|
||||
const { count } = body;
|
||||
if (count <= 0 || count > 20)
|
||||
throw new BadRequestException('Số lượng chặng không hợp lệ (1-20)');
|
||||
const existingLegs = await this.prisma.leg.findMany({
|
||||
where: { tourId },
|
||||
orderBy: { sequence: 'asc' }
|
||||
});
|
||||
const needed = count - existingLegs.length;
|
||||
if (needed > 0) {
|
||||
const createData = Array.from({ length: needed }).map((_, i) => ({
|
||||
tourId,
|
||||
sequence: existingLegs.length + i + 1,
|
||||
note: `Chặng ${existingLegs.length + i + 1}`
|
||||
}));
|
||||
await this.prisma.leg.createMany({ data: createData });
|
||||
}
|
||||
const allLegs = await this.prisma.leg.findMany({
|
||||
where: { tourId },
|
||||
orderBy: { sequence: 'asc' }
|
||||
});
|
||||
const lastLeg = allLegs[allLegs.length - 1];
|
||||
const endPoint = await this.prisma.location.findFirst({
|
||||
where: { leg: { tourId }, plannedEnd: new Date(0) }
|
||||
});
|
||||
if (endPoint && lastLeg && endPoint.legId !== lastLeg.id) {
|
||||
await this.prisma.location.update({
|
||||
where: { id: endPoint.id },
|
||||
data: { legId: lastLeg.id }
|
||||
});
|
||||
}
|
||||
return allLegs;
|
||||
}
|
||||
async addLeg(tourId, body) {
|
||||
const tour = await this.prisma.tour.findUnique({
|
||||
where: { id: tourId },
|
||||
@@ -306,6 +339,15 @@ __decorate([
|
||||
__metadata("design:paramtypes", [String, Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TourController.prototype, "updateTourEndPoint", null);
|
||||
__decorate([
|
||||
UseGuards(JwtAuthGuard),
|
||||
Post(':tourId/legs/batch'),
|
||||
__param(0, Param('tourId', ParseUUIDPipe)),
|
||||
__param(1, Body()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TourController.prototype, "initializeLegs", null);
|
||||
__decorate([
|
||||
UseGuards(JwtAuthGuard),
|
||||
Post(':tourId/legs'),
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -11,6 +11,7 @@ interface TourState {
|
||||
updateTour: (id: string, data: any) => Promise<void>;
|
||||
deleteTour: (id: string) => Promise<void>;
|
||||
addLeg: (tourId: string, data: any) => Promise<void>;
|
||||
initializeLegs: (tourId: string, count: number) => Promise<void>;
|
||||
updateLeg: (legId: string, data: any) => Promise<void>;
|
||||
deleteLeg: (legId: string) => Promise<void>;
|
||||
addLocation: (tourId: string, locationData: any) => Promise<void>;
|
||||
|
||||
Vendored
+14
@@ -85,6 +85,20 @@ export const useTourStore = create((set, get) => ({
|
||||
throw new Error('Lỗi khi thêm chặng');
|
||||
get().fetchTour(tourId);
|
||||
},
|
||||
initializeLegs: async (tourId, count) => {
|
||||
const API_BASE = `http://${window.location.hostname}:3001`;
|
||||
const response = await fetch(`${API_BASE}/api/v1/tours/${tourId}/legs/batch`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
},
|
||||
body: JSON.stringify({ count }),
|
||||
});
|
||||
if (!response.ok)
|
||||
throw new Error('Lỗi khi khởi tạo chặng');
|
||||
await get().fetchTour(tourId);
|
||||
},
|
||||
updateLeg: async (legId, data) => {
|
||||
const API_BASE = `http://${window.location.hostname}:3001`;
|
||||
const response = await fetch(`${API_BASE}/api/v1/legs/${legId}`, {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user