Production cleanups

This commit is contained in:
Stephanos A
2026-07-08 09:01:07 +03:00
parent 63fa6996b1
commit c7403e1a1e
21 changed files with 701 additions and 755 deletions

View File

@@ -311,41 +311,25 @@ export default function ClassesPage() {
<p className="text-xs text-muted-foreground mt-1">Per-km distance-based fare rate</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
<div>
<label className="label">Premium Fee (ETB)</label>
<input
type="number"
name="premiumMinor"
className="input"
defaultValue={editingClass?.premiumMinor ? (editingClass.premiumMinor / 100).toFixed(2) : '0.00'}
min="0"
step="0.01"
placeholder="e.g., 50.00"
/>
<p className="text-xs text-muted-foreground mt-1">Flat fee per passenger (e.g., lounge access, extra legroom)</p>
</div>
<div>
<label className="label">Insurance Fee (ETB)</label>
<input
type="number"
name="insuranceFeeMinor"
className="input"
defaultValue={editingClass?.insuranceFeeMinor ? (editingClass.insuranceFeeMinor / 100).toFixed(2) : '0.00'}
min="0"
step="0.01"
placeholder="e.g., 25.00"
/>
<p className="text-xs text-muted-foreground mt-1">Flat fee per passenger (e.g., travel insurance)</p>
</div>
<input type="hidden" name="premiumMinor" value="0" />
<div className="mt-4">
<label className="label">Insurance Fee (ETB)</label>
<input
type="number"
name="insuranceFeeMinor"
className="input"
defaultValue={editingClass?.insuranceFeeMinor ? (editingClass.insuranceFeeMinor / 100).toFixed(2) : '0.00'}
min="0"
step="0.01"
placeholder="e.g., 25.00"
/>
<p className="text-xs text-muted-foreground mt-1">Flat fee per passenger (e.g., travel insurance)</p>
</div>
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded text-sm text-blue-800 dark:text-blue-200">
<p className="font-medium mb-1">Total Fare Calculation:</p>
<p>Total = (Base Fare × Distance) + Premium + Insurance</p>
<p className="mt-2 text-xs"> Premium applies per passenger</p>
<p className="text-xs"> Insurance applies per passenger</p>
<p>Total = (Base Fare × Distance) + Insurance</p>
<p className="mt-2 text-xs"> Insurance applies per passenger</p>
</div>
</div>

View File

@@ -220,13 +220,6 @@ export default function CurrenciesPage() {
)}
</div>
<div className="card bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 text-sm text-blue-800 dark:text-blue-300 space-y-1">
<p className="font-semibold text-blue-900 dark:text-blue-200 mb-2">How it works</p>
<p> ETB is the transaction currency all fares are stored in ETB minor units (1 ETB = 100 minor)</p>
<p> DJF and USD rates are used to display prices to passengers in their preferred currency</p>
<p> Rates apply globally; changes take effect immediately on the next booking or fare quote</p>
</div>
<Modal
isOpen={showAddModal}
onClose={() => { setShowAddModal(false); setError(null); }}

View File

