Boarding, payment methods, journey direction on seat hold, and more updates

This commit is contained in:
Stephanos A
2026-06-29 08:44:38 +03:00
parent 81ae99cee3
commit c6e56d1c4f
65 changed files with 6437 additions and 1425 deletions

View File

@@ -0,0 +1,178 @@
import { apiClient } from '@/lib/api-client';
export interface FareConfiguration {
id: string;
name: string;
description?: string;
effective_date: string;
expiry_date?: string;
is_active: boolean;
is_default: boolean;
created_by?: string;
approved_by?: string;
approved_at?: string;
created_at: string;
updated_at: string;
rate_rules_count: number;
components_count: number;
age_rules_count: number;
rateRules?: any[];
components?: any[];
ageRules?: any[];
}
export interface CreateFareConfigurationRequest {
name: string;
description?: string;
effectiveDate: string;
expiryDate?: string;
rateRules: RateRule[];
components: FareComponent[];
ageRules: AgeRule[];
isDefault?: boolean;
}
export interface RateRule {
nationalityType: 'LOCAL' | 'INTERNATIONAL';
coachType: 'REGULAR_SEAT' | 'ECONOMY_BED' | 'VIP_BED';
bedPosition?: 'UPPER' | 'MIDDLE' | 'LOWER';
ratePerKmMinor: number;
isActive?: boolean;
}
export interface FareComponent {
componentType: 'INSURANCE' | 'PREMIUM' | 'SERVICE_CHARGE' | 'TAX' | 'DEMAND';
componentName: string;
calculationMethod: 'MULTIPLIER' | 'PERCENTAGE' | 'FIXED_AMOUNT';
valueMinor?: number;
percentageValue?: number;
appliesTo: 'BASE_FARE' | 'SUBTOTAL' | 'TOTAL';
applyOrder: number;
isActive?: boolean;
}
export interface AgeRule {
ruleName: string;
minAge: number;
maxAge?: number;
pricingType: 'FREE' | 'FULL_FARE' | 'DISCOUNTED';
discountPercentage?: number;
maxFreePassengers?: number;
appliesToComponents?: boolean;
isActive?: boolean;
}
export interface TestScenario {
distanceKm: number;
nationality: string;
coachType: 'REGULAR_SEAT' | 'ECONOMY_BED' | 'VIP_BED';
bedPosition?: 'UPPER' | 'MIDDLE' | 'LOWER';
adultCount: number;
childCount?: number;
promoCode?: string;
loyaltyPoints?: number;
}
export interface CalculationResult {
baseFareMinor: number;
componentsTotal: number;
totalBeforeDiscounts: number;
discountsTotal: number;
finalTotalMinor: number;
breakdown: Array<{
step: string;
description: string;
amount: number;
runningTotal: number;
}>;
currency: string;
calculationTimestamp: string;
}
export interface SystemStatus {
configurableFaresEnabled: boolean;
rolloutPercentage: number;
totalConfigurations: number;
activeConfiguration: string | null;
activeConfigurationName: string | null;
systemReady: boolean;
}
export const configurableFareApi = {
// Configuration Management
getAllConfigurations: (): Promise<FareConfiguration[]> =>
apiClient.get('/admin/fare-configurations'),
getConfigurationById: (id: string): Promise<FareConfiguration> =>
apiClient.get(`/admin/fare-configurations/${id}`),
createConfiguration: (data: CreateFareConfigurationRequest): Promise<FareConfiguration> =>
apiClient.post('/admin/fare-configurations', data),
updateConfiguration: (id: string, data: Partial<CreateFareConfigurationRequest>): Promise<FareConfiguration> =>
apiClient.put(`/admin/fare-configurations/${id}`, data),
activateConfiguration: (id: string): Promise<{ success: boolean; message: string }> =>
apiClient.post(`/admin/fare-configurations/${id}/activate`),
deleteConfiguration: (id: string): Promise<{ success: boolean; message: string }> =>
apiClient.delete(`/admin/fare-configurations/${id}`),
testConfiguration: (id: string, scenario: TestScenario): Promise<CalculationResult> =>
apiClient.post(`/admin/fare-configurations/${id}/test`, scenario),
getAuditTrail: (id: string): Promise<any[]> =>
apiClient.get(`/admin/fare-configurations/${id}/audit`),
// Migration & Setup
migrateLegacySystem: (dryRun: boolean = false): Promise<{
scheduleFareRules: number;
segmentFareRules: number;
configurationsCreated: number;
dryRun: boolean;
}> =>
apiClient.post('/admin/fare-migration/migrate-legacy', { dryRun }),
createNewFormulaConfiguration: (data: {
name: string;
description?: string;
activateImmediately?: boolean;
}): Promise<FareConfiguration> =>
apiClient.post('/admin/fare-migration/create-new-formula', data),
completeSetup: (options: {
activateNewFormula?: boolean;
enableFeature?: boolean;
} = {}): Promise<{
migration: any;
newConfiguration: FareConfiguration;
featureEnabled: boolean;
message: string;
}> =>
apiClient.post('/admin/fare-migration/complete-setup', options),
getSystemStatus: (): Promise<SystemStatus> =>
apiClient.get('/admin/fare-migration/status'),
// System Control
getFeatureStatus: (featureName: string = 'USE_CONFIGURABLE_FARES'): Promise<{
enabled: boolean;
config: Record<string, any>;
}> =>
apiClient.get(`/admin/fare-configurations/system/feature-status?feature=${featureName}`),
toggleFeature: (data: {
featureName: string;
enabled: boolean;
config?: Record<string, any>;
}): Promise<{ success: boolean; message: string }> =>
apiClient.post('/admin/fare-configurations/system/toggle-feature', data),
enableConfigurableFares: (rolloutPercentage: number = 100): Promise<{ success: boolean; message: string }> =>
apiClient.post('/admin/fare-configurations/system/enable-configurable-fares', { rolloutPercentage }),
disableConfigurableFares: (): Promise<{ success: boolean; message: string }> =>
apiClient.post('/admin/fare-configurations/system/disable-configurable-fares'),
};
export default configurableFareApi;

