Sửa lỗi hiển thị bảng liệt kê chi phí

This commit is contained in:
2026-06-16 10:28:33 +07:00
parent baa83aad8a
commit 3e1a5db1a6
5 changed files with 79 additions and 40 deletions
+12
View File
@@ -176,6 +176,12 @@ let PublicTourController = class PublicTourController {
legs: {
orderBy: { sequence: 'asc' },
include: {
expenses: {
include: {
location: { select: { name: true, plannedStart: true } },
paidBy: { select: { name: true } }
}
},
locations: {
orderBy: { plannedStart: 'asc' },
include: { _count: { select: { comments: true } } }
@@ -432,6 +438,12 @@ let TourController = class TourController {
legs: {
orderBy: { sequence: 'asc' },
include: {
expenses: {
include: {
location: { select: { name: true, plannedStart: true } },
paidBy: { select: { name: true } }
}
},
locations: {
orderBy: { plannedStart: 'asc' },
include: { _count: { select: { comments: true } } }
+1 -1
View File
File diff suppressed because one or more lines are too long
+12
View File
@@ -123,6 +123,12 @@ class PublicTourController {
legs: {
orderBy: { sequence: 'asc' },
include: {
expenses: {
include: {
location: { select: { name: true, plannedStart: true } },
paidBy: { select: { name: true } }
}
},
locations: {
orderBy: { plannedStart: 'asc' },
include: { _count: { select: { comments: true } } }
@@ -427,6 +433,12 @@ class TourController {
legs: {
orderBy: { sequence: 'asc' },
include: {
expenses: {
include: {
location: { select: { name: true, plannedStart: true } },
paidBy: { select: { name: true } }
}
},
locations: {
orderBy: { plannedStart: 'asc' },
include: { _count: { select: { comments: true } } }
+8 -3
View File
@@ -96,7 +96,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
type: editingLocation.type || 'VISIT',
legId: editingLocation.legId || '',
note: editingLocation.note || '',
expenseAmount: expense?.amount?.toString() || '',
expenseAmount: expense?.amount ? Number(expense.amount).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".") : '',
expenseCategory: expense?.category || 'OTHER',
expenseDescription: expense?.description || '',
expenseNote: expense?.note || '',
@@ -155,6 +155,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
try {
const payload: any = {
...formData,
expenseAmount: formData.expenseAmount.replace(/\./g, ''), // Loại bỏ dấu chấm trước khi gửi
legId: currentLegId,
latitude: parseFloat(formData.latitude as any),
longitude: parseFloat(formData.longitude as any),
@@ -220,9 +221,13 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Số tiền (VNĐ)</label>
<input type="number" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
<input type="text" className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
placeholder="0"
value={formData.expenseAmount} onChange={e => setFormData({...formData, expenseAmount: e.target.value})} />
value={formData.expenseAmount} onChange={e => {
const rawValue = e.target.value.replace(/\D/g, ""); // Chỉ lấy số
const formattedValue = rawValue.replace(/\B(?=(\d{3})+(?!\d))/g, "."); // Thêm dấu chấm
setFormData({...formData, expenseAmount: formattedValue});
}} />
</div>
<div>
<label className="block text-xs font-bold text-gray-600 mb-1">Loại dịch vụ</label>
+46 -36
View File
@@ -56,16 +56,24 @@ export const ExpenseManager = () => {
const avgPerLeg = legs.length > 0 ? totalAmount / legs.length : 0;
// Danh sách phẳng tất cả chi phí để hiển thị bảng
const flatExpenses = (legs || []).flatMap(leg =>
(leg.expenses || []).map((exp: any) => ({
const flatExpenses: any[] = [];
[...(legs || [])].sort((a, b) => a.sequence - b.sequence).forEach((leg, legIdx) => {
const legExpenses = (leg.expenses || []).map((exp: any) => ({
...exp,
legSequence: leg.sequence,
// Lấy ngày của Location nếu có, nếu không lấy ngày của Leg
legDisplayIndex: legIdx + 1, // Số thứ tự chặng liên tục (1, 2, 3...)
date: exp.location?.plannedStart || leg.startDate || null
}))
).sort((a, b) => {
if (!a.date || !b.date) return 0;
return new Date(a.date).getTime() - new Date(b.date).getTime();
})).sort((a: any, b: any) => {
if (!a.date || !b.date) return 0;
return new Date(a.date).getTime() - new Date(b.date).getTime();
});
legExpenses.forEach((exp: any, idx: number) => {
flatExpenses.push({
...exp,
isFirstInLeg: idx === 0
});
});
});
const childRateFactor = 1 - (Number(discount) / 100);
@@ -126,10 +134,10 @@ export const ExpenseManager = () => {
// Cấu hình bảng dữ liệu
const tableColumn = ["STT", "Ngày giờ", "Chặng", "Dịch vụ", "Số tiền (VNĐ)", "Người chi"];
const tableRows = stats.list.map((exp: any, index: number) => [
index + 1,
exp.date ? format(new Date(exp.date), 'dd/MM HH:mm') : '--/--',
`Chặng ${exp.legSequence}`,
const tableRows = stats.list.map((exp: any) => [
exp.isFirstInLeg ? exp.legSequence : '',
exp.date ? format(new Date(exp.date), 'd/M - p') : '--/--',
exp.isFirstInLeg ? `Chặng ${exp.legSequence}` : '',
exp.description || 'Không có mô tả',
Number(exp.amount).toLocaleString(),
exp.paidBy?.name || 'Chưa rõ'
@@ -305,22 +313,35 @@ export const ExpenseManager = () => {
</thead>
<tbody className="divide-y divide-gray-50">
{stats.list.length > 0 ? (
stats.list.map((exp: any, index: number) => (
<tr key={exp.id} className="hover:bg-blue-50/20 transition-colors text-sm">
<td className="px-4 py-4 text-center font-bold text-gray-400">{index + 1}</td>
stats.list.map((exp: any) => (
<tr key={exp.id} className={`hover:bg-blue-50/20 transition-colors text-sm ${!exp.isFirstInLeg ? 'bg-gray-50/10' : ''}`}>
<td className="px-4 py-4 text-center font-black text-gray-400">
{exp.isFirstInLeg ? exp.legDisplayIndex : ''}
</td>
<td className="px-4 py-4">
<div className="flex items-center gap-1.5 text-gray-600 font-medium">
<Calendar className="w-3.5 h-3.5 opacity-40" />
{exp.date ? format(new Date(exp.date), 'dd/MM HH:mm') : '--/--'}
{exp.date ? format(new Date(exp.date), 'd/M - p') : '--/--'}
</div>
</td>
<td className="px-4 py-4 whitespace-nowrap">
<span className="px-2 py-1 bg-blue-50 text-blue-600 rounded-lg font-bold text-[10px]">
Chặng {exp.legSequence}
</span>
{exp.isFirstInLeg ? (
<span className="px-2 py-1 bg-blue-50 text-blue-600 rounded-lg font-bold text-[10px]">
Chặng {exp.legSequence}
</span>
) : (
<div className="ml-6 border-l-2 border-blue-100/50 h-4" />
)}
</td>
<td className="px-4 py-4 font-semibold text-gray-800">
{exp.description || 'Không có mô tả'}
{exp.location?.name ? (
<div className="flex flex-col">
<span className="text-gray-900">{exp.description || 'Chi phí dịch vụ'}</span>
<span className="text-[10px] text-blue-500 font-bold uppercase tracking-tighter">@{exp.location.name}</span>
</div>
) : (
exp.description || 'Không có mô tả'
)}
</td>
<td className="px-4 py-4 text-right font-black text-blue-600">
{Number(exp.amount).toLocaleString()}đ
@@ -337,26 +358,15 @@ export const ExpenseManager = () => {
</tr>
))
) : (
// Khung bảng mẫu khi chưa có dữ liệu (Placeholder)
[1, 2, 3].map((i) => (
<tr key={`sample-${i}`} className="opacity-30 grayscale pointer-events-none select-none text-sm">
<td className="px-4 py-4 text-center font-bold text-gray-300">{i}</td>
<td className="px-4 py-4">
<div className="flex items-center gap-1.5 text-gray-300 font-medium">
<Calendar className="w-3.5 h-3.5 opacity-20" /> --/-- --:--
</div>
</td>
<td className="px-4 py-4 whitespace-nowrap">
<span className="px-2 py-1 bg-gray-100 text-gray-400 rounded-lg font-bold text-[10px]">Chặng -</span>
</td>
<td className="px-4 py-4 text-gray-300 italic"> dụ: tham quan, Tiền ăn trưa...</td>
<td className="px-4 py-4 text-right font-black text-gray-300">0đ</td>
<td className="px-4 py-4">
<div className="flex items-center gap-1.5 text-gray-300">
<User className="w-3.5 h-3.5 opacity-20" /> Chưa dữ liệu
</div>
</td>
<td className="px-4 py-4 text-xs text-gray-200 italic">-</td>
<td className="px-4 py-4 text-gray-300">--/-- --:--</td>
<td className="px-4 py-4 text-gray-300">Chặng -</td>
<td className="px-4 py-4 text-gray-300 italic">Chưa dữ liệu...</td>
<td className="px-4 py-4 text-right text-gray-300">0đ</td>
<td className="px-4 py-4 text-gray-300">Chưa </td>
<td className="px-4 py-4 text-gray-200">-</td>
</tr>
))
)}