@@ -82,7 +82,7 @@ export default function OperationsSection() {
{/* EXCESS BAGGAGE */}
<div id="excess-baggage" className="border-t pt-8">
<h2 className="text-2xl font-bold text-slate-900 dark:text-white mb-2">📦 Luggage (Excess Baggage)</h2>
<h2 className="text-2xl font-bold text-slate-900 dark:text-white mb-2">📦 Luggage (Excess Luggage)</h2>
<p className={li}>Handle excess baggage charges at boarding passenger self-pay or agent cash collection. Access level: Agent, Supervisor, Admin.</p>
<div className="mt-3 flex flex-wrap gap-2 text-xs">
{['PENDING','PAID','CASH_COLLECTED','EXPIRED','WAIVED'].map(s => (
@@ -91,7 +91,7 @@ export default function OperationsSection() {
</div>
</div>
<div id="excess-baggage-how" className="border-t pt-8">
<h3 className="text-xl font-bold text-slate-900 dark:text-white mb-4">📦 How-To: Handle Excess Baggage</h3>
<h3 className="text-xl font-bold text-slate-900 dark:text-white mb-4">📦 How-To: Handle Excess Luggage</h3>
<div className="space-y-3">
<HowToStep number={1} title="Find Charges">
<ol className={ol}><li>Click <strong>Luggage</strong> in Operations</li><li>Search by booking reference; filter by status or date</li></ol>

View File

@@ -609,7 +609,7 @@ export default function PricingPage() {
className={`px-4 py-2 font-medium border-b-2 transition-colors ${tab === 'baggage' ? 'border-primary text-primary' : 'border-transparent text-muted-foreground'
}`}
>
Excess Baggage Rates
Excess Luggage Rates
</button>
</div>
@@ -765,24 +765,6 @@ export default function PricingPage() {
</div>
</div>
<div className="card bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
<h3 className="font-semibold text-blue-900 dark:text-blue-200 mb-3">Pricing Structure</h3>
<ul className="text-sm text-blue-800 dark:text-blue-300 space-y-2">
<li>
<strong>Segment Fares:</strong> Set fares for specific stop-to-stop segments (e.g., Addis Dire Dawa)
</li>
<li>
<strong>Schedule Fares:</strong> Set custom pricing for each schedule by seat class and passenger type
</li>
<li>
<strong>Passenger Type:</strong> ADULT (5+ years) or CHILD (&lt;5) first child travels free, subsequent children pay full fare
</li>
<li>
<strong>Nationality-based:</strong> Override fares for specific nationalities (Ethiopian, Djiboutian, Other)
</li>
</ul>
</div>
{/* Delete Confirmation */}
<ConfirmDialog
isOpen={deleteConfirm.isOpen}
@@ -1113,7 +1095,7 @@ export default function PricingPage() {
</div>
</div>
</Modal>
{/* Baggage Allowance Modal */}
{/* Luggage Allowance Modal */}
<Modal isOpen={baggageModal} onClose={() => { setBaggageModal(false); setEditingAllowance(null); setBaggageError(null); }} title={editingAllowance ? 'Edit Allowance Rule' : 'Add Allowance Rule'} size="md">
<div className="space-y-4">
{baggageError && <div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 p-3 rounded-lg text-sm text-red-800 dark:text-red-200">{baggageError}</div>}

View File

@@ -52,15 +52,28 @@ export default function SeatsPage() {
queryFn: async () => {
if (!selectedRoute) return null;
const template: any[] = await routeCoachTemplatesApi.get(selectedRoute);
if (!template?.length) return [];
const fullCoaches = await Promise.all(
template.map((entry: any) => fleetApi.getCoach(entry.coachId ?? entry.coach?.id))
);
return fullCoaches.map((coach: any, i: number) => ({
if (template?.length) {
const fullCoaches = await Promise.all(
template.map((entry: any) => fleetApi.getCoach(entry.coachId ?? entry.coach?.id))
);
return fullCoaches.map((coach: any, i: number) => ({
...coach,
coachNumber: coach.number,
positionNumber: template[i].positionNumber,
seatArrangement: coach.arrangement,
}));
}
// No template — fetch coaches from the most recent schedule for this route
const schedules: any = await schedulesApi.getAll({ routeId: selectedRoute });
const scheduleList: any[] = schedules?.items || schedules?.data || (Array.isArray(schedules) ? schedules : []);
if (!scheduleList.length) return [];
const latestSchedule = scheduleList[scheduleList.length - 1];
const seatMap: any = await seatsApi.getSeatMap(latestSchedule.id);
return (seatMap?.coaches || []).map((coach: any, i: number) => ({
...coach,
coachNumber: coach.number,
positionNumber: template[i].positionNumber,
seatArrangement: coach.arrangement,
coachNumber: coach.number ?? coach.coachNumber,
positionNumber: coach.positionNumber ?? i + 1,
seatArrangement: coach.arrangement ?? coach.seatArrangement,
}));
},
enabled: !!selectedRoute,

View File

@@ -237,16 +237,6 @@ export default function TariffRatesPage() {
</ActionButton>
</div>
{/* Tariff reference card */}
<div className="card bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
<h3 className="font-semibold text-blue-900 dark:text-blue-200 mb-2">Official Tariff Formula</h3>
<p className="text-sm text-blue-800 dark:text-blue-300 font-mono">
Fare = KM × rate × 1.02 × ExchangeRate
</p>
<p className="text-xs text-blue-700 dark:text-blue-400 mt-1">
Rate is stored as <strong>baseFareMinor = tariff_decimal × 100,000</strong> (e.g. 0.03 3000). The ×1.02 insurance coefficient is applied automatically by the fare engine.
</p>
</div>
<div className="card">
<div className="relative mb-4">

View File

@@ -884,11 +884,11 @@ export default function TicketsPage() {
})()}
</Modal>
{/* Excess Baggage Modal */}
{/* Excess Luggage Modal */}
<Modal
isOpen={excessModalOpen}
onClose={() => { setExcessModalOpen(false); setExcessTicket(null); setExcessResult(null); }}
title="Log Excess Baggage"
title="Log Excess Luggage"
size="sm"
>
{excessResult ? (

View File

@@ -444,7 +444,7 @@ export const packageInquiriesApi = {
remove: (id: string) => apiClient.delete(`/packages/inquiries/${id}`),
};
// Excess Baggage API
// Excess Luggage API
export const excessBaggageApi = {
logCharge: (data: any) => apiClient.post<any>('/agents/excess-baggage', data),
getCharge: (id: string) => apiClient.get<any>(`/agents/excess-baggage/${id}`),