View File

@@ -113,6 +113,7 @@ export const fleetApi = {
createCoach: (data: any) => apiClient.post<any>('/fleet/coaches', data),
updateCoach: (id: string, data: any) => apiClient.patch<any>(`/fleet/coaches/${id}`, data),
deleteCoach: (id: string) => apiClient.delete(`/fleet/coaches/${id}`),
generateSeatMap: (data: any) => apiClient.post<any>('/fleet/seatmap/generate', data),
};
// Schedules API
@@ -165,6 +166,13 @@ export const paymentsApi = {
getById: (id: string) => apiClient.get<any>(`/payments/${id}`),
refund: (id: string, data: any) => apiClient.post<any>(`/payments/${id}/refund`, data),
getProviders: () => apiClient.get<any[]>('/payments/providers'),
getMethods: async () => {
const response = await apiClient.get('/payments/methods');
return (response as any)?.data || response || [];
},
addMethod: (data: any) => apiClient.post('/payments/methods', data),
updateMethod: (id: string, data: any) => apiClient.patch(`/payments/methods/${id}`, data),
deleteMethod: (id: string) => apiClient.delete(`/payments/methods/${id}`),
};
// Tickets API
@@ -182,6 +190,7 @@ export const ticketsApi = {
},
getById: (id: string) => apiClient.get<any>(`/tickets/${id}`),
validate: (ticketId: string, data: any) => apiClient.post<any>(`/tickets/${ticketId}/validate`, data),
scanAndBoard: (qrCodeOrRef: string, data: any) => apiClient.post<any>(`/tickets/scan-board/${encodeURIComponent(qrCodeOrRef)}`, data),
regenerate: (ticketId: string) => apiClient.post<any>(`/tickets/${ticketId}/regenerate`),
};
@@ -371,7 +380,7 @@ export const reportsApi = {
const query = reportType ? `?type=${reportType}` : '';
const response = await apiClient.get<any>(`/reports${query}`);
if (Array.isArray(response)) return { items: response };
return response?.data ? (Array.isArray(response.data) ? { items: response.data } : response) : { items: [] };
return (response as any)?.data ? (Array.isArray((response as any).data) ? { items: (response as any).data } : response) : { items: [] };
},
};
@@ -383,7 +392,7 @@ export const packagesApi = {
) as Record<string, string>;
const query = new URLSearchParams(cleanParams).toString();
const response = await apiClient.get<any>(`/packages/all${query ? `?${query}` : ''}`);
if (response?.data) return Array.isArray(response.data) ? { items: response.data } : response;
if ((response as any)?.data) return Array.isArray((response as any).data) ? { items: (response as any).data } : response;
return Array.isArray(response) ? { items: response } : response;
},
getById: (id: string) => apiClient.get<any>(`/packages/${id}`),
@@ -405,7 +414,7 @@ export const packageInquiriesApi = {
) as Record<string, string>;
const query = new URLSearchParams(cleanParams).toString();
const response = await apiClient.get<any>(`/packages/inquiries${query ? `?${query}` : ''}`);
if (response?.data) return Array.isArray(response.data) ? { items: response.data } : response;
if ((response as any)?.data) return Array.isArray((response as any).data) ? { items: (response as any).data } : response;
return Array.isArray(response) ? { items: response } : response;
},
create: (data: any) => apiClient.post<any>('/packages/inquiries', data),
@@ -423,11 +432,12 @@ export const excessBaggageApi = {
) as Record<string, string>;
const query = new URLSearchParams(cleanParams).toString();
const response = await apiClient.get<any>(`/agents/excess-baggage${query ? `?${query}` : ''}`);
if (response?.data) return Array.isArray(response.data) ? { items: response.data } : response;
if ((response as any)?.data) return Array.isArray((response as any).data) ? { items: (response as any).data } : response;
return Array.isArray(response) ? { items: response } : response;
},
resendLink: (id: string) => apiClient.post<any>(`/agents/excess-baggage/${id}/resend`, {}),
waive: (id: string, data: any) => apiClient.patch<any>(`/agents/excess-baggage/${id}/waive`, data),
delete: (id: string) => apiClient.delete(`/agents/excess-baggage/${id}`),
};
// System Config API