mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
fix issue
This commit is contained in:
@@ -95,7 +95,13 @@ function usedWeight(schedule: TrainScheduleDetail): number {
|
||||
);
|
||||
}
|
||||
|
||||
/** Max pull weight across all locomotives on the set (0 when unknown). */
|
||||
/**
|
||||
* Pull capacity of the set = the WEAKEST locomotive's max pull weight (0 when
|
||||
* unknown). The API caps at the weakest loco, not the sum of all locos — a
|
||||
* consist can only pull as hard as its weakest engine. Note: the API also adds
|
||||
* the consist tare to the used weight when it checks this cap; tare isn't
|
||||
* available client-side, so this meter compares cargo-only load against pull.
|
||||
*/
|
||||
function pullCapacity(schedule: TrainScheduleDetail): number {
|
||||
const set = schedule.trainSet;
|
||||
if (!set) return 0;
|
||||
@@ -105,7 +111,8 @@ function pullCapacity(schedule: TrainScheduleDetail): number {
|
||||
: set.locomotive
|
||||
? [set.locomotive]
|
||||
: [];
|
||||
return locos.reduce((sum, l) => sum + (Number(l.maxPullWeightTons) || 0), 0);
|
||||
if (locos.length === 0) return 0;
|
||||
return Math.min(...locos.map((l) => Number(l.maxPullWeightTons) || 0));
|
||||
}
|
||||
|
||||
export function ScheduleWorkspacePanel({
|
||||
|
||||
@@ -19,7 +19,7 @@ export function AssignWagonDialog({ trainId }: { trainId: string }) {
|
||||
const { toast } = useToast();
|
||||
|
||||
const available = (wagons ?? []).filter(
|
||||
(w) => w.status === Freight.WagonStatus.Available || !w.trainId,
|
||||
(w) => w.status === Freight.WagonStatus.Available && !w.trainId,
|
||||
);
|
||||
|
||||
const yardLabelById = useMemo(
|
||||
|
||||
@@ -183,10 +183,22 @@ const WagonYardWorkspaceModal = ({ opened, onClose }: WagonYardWorkspaceModalPro
|
||||
return yardWagons.filter((w) => w.currentYardId === yardId && w.wagonTypeId === typeId);
|
||||
}, [yardWagons, yardId, typeId]);
|
||||
|
||||
const availableWagons = useMemo(() => matching.filter((w) => w.status === AVAILABLE), [matching]);
|
||||
const assignedWagons = useMemo(() => matching.filter((w) => w.status === ASSIGNED), [matching]);
|
||||
// Wagons coupled to a built train (trainId set) are managed through the
|
||||
// train-builder flow — they can't be bulk-flipped or transferred here, so
|
||||
// keep them out of the action pools and bucket them under "Other".
|
||||
const availableWagons = useMemo(
|
||||
() => matching.filter((w) => w.status === AVAILABLE && !w.trainId),
|
||||
[matching],
|
||||
);
|
||||
const assignedWagons = useMemo(
|
||||
() => matching.filter((w) => w.status === ASSIGNED && !w.trainId),
|
||||
[matching],
|
||||
);
|
||||
const otherWagons = useMemo(
|
||||
() => matching.filter((w) => w.status !== AVAILABLE && w.status !== ASSIGNED),
|
||||
() =>
|
||||
matching.filter(
|
||||
(w) => w.trainId != null || (w.status !== AVAILABLE && w.status !== ASSIGNED),
|
||||
),
|
||||
[matching],
|
||||
);
|
||||
const total = matching.length;
|
||||
|
||||
@@ -298,7 +298,17 @@ const FleetResourcePage = () => {
|
||||
const handleFormSubmit = async (values: Record<string, unknown>) => {
|
||||
try {
|
||||
if (editing && "id" in editing) {
|
||||
await update.mutateAsync({ slug, id: String(editing.id), data: values });
|
||||
// PATCH only the fields the user actually changed. Re-sending the whole
|
||||
// form used to re-submit status/currentYardId on every save — which,
|
||||
// for a locomotive/wagon coupled to a built train, silently diverged
|
||||
// the consist (editing a name could move the loco to another yard).
|
||||
const editingRecord = editing as unknown as Record<string, unknown>;
|
||||
const changed = Object.fromEntries(
|
||||
Object.entries(values).filter(
|
||||
([key, value]) => value !== editingRecord[key],
|
||||
),
|
||||
);
|
||||
await update.mutateAsync({ slug, id: String(editing.id), data: changed });
|
||||
toast({ title: `${config.entityLabel} updated` });
|
||||
} else {
|
||||
await create.mutateAsync({ slug, data: values });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
@@ -65,11 +65,6 @@ function MetaItem({ label, value }: { label: string; value: string }) {
|
||||
export default function InvoiceDetailPage() {
|
||||
const { id = "" } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [paymentMethod, setPaymentMethod] = useState<"TELEBIRR" | "WAAFI">(
|
||||
"TELEBIRR",
|
||||
);
|
||||
console.log(paymentMethod)
|
||||
|
||||
const {
|
||||
data: invoice,
|
||||
isLoading,
|
||||
@@ -93,11 +88,6 @@ export default function InvoiceDetailPage() {
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!invoice) return;
|
||||
setPaymentMethod(invoice.currency === "USD" ? "WAAFI" : "TELEBIRR");
|
||||
}, [invoice?.currency]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Center py={80}>
|
||||
@@ -128,7 +118,9 @@ export default function InvoiceDetailPage() {
|
||||
|
||||
const payable = isPayable(invoice.status);
|
||||
const lines = invoice.lines ?? [];
|
||||
// const amountDue = Number(invoice.balanceAmount ?? invoice.totalAmount);
|
||||
// The backend charges the outstanding balance, not the invoice total — a
|
||||
// partially-paid invoice must show the remaining amount on the pay button.
|
||||
const amountDue = Number(invoice.balanceAmount ?? invoice.totalAmount);
|
||||
|
||||
const handlePay = () => {
|
||||
setPayModalOpen(true);
|
||||
@@ -263,8 +255,7 @@ export default function InvoiceDetailPage() {
|
||||
root: { fontWeight: 600, height: 42, paddingInline: 18 },
|
||||
}}
|
||||
>
|
||||
Pay{" "}
|
||||
{formatCurrency(Number(invoice.totalAmount), invoice.currency)}
|
||||
Pay {formatCurrency(amountDue, invoice.currency)}
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
@@ -390,10 +381,7 @@ export default function InvoiceDetailPage() {
|
||||
payMutation.reset();
|
||||
}
|
||||
}}
|
||||
amountLabel={formatCurrency(
|
||||
Number(invoice.totalAmount),
|
||||
invoice.currency,
|
||||
)}
|
||||
amountLabel={formatCurrency(amountDue, invoice.currency)}
|
||||
currency={invoice.currency}
|
||||
processing={payMutation.isPending}
|
||||
error={
|
||||
|
||||
@@ -120,7 +120,9 @@ export function PayClearanceFeeButton({
|
||||
onClose={pay.close}
|
||||
amountLabel={
|
||||
pay.invoice
|
||||
? `${Number(pay.invoice.totalAmount).toLocaleString()} ${pay.invoice.currency}`
|
||||
? `${Number(
|
||||
pay.invoice.balanceAmount ?? pay.invoice.totalAmount,
|
||||
).toLocaleString()} ${pay.invoice.currency}`
|
||||
: undefined
|
||||
}
|
||||
currency={pay.invoice?.currency ?? currency}
|
||||
|
||||
Reference in New Issue
Block a user