Thêm tính năng owner có thể thêm thành viên, member thêm thành viên sẽ pending

This commit is contained in:
3dtours committed 2026-06-14 22:14:40 +07:00
1 parent 7cc724a133
commit c6341aa12f
16 files changed
+395 -23

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
name: Local Config
version: 1.0.0
schema: v1
models:
- name: Autodetect
provider: lmstudio
model: AUTODETECT
apiBase: http://192.168.1.12:1234/v1/
+15 -5
View File
@@ -5,6 +5,7 @@ import { useTourStore } from './useTourStore.js';
import { AddLocationModal } from './AddLocationModal.js'; import { AddLocationModal } from './AddLocationModal.js';
import { AddMemberModal } from './AddMemberModal.js'; import { AddMemberModal } from './AddMemberModal.js';
import { ConfirmModal } from './ConfirmModal.js'; import { ConfirmModal } from './ConfirmModal.js';
import { NotificationModal, useNotificationModal } from './components/NotificationModal.js';
import { MapContainer, TileLayer, Marker, Popup, useMapEvents, Polyline } from 'react-leaflet'; import { MapContainer, TileLayer, Marker, Popup, useMapEvents, Polyline } from 'react-leaflet';
import _MarkerClusterGroup from 'react-leaflet-cluster'; import _MarkerClusterGroup from 'react-leaflet-cluster';
const MarkerClusterGroup = (_MarkerClusterGroup as any).default || _MarkerClusterGroup; const MarkerClusterGroup = (_MarkerClusterGroup as any).default || _MarkerClusterGroup;
@@ -181,6 +182,8 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
fetchJoinRequests, acceptJoinRequest, rejectJoinRequest fetchJoinRequests, acceptJoinRequest, rejectJoinRequest
} = useTourStore(); } = useTourStore();
const notificationModal = useNotificationModal();
// Khôi phục vị trí và mức zoom từ localStorage // Khôi phục vị trí và mức zoom từ localStorage
const [initialViewState] = useState(() => { const [initialViewState] = useState(() => {
const saved = localStorage.getItem('map_view_state'); const saved = localStorage.getItem('map_view_state');
@@ -443,7 +446,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
await acceptJoinRequest(currentTour.id, req.id); await acceptJoinRequest(currentTour.id, req.id);
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Không thể chấp nhận yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể chấp nhận yêu cầu', 'error');
} finally { } finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
setConfirmState({ open: false }); setConfirmState({ open: false });
@@ -472,7 +475,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
await rejectJoinRequest(currentTour.id, req.id); await rejectJoinRequest(currentTour.id, req.id);
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Không thể từ chối yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể từ chối yêu cầu', 'error');
} finally { } finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
setConfirmState({ open: false }); setConfirmState({ open: false });
@@ -721,7 +724,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
await acceptJoinRequest(currentTour.id, req.id); await acceptJoinRequest(currentTour.id, req.id);
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Không thể chấp nhận yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể chấp nhận yêu cầu', 'error');
} finally { } finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
setConfirmState({ open: false }); setConfirmState({ open: false });
@@ -748,7 +751,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
await rejectJoinRequest(currentTour.id, req.id); await rejectJoinRequest(currentTour.id, req.id);
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Không thể từ chối yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể từ chối yêu cầu', 'error');
} finally { } finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
setConfirmState({ open: false }); setConfirmState({ open: false });
@@ -849,7 +852,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
await removeMember(currentTour.id, selectedMember.userId); await removeMember(currentTour.id, selectedMember.userId);
setIsMemberDetailOpen(false); setIsMemberDetailOpen(false);
} catch (e) { } catch (e) {
alert('Không thể xóa thành viên'); notificationModal.openModal('Thông báo', 'Không thể xóa thành viên', 'error');
} }
}} }}
className="px-3 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-sm font-bold" className="px-3 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-sm font-bold"
@@ -879,6 +882,13 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
onConfirm={() => confirmState.onConfirm?.()} onConfirm={() => confirmState.onConfirm?.()}
onCancel={() => setConfirmState({ open: false })} onCancel={() => setConfirmState({ open: false })}
/> />
<NotificationModal
isOpen={notificationModal.modalState?.isOpen ?? false}
title={notificationModal.modalState?.title}
message={notificationModal.modalState?.message}
type={notificationModal.modalState?.type}
onConfirm={() => notificationModal.closeModal()}
/>
</div> </div>
); );
}; };
+208
View File
@@ -0,0 +1,208 @@
import React, { useState } from 'react';
// Types for notification modal
export interface NotificationModalProps {
isOpen: boolean;
title?: string;
message: string;
onConfirm?: () => void;
onCancel?: () => void;
type?: 'info' | 'success' | 'warning' | 'error';
confirmButtonText?: string;
cancelButtonText?: string;
}
// Icon components for different types
const Icons = {
info: (props: React.SVGProps<SVGSVGElement>) => (
<svg {...props} viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
<path d="M12 16v-4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<path d="M12 8h.01" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
</svg>
),
success: (props: React.SVGProps<SVGSVGElement>) => (
<svg {...props} viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
<path d="M8 12l2.5 2.5L15.5 9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
),
warning: (props: React.SVGProps<SVGSVGElement>) => (
<svg {...props} viewBox="0 0 24 24" fill="none">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" stroke="currentColor" strokeWidth="2" />
<line x1="12" y1="9" x2="12" y2="13" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<circle cx="12" cy="17" r="1" fill="currentColor" />
</svg>
),
error: (props: React.SVGProps<SVGSVGElement>) => (
<svg {...props} viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" />
<line x1="15" y1="9" x2="9" y2="15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<line x1="9" y1="9" x2="15" y2="15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
</svg>
),
};
// Default props
const defaultProps: Partial<NotificationModalProps> = {
title: 'Thông báo',
type: 'info',
confirmButtonText: 'OK',
cancelButtonText: 'Hủy',
};
export const NotificationModal: React.FC<NotificationModalProps> = ({
isOpen,
title = defaultProps.title,
message,
onConfirm,
onCancel,
type = defaultProps.type,
confirmButtonText = defaultProps.confirmButtonText,
cancelButtonText = defaultProps.cancelButtonText,
}) => {
const [isAnimating, setIsAnimating] = useState(false);
// Get colors based on type
const getTypeStyles = () => {
switch (type) {
case 'success':
return { bg: '#d4edda', border: '#c3e6cb', text: '#155724' };
case 'warning':
return { bg: '#fff3cd', border: '#ffeeba', text: '#856404' };
case 'error':
return { bg: '#f8d7da', border: '#f5c6cb', text: '#721c24' };
default:
return { bg: '#e2e3e5', border: '#d6d8db', text: '#383d41' };
}
};
const styles = getTypeStyles();
// Animation classes based on state
const getAnimationClass = () => {
if (!isOpen) return 'opacity-0 translate-y-4';
if (isAnimating && onCancel) return 'animate-fade-out';
return 'animate-fade-in';
};
// Handle confirm click
const handleConfirm = () => {
setIsAnimating(true);
onConfirm?.();
setTimeout(() => setIsAnimating(false), 300);
};
// Handle cancel click
const handleCancel = () => {
setIsAnimating(true);
onCancel?.();
setTimeout(() => setIsAnimating(false), 300);
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[100] p-4">
<div
className={`bg-white rounded-lg shadow-xl max-w-md w-full transform transition-all duration-300 ${getAnimationClass()}`}
role="alertdialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-message"
>
{/* Header */}
<div className={`p-6 border-b ${styles.border}`}>
<div className="flex items-center gap-3">
{type === 'success' && <Icons.success className="w-5 h-5 text-green-600" />}
{type === 'warning' && <Icons.warning className="w-5 h-5 text-yellow-600" />}
{type === 'error' && <Icons.error className="w-5 h-5 text-red-600" />}
{type === 'info' && <Icons.info className="w-5 h-5 text-blue-600" />}
<h2 id="modal-title" className={`text-xl font-semibold ${styles.text}`}>
{title}
</h2>
</div>
</div>
{/* Body */}
<div className="p-6">
<p id="modal-message" className="text-gray-700 leading-relaxed">{message}</p>
</div>
{/* Footer */}
<div className={`px-6 py-4 flex justify-end gap-3 border-t ${styles.border}`}>
{onCancel && (
<button
onClick={handleCancel}
className="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors"
>
{cancelButtonText}
</button>
)}
{onConfirm && (
<button
onClick={handleConfirm}
className={`px-4 py-2 text-white rounded-md font-medium transition-colors ${
type === 'error'
? 'bg-red-600 hover:bg-red-700'
: 'bg-blue-600 hover:bg-blue-700'
}`}
>
{confirmButtonText}
</button>
)}
</div>
</div>
</div>
);
};
// Hook for easy usage without props management
export const useNotificationModal = () => {
const [modalState, setModalState] = useState<{
isOpen: boolean;
title?: string;
message: string;
type?: 'info' | 'success' | 'warning' | 'error';
onConfirm?: () => void;
onCancel?: () => void;
} | null>(null);
const openModal = (
title: string,
message: string,
type: 'info' | 'success' | 'warning' | 'error' = 'info',
onConfirm?: () => void,
onCancel?: () => void,
) => {
setModalState({
isOpen: true,
title,
message,
type,
onConfirm,
onCancel,
});
// Auto-close after 5 seconds if no confirm action
const timer = setTimeout(() => {
if (onCancel) {
setModalState((prev) => ({ ...prev, isOpen: false }));
}
}, 5000);
return () => clearTimeout(timer);
};
const closeModal = () => {
setModalState((prev) => prev ? { ...prev, isOpen: false } : null);
};
return {
modalState,
openModal,
closeModal,
};
};
export default NotificationModal;
+8 -6
View File
@@ -6,6 +6,7 @@ import { useTourStore } from './useTourStore.js';
import { AddLocationModal } from './AddLocationModal.js'; import { AddLocationModal } from './AddLocationModal.js';
import { AddMemberModal } from './AddMemberModal.js'; import { AddMemberModal } from './AddMemberModal.js';
import { ConfirmModal } from './ConfirmModal.js'; import { ConfirmModal } from './ConfirmModal.js';
import { NotificationModal, useNotificationModal } from './components/NotificationModal.js';
import { MapContainer, TileLayer, Marker, Popup, useMapEvents, Polyline } from 'react-leaflet'; import { MapContainer, TileLayer, Marker, Popup, useMapEvents, Polyline } from 'react-leaflet';
import _MarkerClusterGroup from 'react-leaflet-cluster'; import _MarkerClusterGroup from 'react-leaflet-cluster';
const MarkerClusterGroup = _MarkerClusterGroup.default || _MarkerClusterGroup; const MarkerClusterGroup = _MarkerClusterGroup.default || _MarkerClusterGroup;
@@ -104,6 +105,7 @@ export const TourDetailPage = ({ onBack }) => {
const [joinRequestActionId, setJoinRequestActionId] = useState(null); const [joinRequestActionId, setJoinRequestActionId] = useState(null);
const [confirmState, setConfirmState] = useState({ open: false }); const [confirmState, setConfirmState] = useState({ open: false });
const { currentTour, legs, fetchTour, fetchPublicTours, publicTours, userRole, mapCenter, setMapCenter, updateTourStartPoint, updateTourEndPoint, initializeLegs, addLocation, addMember, removeMember, fetchJoinRequests, acceptJoinRequest, rejectJoinRequest } = useTourStore(); const { currentTour, legs, fetchTour, fetchPublicTours, publicTours, userRole, mapCenter, setMapCenter, updateTourStartPoint, updateTourEndPoint, initializeLegs, addLocation, addMember, removeMember, fetchJoinRequests, acceptJoinRequest, rejectJoinRequest } = useTourStore();
const notificationModal = useNotificationModal();
const [initialViewState] = useState(() => { const [initialViewState] = useState(() => {
const saved = localStorage.getItem('map_view_state'); const saved = localStorage.getItem('map_view_state');
if (saved) { if (saved) {
@@ -267,7 +269,7 @@ export const TourDetailPage = ({ onBack }) => {
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} }
catch (e) { catch (e) {
alert(e.message || 'Không thể chấp nhận yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể chấp nhận yêu cầu', 'error');
} }
finally { finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
@@ -290,7 +292,7 @@ export const TourDetailPage = ({ onBack }) => {
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} }
catch (e) { catch (e) {
alert(e.message || 'Không thể từ chối yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể từ chối yêu cầu', 'error');
} }
finally { finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
@@ -333,7 +335,7 @@ export const TourDetailPage = ({ onBack }) => {
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} }
catch (e) { catch (e) {
alert(e.message || 'Không thể chấp nhận yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể chấp nhận yêu cầu', 'error');
} }
finally { finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
@@ -355,7 +357,7 @@ export const TourDetailPage = ({ onBack }) => {
setJoinRequests((prev) => prev.filter((r) => r.id !== req.id)); setJoinRequests((prev) => prev.filter((r) => r.id !== req.id));
} }
catch (e) { catch (e) {
alert(e.message || 'Không thể từ chối yêu cầu'); notificationModal.openModal('Thông báo', e.message || 'Không thể từ chối yêu cầu', 'error');
} }
finally { finally {
setJoinRequestActionId(null); setJoinRequestActionId(null);
@@ -376,11 +378,11 @@ export const TourDetailPage = ({ onBack }) => {
setIsMemberDetailOpen(false); setIsMemberDetailOpen(false);
} }
catch (e) { catch (e) {
alert('Không thể xóa thành viên'); notificationModal.openModal('Thông báo', 'Không thể xóa thành viên', 'error');
} }
}, className: "px-3 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-sm font-bold", children: "X\u00F3a" })), canEdit && selectedMember.role === 'OWNER' && (_jsx("button", { onClick: () => { }, className: "px-3 py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-sm font-bold", children: "X\u00F3a" })), canEdit && selectedMember.role === 'OWNER' && (_jsx("button", { onClick: () => {
setIsMemberDetailOpen(false); setIsMemberDetailOpen(false);
setIsAddMemberOpen(true); setIsAddMemberOpen(true);
}, className: "px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-xl text-sm font-bold", children: "M\u1EDDi th\u00EAm ng\u01B0\u1EDDi" }))] })] })] })), _jsx(ConfirmModal, { isOpen: confirmState.open, title: confirmState.title, message: confirmState.message, onConfirm: () => confirmState.onConfirm?.(), onCancel: () => setConfirmState({ open: false }) })] })); }, className: "px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-xl text-sm font-bold", children: "M\u1EDDi th\u00EAm ng\u01B0\u1EDDi" }))] })] })] })), _jsx(ConfirmModal, { isOpen: confirmState.open, title: confirmState.title, message: confirmState.message, onConfirm: () => confirmState.onConfirm?.(), onCancel: () => setConfirmState({ open: false }) }), _jsx(NotificationModal, { isOpen: notificationModal.modalState?.isOpen ?? false, title: notificationModal.modalState?.title, message: notificationModal.modalState?.message, type: notificationModal.modalState?.type, onConfirm: () => notificationModal.closeModal() })] }));
}; };
//# 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":"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 truncated {"version":3,"file":"TourDetailPage.js","sourceRoot":"","sources":["../TourDetailPage.tsx"],"names":[],"mappings":"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 truncated
+25
View File
@@ -0,0 +1,25 @@
import React from 'react';
export interface NotificationModalProps {
isOpen: boolean;
title?: string;
message: string;
onConfirm?: () => void;
onCancel?: () => void;
type?: 'info' | 'success' | 'warning' | 'error';
confirmButtonText?: string;
cancelButtonText?: string;
}
export declare const NotificationModal: React.FC<NotificationModalProps>;
export declare const useNotificationModal: () => {
modalState: {
isOpen: boolean;
title?: string;
message: string;
type?: "info" | "success" | "warning" | "error";
onConfirm?: () => void;
onCancel?: () => void;
};
openModal: (title: string, message: string, type?: "info" | "success" | "warning" | "error", onConfirm?: () => void, onCancel?: () => void) => () => void;
closeModal: () => void;
};
export default NotificationModal;
+81
View File
@@ -0,0 +1,81 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from 'react';
const Icons = {
info: (props) => (_jsxs("svg", { ...props, viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }), _jsx("path", { d: "M12 16v-4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("path", { d: "M12 8h.01", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" })] })),
success: (props) => (_jsxs("svg", { ...props, viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }), _jsx("path", { d: "M8 12l2.5 2.5L15.5 9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] })),
warning: (props) => (_jsxs("svg", { ...props, viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z", stroke: "currentColor", strokeWidth: "2" }), _jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("circle", { cx: "12", cy: "17", r: "1", fill: "currentColor" })] })),
error: (props) => (_jsxs("svg", { ...props, viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }), _jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })] })),
};
const defaultProps = {
title: 'Thông báo',
type: 'info',
confirmButtonText: 'OK',
cancelButtonText: 'Hủy',
};
export const NotificationModal = ({ isOpen, title = defaultProps.title, message, onConfirm, onCancel, type = defaultProps.type, confirmButtonText = defaultProps.confirmButtonText, cancelButtonText = defaultProps.cancelButtonText, }) => {
const [isAnimating, setIsAnimating] = useState(false);
const getTypeStyles = () => {
switch (type) {
case 'success':
return { bg: '#d4edda', border: '#c3e6cb', text: '#155724' };
case 'warning':
return { bg: '#fff3cd', border: '#ffeeba', text: '#856404' };
case 'error':
return { bg: '#f8d7da', border: '#f5c6cb', text: '#721c24' };
default:
return { bg: '#e2e3e5', border: '#d6d8db', text: '#383d41' };
}
};
const styles = getTypeStyles();
const getAnimationClass = () => {
if (!isOpen)
return 'opacity-0 translate-y-4';
if (isAnimating && onCancel)
return 'animate-fade-out';
return 'animate-fade-in';
};
const handleConfirm = () => {
setIsAnimating(true);
onConfirm?.();
setTimeout(() => setIsAnimating(false), 300);
};
const handleCancel = () => {
setIsAnimating(true);
onCancel?.();
setTimeout(() => setIsAnimating(false), 300);
};
if (!isOpen)
return null;
return (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-[100] p-4", children: _jsxs("div", { className: `bg-white rounded-lg shadow-xl max-w-md w-full transform transition-all duration-300 ${getAnimationClass()}`, role: "alertdialog", "aria-modal": "true", "aria-labelledby": "modal-title", "aria-describedby": "modal-message", children: [_jsx("div", { className: `p-6 border-b ${styles.border}`, children: _jsxs("div", { className: "flex items-center gap-3", children: [type === 'success' && _jsx(Icons.success, { className: "w-5 h-5 text-green-600" }), type === 'warning' && _jsx(Icons.warning, { className: "w-5 h-5 text-yellow-600" }), type === 'error' && _jsx(Icons.error, { className: "w-5 h-5 text-red-600" }), type === 'info' && _jsx(Icons.info, { className: "w-5 h-5 text-blue-600" }), _jsx("h2", { id: "modal-title", className: `text-xl font-semibold ${styles.text}`, children: title })] }) }), _jsx("div", { className: "p-6", children: _jsx("p", { id: "modal-message", className: "text-gray-700 leading-relaxed", children: message }) }), _jsxs("div", { className: `px-6 py-4 flex justify-end gap-3 border-t ${styles.border}`, children: [onCancel && (_jsx("button", { onClick: handleCancel, className: "px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition-colors", children: cancelButtonText })), onConfirm && (_jsx("button", { onClick: handleConfirm, className: `px-4 py-2 text-white rounded-md font-medium transition-colors ${type === 'error'
? 'bg-red-600 hover:bg-red-700'
: 'bg-blue-600 hover:bg-blue-700'}`, children: confirmButtonText }))] })] }) }));
};
export const useNotificationModal = () => {
const [modalState, setModalState] = useState(null);
const openModal = (title, message, type = 'info', onConfirm, onCancel) => {
setModalState({
isOpen: true,
title,
message,
type,
onConfirm,
onCancel,
});
const timer = setTimeout(() => {
if (onCancel) {
setModalState((prev) => ({ ...prev, isOpen: false }));
}
}, 5000);
return () => clearTimeout(timer);
};
const closeModal = () => {
setModalState((prev) => prev ? { ...prev, isOpen: false } : null);
};
return {
modalState,
openModal,
closeModal,
};
};
export default NotificationModal;
//# sourceMappingURL=NotificationModal.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"NotificationModal.js","sourceRoot":"","sources":["../../components/NotificationModal.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAexC,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,CAAC,KAAoC,EAAE,EAAE,CAAC,CAC9C,kBAAS,KAAK,EAAE,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,aAC7C,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,GAAG,EACvE,eAAM,CAAC,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,GAAG,EAClF,eAAM,CAAC,EAAC,WAAW,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,GAAG,IAC9E,CACP;IACD,OAAO,EAAE,CAAC,KAAoC,EAAE,EAAE,CAAC,CACjD,kBAAS,KAAK,EAAE,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,aAC7C,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,GAAG,EACvE,eAAM,CAAC,EAAC,sBAAsB,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,GAAG,IAChH,CACP;IACD,OAAO,EAAE,CAAC,KAAoC,EAAE,EAAE,CAAC,CACjD,kBAAS,KAAK,EAAE,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,aAC7C,eAAM,CAAC,EAAC,0FAA0F,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,GAAG,EAC3I,eAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,GAAG,EACnG,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,EAAC,IAAI,EAAC,cAAc,GAAG,IAChD,CACP;IACD,KAAK,EAAE,CAAC,KAAoC,EAAE,EAAE,CAAC,CAC/C,kBAAS,KAAK,EAAE,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,aAC7C,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,GAAG,EACvE,eAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,GAAG,EAClG,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,GAAG,IAC9F,CACP;CACF,CAAC;AAGF,MAAM,YAAY,GAAoC;IACpD,KAAK,EAAE,WAAW;IAClB,IAAI,EAAE,MAAM;IACZ,iBAAiB,EAAE,IAAI;IACvB,gBAAgB,EAAE,KAAK;CACxB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAqC,CAAC,EAClE,MAAM,EACN,KAAK,GAAG,YAAY,CAAC,KAAK,EAC1B,OAAO,EACP,SAAS,EACT,QAAQ,EACR,IAAI,GAAG,YAAY,CAAC,IAAI,EACxB,iBAAiB,GAAG,YAAY,CAAC,iBAAiB,EAClD,gBAAgB,GAAG,YAAY,CAAC,gBAAgB,GACjD,EAAE,EAAE;IACH,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAGtD,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,SAAS;gBACZ,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC/D,KAAK,SAAS;gBACZ,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC/D,KAAK,OAAO;gBACV,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC/D;gBACE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACjE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAG/B,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,IAAI,CAAC,MAAM;YAAE,OAAO,yBAAyB,CAAC;QAC9C,IAAI,WAAW,IAAI,QAAQ;YAAE,OAAO,kBAAkB,CAAC;QACvD,OAAO,iBAAiB,CAAC;IAC3B,CAAC,CAAC;IAGF,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,SAAS,EAAE,EAAE,CAAC;QACd,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC,CAAC;IAGF,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,QAAQ,EAAE,EAAE,CAAC;QACb,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC,CAAC;IAEF,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,CACL,cAAK,SAAS,EAAC,wEAAwE,YACrF,eACE,SAAS,EAAE,uFAAuF,iBAAiB,EAAE,EAAE,EACvH,IAAI,EAAC,aAAa,gBACP,MAAM,qBACD,aAAa,sBACZ,eAAe,aAGhC,cAAK,SAAS,EAAE,gBAAgB,MAAM,CAAC,MAAM,EAAE,YAC7C,eAAK,SAAS,EAAC,yBAAyB,aACrC,IAAI,KAAK,SAAS,IAAI,KAAC,KAAK,CAAC,OAAO,IAAC,SAAS,EAAC,wBAAwB,GAAG,EAC1E,IAAI,KAAK,SAAS,IAAI,KAAC,KAAK,CAAC,OAAO,IAAC,SAAS,EAAC,yBAAyB,GAAG,EAC3E,IAAI,KAAK,OAAO,IAAI,KAAC,KAAK,CAAC,KAAK,IAAC,SAAS,EAAC,sBAAsB,GAAG,EACpE,IAAI,KAAK,MAAM,IAAI,KAAC,KAAK,CAAC,IAAI,IAAC,SAAS,EAAC,uBAAuB,GAAG,EACpE,aAAI,EAAE,EAAC,aAAa,EAAC,SAAS,EAAE,yBAAyB,MAAM,CAAC,IAAI,EAAE,YACnE,KAAK,GACH,IACD,GACF,EAGN,cAAK,SAAS,EAAC,KAAK,YAClB,YAAG,EAAE,EAAC,eAAe,EAAC,SAAS,EAAC,+BAA+B,YAAE,OAAO,GAAK,GACzE,EAGN,eAAK,SAAS,EAAE,6CAA6C,MAAM,CAAC,MAAM,EAAE,aACzE,QAAQ,IAAI,CACX,iBACE,OAAO,EAAE,YAAY,EACrB,SAAS,EAAC,wEAAwE,YAEjF,gBAAgB,GACV,CACV,EACA,SAAS,IAAI,CACZ,iBACE,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,iEACT,IAAI,KAAK,OAAO;gCACd,CAAC,CAAC,6BAA6B;gCAC/B,CAAC,CAAC,+BACN,EAAE,YAED,iBAAiB,GACX,CACV,IACG,IACF,GACF,CACP,CAAC;AACJ,CAAC,CAAC;AAGF,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE;IACvC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAOlC,IAAI,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,CAChB,KAAa,EACb,OAAe,EACf,OAAiD,MAAM,EACvD,SAAsB,EACtB,QAAqB,EACrB,EAAE;QACF,aAAa,CAAC;YACZ,MAAM,EAAE,IAAI;YACZ,KAAK;YACL,OAAO;YACP,IAAI;YACJ,SAAS;YACT,QAAQ;SACT,CAAC,CAAC;QAGH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,QAAQ,EAAE,CAAC;gBACb,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,aAAa,CAAC,CAAC,IAAI,EALine truncated
+21 -3
View File
@@ -342,7 +342,13 @@ let TourController = class TourController {
include: { user: { select: { id: true, name: true, email: true } } }, include: { user: { select: { id: true, name: true, email: true } } },
}); });
} }
const currentRole = req.user.tourParticipation?.role; let currentRole = req.user.tourParticipation?.role;
if (!currentRole) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
currentRole = participation?.role;
}
if (!currentRole || !(currentRole === 'OWNER' || currentRole === 'MANAGER')) { if (!currentRole || !(currentRole === 'OWNER' || currentRole === 'MANAGER')) {
const joinRequest = await this.prisma.joinRequest.create({ const joinRequest = await this.prisma.joinRequest.create({
data: { data: {
@@ -407,7 +413,13 @@ let TourController = class TourController {
return joinRequest; return joinRequest;
} }
async acceptJoinRequest(tourId, requestId, req) { async acceptJoinRequest(tourId, requestId, req) {
const role = req.user.tourParticipation?.role; let role = req.user.tourParticipation?.role;
if (!role) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
role = participation?.role;
}
if (!role || !(role === 'OWNER' || role === 'MANAGER')) { if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
throw new ForbiddenException('Bạn không có quyền chấp nhận yêu cầu tham gia.'); throw new ForbiddenException('Bạn không có quyền chấp nhận yêu cầu tham gia.');
} }
@@ -446,7 +458,13 @@ let TourController = class TourController {
return { success: true, message: 'Đã chấp nhận yêu cầu tham gia.' }; return { success: true, message: 'Đã chấp nhận yêu cầu tham gia.' };
} }
async rejectJoinRequest(tourId, requestId, req) { async rejectJoinRequest(tourId, requestId, req) {
const role = req.user.tourParticipation?.role; let role = req.user.tourParticipation?.role;
if (!role) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
role = participation?.role;
}
if (!role || !(role === 'OWNER' || role === 'MANAGER')) { if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
throw new ForbiddenException('Bạn không có quyền từ chối yêu cầu tham gia.'); throw new ForbiddenException('Bạn không có quyền từ chối yêu cầu tham gia.');
} }
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"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 truncated {"version":3,"file":"main.js","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"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 truncated
+1 -1
View File
@@ -21,7 +21,7 @@ let TourRoleGuard = class TourRoleGuard {
if (!user || !tourId) { if (!user || !tourId) {
throw new 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ệ.");
} }
if (path.includes('/join-requests') && request.method === 'POST' && !request.params.requestId) { if (path.includes('/join-requests') && request.method === 'POST' && (!request.params.requestId || /\/join-requests\/[^/]+\/(accept|reject)$/.test(path))) {
request.tourParticipation = null; request.tourParticipation = null;
return true; return true;
} }
+1 -1
View File
@@ -1 +1 @@
{"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;QAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;QAEzB,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,IAAI,kBAAkB,CAAC,+CAA+C,CAAC,CAAC;QAChF,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC9F,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC;YACjE,KAAK,EAAE;gBACL,aAAa,EAAE;oBACb,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,kBAAkB,CAAC,4CAA4C,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,CAAC,iBAAiB,GAAG,aAAa,CAAC;QAE1C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC;QAChC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAEjD,IACE,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,aAAa,CAAC;YACxD,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;AA/CY,aAAa;IADzB,UAAU,EAAE;qCAEiB,aAAa;GAD9B,aAAa,CA+CzB"} {"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;QAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;QAEzB,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,IAAI,kBAAkB,CAAC,+CAA+C,CAAC,CAAC;QAChF,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,0CAA0C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACzJ,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC;YACjE,KAAK,EAAE;gBACL,aAAa,EAAE;oBACb,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,IAAI,CAAC,EAAE;iBAChB;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,kBAAkB,CAAC,4CAA4C,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,CAAC,iBAAiB,GAAG,aAAa,CAAC;QAE1C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC;QAChC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAEjD,IACE,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,aAAa,CAAC;YACxD,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;AA/CY,aAAa;IADzB,UAAU,EAAE;qCAEiB,aAAa;GAD9B,aAAa,CA+CzB"}
+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","../node_modules/zustand/esm/vanilla.d.mts","../node_modules/zustand/esm/react.d.mts","../node_modules/zustand/esm/index.d.mts","../useTourStore.ts","../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/FeatureGroup.d.ts","../node_modules/react-leaflet/lib/GeoJSON.d.ts","../node_modules/react-leaflet/lib/ImageOverlay.d.ts","../node_modules/react-leaflet/lib/LayersLine 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","../node_modules/zustand/esm/vanilla.d.mts","../node_modules/zustand/esm/react.d.mts","../node_modules/zustand/esm/index.d.mts","../useTourStore.ts","../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/FeatureGroup.d.ts","../node_modules/react-leaflet/lib/GeoJSON.d.ts","../node_modules/react-leaflet/lib/ImageOverlay.d.ts","../node_modules/react-leaflet/lib/LayersLine truncated
+1
View File
@@ -0,0 +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","../node_modules/zustand/esm/vanilla.d.mts","../node_modules/zustand/esm/react.d.mts","../node_modules/zustand/esm/index.d.mts","../useTourStore.ts","../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/FeatureGroup.d.ts","../node_modules/react-leaflet/lib/GeoJSON.d.ts","../node_modules/react-leaflet/lib/ImageOverlay.d.ts","../node_modules/react-leaflet/lib/LayersLine truncated
+21 -3
View File
@@ -378,7 +378,13 @@ class TourController {
}); });
} }
const currentRole = req.user.tourParticipation?.role; let currentRole = req.user.tourParticipation?.role;
if (!currentRole) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
currentRole = participation?.role;
}
if (!currentRole || !(currentRole === 'OWNER' || currentRole === 'MANAGER')) { if (!currentRole || !(currentRole === 'OWNER' || currentRole === 'MANAGER')) {
const joinRequest = await this.prisma.joinRequest.create({ const joinRequest = await this.prisma.joinRequest.create({
data: { data: {
@@ -458,7 +464,13 @@ class TourController {
@UseGuards(JwtAuthGuard, TourRoleGuard) @UseGuards(JwtAuthGuard, TourRoleGuard)
@Post(':tourId/join-requests/:requestId/accept') @Post(':tourId/join-requests/:requestId/accept')
async acceptJoinRequest(@Param('tourId', ParseUUIDPipe) tourId: string, @Param('requestId') requestId: string, @Req() req: any) { async acceptJoinRequest(@Param('tourId', ParseUUIDPipe) tourId: string, @Param('requestId') requestId: string, @Req() req: any) {
const role = req.user.tourParticipation?.role; let role = req.user.tourParticipation?.role;
if (!role) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
role = participation?.role;
}
if (!role || !(role === 'OWNER' || role === 'MANAGER')) { if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
throw new ForbiddenException('Bạn không có quyền chấp nhận yêu cầu tham gia.'); throw new ForbiddenException('Bạn không có quyền chấp nhận yêu cầu tham gia.');
} }
@@ -504,7 +516,13 @@ class TourController {
@UseGuards(JwtAuthGuard, TourRoleGuard) @UseGuards(JwtAuthGuard, TourRoleGuard)
@Post(':tourId/join-requests/:requestId/reject') @Post(':tourId/join-requests/:requestId/reject')
async rejectJoinRequest(@Param('tourId', ParseUUIDPipe) tourId: string, @Param('requestId') requestId: string, @Req() req: any) { async rejectJoinRequest(@Param('tourId', ParseUUIDPipe) tourId: string, @Param('requestId') requestId: string, @Req() req: any) {
const role = req.user.tourParticipation?.role; let role = req.user.tourParticipation?.role;
if (!role) {
const participation = await this.prisma.tourParticipant.findUnique({
where: { tourId_userId: { tourId, userId: req.user.id } },
});
role = participation?.role;
}
if (!role || !(role === 'OWNER' || role === 'MANAGER')) { if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
throw new ForbiddenException('Bạn không có quyền từ chối yêu cầu tham gia.'); throw new ForbiddenException('Bạn không có quyền từ chối yêu cầu tham gia.');
} }
+1 -1
View File
@@ -16,7 +16,7 @@ export class TourRoleGuard implements CanActivate {
throw new 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ệ.");
} }
if (path.includes('/join-requests') && request.method === 'POST' && !request.params.requestId) { if (path.includes('/join-requests') && request.method === 'POST' && (!request.params.requestId || /\/join-requests\/[^/]+\/(accept|reject)$/.test(path))) {
request.tourParticipation = null; request.tourParticipation = null;
return true; return true;
} }