fix: hiển thị mô tả và tên của hành trình có thêm tags
This commit is contained in:
Vendored
+2
-1
@@ -211,10 +211,11 @@ let TourController = class TourController {
|
|||||||
this.prisma = prisma;
|
this.prisma = prisma;
|
||||||
}
|
}
|
||||||
async createTour(body, req) {
|
async createTour(body, req) {
|
||||||
const { title, startDate, endDate, adultCount, childCount, childDiscount, tags } = body;
|
const { title, description, startDate, endDate, adultCount, childCount, childDiscount, tags } = body;
|
||||||
return this.prisma.tour.create({
|
return this.prisma.tour.create({
|
||||||
data: {
|
data: {
|
||||||
title,
|
title,
|
||||||
|
description,
|
||||||
startDate: startDate ? new Date(startDate) : null,
|
startDate: startDate ? new Date(startDate) : null,
|
||||||
endDate: endDate ? new Date(endDate) : null,
|
endDate: endDate ? new Date(endDate) : null,
|
||||||
tags: tags || [],
|
tags: tags || [],
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2
-1
@@ -150,10 +150,11 @@ class TourController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Post()
|
@Post()
|
||||||
async createTour(@Body() body: any, @Req() req: any) {
|
async createTour(@Body() body: any, @Req() req: any) {
|
||||||
const { title, startDate, endDate, adultCount, childCount, childDiscount, tags } = body;
|
const { title, description, startDate, endDate, adultCount, childCount, childDiscount, tags } = body;
|
||||||
return this.prisma.tour.create({
|
return this.prisma.tour.create({
|
||||||
data: {
|
data: {
|
||||||
title,
|
title,
|
||||||
|
description,
|
||||||
startDate: startDate ? new Date(startDate) : null,
|
startDate: startDate ? new Date(startDate) : null,
|
||||||
endDate: endDate ? new Date(endDate) : null,
|
endDate: endDate ? new Date(endDate) : null,
|
||||||
tags: tags || [],
|
tags: tags || [],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useTourStore } from '@/store/useTourStore';
|
|||||||
|
|
||||||
export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolean, onClose: () => void, onSuccess: (tour: any) => void }) => {
|
export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolean, onClose: () => void, onSuccess: (tour: any) => void }) => {
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
|
const [description, setDescription] = useState('');
|
||||||
const [startDate, setStartDate] = useState('');
|
const [startDate, setStartDate] = useState('');
|
||||||
const [endDate, setEndDate] = useState('');
|
const [endDate, setEndDate] = useState('');
|
||||||
const [adultCount, setAdultCount] = useState(2);
|
const [adultCount, setAdultCount] = useState(2);
|
||||||
@@ -75,6 +76,7 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
|
|||||||
const memberIds = members.map((m) => m.id);
|
const memberIds = members.map((m) => m.id);
|
||||||
const tour = await createTour({
|
const tour = await createTour({
|
||||||
title,
|
title,
|
||||||
|
description,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
memberIds,
|
memberIds,
|
||||||
@@ -113,6 +115,56 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-gray-700 mb-2 flex items-center gap-2">
|
||||||
|
<TagIcon className="w-4 h-4" /> Phân loại Tour
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{availableTags.map(tag => (
|
||||||
|
<button
|
||||||
|
key={tag}
|
||||||
|
type="button"
|
||||||
|
onClick={() => toggleTag(tag)}
|
||||||
|
className={`px-3 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
||||||
|
selectedTags.includes(tag)
|
||||||
|
? 'bg-blue-600 text-white border-blue-600 shadow-md shadow-blue-100'
|
||||||
|
: 'bg-white text-gray-500 border-gray-200 hover:border-blue-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 mt-3">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={customTag}
|
||||||
|
onChange={(e) => setCustomTag(e.target.value)}
|
||||||
|
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), addCustomTag())}
|
||||||
|
placeholder="Thêm nhãn tùy chỉnh..."
|
||||||
|
className="flex-1 px-3 py-2 bg-gray-50 border border-gray-100 rounded-xl text-xs outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addCustomTag}
|
||||||
|
className="px-4 py-2 bg-blue-50 text-blue-600 rounded-xl text-xs font-bold hover:bg-blue-100 transition-all"
|
||||||
|
>
|
||||||
|
Thêm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-gray-700 mb-1">Mô tả chuyến đi</label>
|
||||||
|
<textarea
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 resize-none text-sm"
|
||||||
|
rows={3}
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
placeholder="Viết vài dòng giới thiệu về hành trình..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-bold text-gray-700 mb-1">Bắt đầu</label>
|
<label className="block text-sm font-bold text-gray-700 mb-1">Bắt đầu</label>
|
||||||
@@ -159,45 +211,6 @@ export const CreateTourModal = ({ isOpen, onClose, onSuccess }: { isOpen: boolea
|
|||||||
<p className="text-[10px] text-blue-400 italic font-medium">* Dùng để tính toán đơn giá bình quân trong báo cáo chi phí.</p>
|
<p className="text-[10px] text-blue-400 italic font-medium">* Dùng để tính toán đơn giá bình quân trong báo cáo chi phí.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-bold text-gray-700 mb-2 flex items-center gap-2">
|
|
||||||
<TagIcon className="w-4 h-4" /> Phân loại Tour
|
|
||||||
</label>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{availableTags.map(tag => (
|
|
||||||
<button
|
|
||||||
key={tag}
|
|
||||||
type="button"
|
|
||||||
onClick={() => toggleTag(tag)}
|
|
||||||
className={`px-3 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
|
||||||
selectedTags.includes(tag)
|
|
||||||
? 'bg-blue-600 text-white border-blue-600 shadow-md shadow-blue-100'
|
|
||||||
: 'bg-white text-gray-500 border-gray-200 hover:border-blue-300'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 mt-3">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={customTag}
|
|
||||||
onChange={(e) => setCustomTag(e.target.value)}
|
|
||||||
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), addCustomTag())}
|
|
||||||
placeholder="Thêm nhãn tùy chỉnh..."
|
|
||||||
className="flex-1 px-3 py-2 bg-gray-50 border border-gray-100 rounded-xl text-xs outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={addCustomTag}
|
|
||||||
className="px-4 py-2 bg-blue-50 text-blue-600 rounded-xl text-xs font-bold hover:bg-blue-100 transition-all"
|
|
||||||
>
|
|
||||||
Thêm
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-bold text-gray-700 mb-2">Thành viên tham gia</label>
|
<label className="block text-sm font-bold text-gray-700 mb-2">Thành viên tham gia</label>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
|
|||||||
@@ -526,8 +526,9 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac
|
|||||||
<div className="max-w-2xl mx-auto space-y-4">
|
<div className="max-w-2xl mx-auto space-y-4">
|
||||||
<h2 className="text-3xl font-black tracking-tight drop-shadow-md">{tourInfo.title}</h2>
|
<h2 className="text-3xl font-black tracking-tight drop-shadow-md">{tourInfo.title}</h2>
|
||||||
|
|
||||||
|
{/* Nhãn hiển thị ngay dưới Tiêu đề */}
|
||||||
{currentTour?.tags && currentTour.tags.length > 0 && (
|
{currentTour?.tags && currentTour.tags.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2 pt-1">
|
||||||
{currentTour.tags.map((tag: string) => (
|
{currentTour.tags.map((tag: string) => (
|
||||||
<span key={tag} className="px-2.5 py-1 bg-white/20 backdrop-blur-md border border-white/30 rounded-lg text-[10px] font-black uppercase tracking-wider">
|
<span key={tag} className="px-2.5 py-1 bg-white/20 backdrop-blur-md border border-white/30 rounded-lg text-[10px] font-black uppercase tracking-wider">
|
||||||
{tag}
|
{tag}
|
||||||
@@ -536,6 +537,7 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Mô tả hiển thị dưới Nhãn */}
|
||||||
{currentTour?.description && (
|
{currentTour?.description && (
|
||||||
<p className="text-sm md:text-base text-white/90 max-w-xl line-clamp-3 md:line-clamp-none bg-black/20 backdrop-blur-sm p-4 rounded-2xl border border-white/10 italic leading-relaxed">
|
<p className="text-sm md:text-base text-white/90 max-w-xl line-clamp-3 md:line-clamp-none bg-black/20 backdrop-blur-sm p-4 rounded-2xl border border-white/10 italic leading-relaxed">
|
||||||
<Quote className="w-4 h-4 inline-block mr-2 opacity-50" />
|
<Quote className="w-4 h-4 inline-block mr-2 opacity-50" />
|
||||||
|
|||||||
Reference in New Issue
Block a user