- Implemented method in to allow customers to request an extension for expired contracts.
- Added component in for users to initiate extension requests.
- Updated to include logic for handling extension requests for expired contracts.
- Enhanced to display extension request options and status.
- Created migration to add and columns to the contracts table.
- Added unit tests for contract extension request and handling in .
- Defined DTOs for request and extension in .
- Updated types in to include new fields related to contract extensions.
- Added TrainCrewAssignment module with controller and service for managing crew assignments.
- Integrated TrainCrewAssignmentService into TrainSchedulingService to ensure crew readiness before train dispatch.
- Updated TrainScheduling module to include TrainCrewModule for dependency injection.
- Introduced new permissions for assigning train crew in freight permissions registry.
- Enhanced front-end ScheduleCrewPage to allow assignment of crew members to train schedules, including validation and UI for adding/removing drivers and support crew.
- Created trainCrewAssignment.service to handle API interactions for crew assignments.
- Introduced ScheduleCrewPage for assigning train crew to schedules.
- Updated routing in App.tsx to include crew assignment path.
- Enhanced TrainScheduleV2ListPage with a button to navigate to crew assignment.
- Added new chart components and styles for improved wagon performance reporting.
- Refactored existing styles and components for better visual feedback and usability.
Replace the icon-and-cards landing with a photographic hero that
alternates
between the Furi station and freight train photos, a single headline,
and two
compact glass cards that pin the background on hover. Below it: the
corridor
rail, one showcase mockup per portal (passenger trip search, freight
live
tracking), the existing stats and footer.
- Add the official EDR logo as transparent green/white PNGs (lockup and
mark),
used in the header, footer and favicon
- Switch to Plus Jakarta Sans and a palette keyed to the logo green
- Split the page into components under src/components
- Header links: Passenger, Freight, Publications
- Added DTOs for creating, querying, and updating train crew members.
- Created entity for train crew members with relevant fields and enums for role, nationality, and status.
- Developed service for handling CRUD operations and ensuring no duplicate crew members.
- Implemented controller to manage API endpoints for train crew operations.
- Integrated permissions for viewing, creating, updating, and deleting train crew members.
- Added frontend components for displaying, adding, editing, and deleting train crew members.
- Established API service for interacting with the train crew backend.
- Updated routing and sidebar to include train crew management section.
edrsc.com had no entry point — a visitor could not tell which of the two
apps they wanted. This adds @edr/landing: one static page whose job is to
make that choice obvious, with the two doors above the fold and the rest
kept to supporting context.
Fills in apps/edr-landing/, which until now held an orphaned next-env.d.ts
from an abandoned Next 15 start and was built by nothing.
- Next.js 14 static export. Versions mirror the passenger portal exactly,
so they resolve to what the lockfile already had and add 6 packages.
Depends on no workspace package — @edr/ui-common's Tailwind 4 tokens do
not fit this Tailwind 3 setup.
- Destinations come from NEXT_PUBLIC_PASSENGER_URL / NEXT_PUBLIC_FREIGHT_URL
as origins, with the entry path appended in src/lib/apps.ts. Freight links
to /portal rather than /, which is that app's own marketing landing.
- Design lifted from EDRFreightLandingPage so the two public surfaces read
as one railway. Figures (752 km, ~16 h) are the ones freight already
asserts publicly.
- Fonts load as a stylesheet, not next/font: next/font fetches from
fonts.googleapis.com during `next build` and failed the build outright on
a machine without network. Verified deterministic over repeated builds.
- CountUp renders its final value server-side and the reveal animation's
hidden state is scoped behind html.js, so the page is complete with
JavaScript off instead of blank.
turbo.json: a static export's artifact is out/, which the build task did not
list — a cache hit would have restored a build with the artifact missing.
Also gitignored.
Dockerfile.web: passes the two NEXT_PUBLIC_* build args through (a static
export inlines them at build time, so runtime env does nothing), and exempts
this app from the VITE_* required-check that guards the freight Vite apps and
would otherwise fail its build.
Claude-Session: https://claude.ai/code/session_011ZMMHwK4Ham1Dt19FVZQj3
dev independently added 3850000000000-Publications.ts. Renumbered the
DJF branch's own 3850000000000-ExchangeSettingsPerCurrency.ts to the
next free slot (3880000000000, above this branch's other new
migrations) per the project's unique-timestamp rule. Unpushed and
un-shared, so a rename is safe here — updated the class name/name
property to match, and the already-applied local dev DB's recorded
migration row.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
Currency dropdowns/pickers (AdditionalPaymentsTab, ClearanceChargesTab,
PhasedClearanceActionPanel, AdviseDutyCard, ContractRequestsPage,
ruleEngine/resources, WarehouseRulesPage, VehicleDetailPage,
FeePreviewModal) offer DJF alongside ETB/USD; GlCreateBookingForm's
currency selector gets allowDjf next to allowUsd. Narrow 'ETB'|'USD'
type unions widened to include 'DJF' across the warehouse
billingCurrency plumbing (useWarehouses, warehouse.service, api.ts)
and the customer/invoice types.
Ad-hoc money() formatters (BookingTrucksPanel, AccrualDashboard,
ImportTrucksPage, EmptyReturnRequestsPage) and formatMoney call sites
that hardcoded 2 decimals (wagon-cancellation cards, BookingRequestDetailPage,
WagonCancellationsPage, PaymentsPage, WarehouseInvoicesPage) now use
currencyDecimals() from @edr/ui-common so DJF renders with 0 decimals
instead of forced cents. The 3 duplicate overview formatCurrency/
formatAmount helpers (typed 'ETB'|'USD') widen to accept any currency.
Two correctness fixes: OverviewRecentBookingsTable's currency==='USD'
? 'USD' : 'ETB' was mislabeling every non-USD currency as ETB; and
WarehouseInvoicesPage's gateway-method default now routes any
non-ETB currency (not just USD) to WAAFI, so DJF invoices get a
working default instead of TELEBIRR (ETB-only).
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
exchangeSettings.service/hook/card follow the backend's new shape: a
single ExchangeSettings object becomes a list, get() becomes list(),
and setFallbackRate(rate) becomes setFallbackRate(currency, rate).
ExchangeRateSettingsCard now renders one row per currency instead of
one hardcoded USD->ETB form.
manualPaymentSettings.service/hook/card add djfEnabled alongside
etb/usdEnabled, matching the backend's new column.
InvoicesPage's collected-summary KPI strip adds a DJF tile and folds
its ETB-equivalent into the existing 'Total collected' conversion,
reading each currency's rate from the now-list-shaped settings query.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
lib/currency.ts re-exports the shared @edr/ui-common formatter (was a
local implementation always forcing 2 decimals, wrong for DJF).
Currency pickers (new-booking-form, new-contract-form, new-shipment
Currency selector and schemas) offer DJF wherever USD is offered.
The bigger piece: offline-payment.ts's isUsdCurrency/isUsdOfflineBooking
assumed exactly two payment rails (ETB online, USD offline) and picked
one. DJF supports BOTH, so it's replaced with independent
canPayOnline/canPayOffline predicates, updated across the 5 call sites
that gated the Pay button vs. the bank-transfer badge. PaymentMethodModal's
WAAFI/CAC Bank entries (Djibouti gateways mislabeled USD-only) now list
DJF, and a currency that matches no provider returns no providers
instead of silently offering all of them (was returning every provider,
including the ETB-only one, on any unmatched currency).
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
New lib/currency.ts is the single source of truth for currency
display across both freight web apps: symbol, name and decimals per
code (DJF is zero-decimal by convention, unlike ETB/USD). Portal's own
currency lib and backoffice's ad-hoc formatters are rewired onto this
in the next two commits instead of each guessing decimals.
CurrencySelector gets an allowDjf prop mirroring the existing allowUsd
gate, so import-shipment currency pickers can offer DJF the same way
they offer USD.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
The billing KPI queries fan out per currency with a FILTER (WHERE
payment.currency = '...') per column rather than a generic GROUP BY,
so DJF needs an explicit third variant at each of the 7 query sites
(revenueMtd*, amount*, revenue* pairs) plus the matching DTO fields.
The already-generic queries (getBookingsByCurrency,
getRevenueByCurrency, company-dashboard's sumPaidSpendByCurrency)
needed no change — they GROUP BY currency and pick up DJF on their own.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
Adds DJF to freight.payments_currency_enum (migration 3860000000000,
alone in its own migration per Postgres's ADD VALUE-in-a-transaction
restriction) and to manual_payment_settings (djf_enabled column,
migration 3870000000000, default true).
Replaces the binary ETB/USD assumptions that would have silently
mispriced or discarded a DJF booking:
- booking-pricing / contract-pricing: usdToEtb scalar -> a rate table
keyed by source currency (ExchangeService.getRateTable), so a
contract-frozen rate converts into whatever currency the booking is
paid in instead of being dropped when neither leg is ETB or USD.
- warehouse-fee / booking-wagon-cancellation: normalizeCurrency no
longer coerces anything non-ETB to USD.
- additional-charge: convertAmount no longer bails out for a currency
that isn't literally ETB or USD.
- manual-payment-settings: isEnabled/enabledCurrencies cover DJF.
Widens the three @IsIn(['ETB','USD']) DTO validators, and adds DJF to
the export/report currency filter option lists.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
exchange_settings was a single row holding the USD->ETB fallback only.
Restructured to one row per foreign currency (adds a currency column,
migration 3850000000000) so DJF gets its own fallback rate, source and
sync timestamp instead of a parallel column. Service/controller/DTO
follow: get/loadFallbackRate/saveFallbackRate/setManualRate all take a
currency now, GET /exchange-settings returns the list, and
PATCH /exchange-settings/:currency sets one. Per-currency manual-rate
ceiling (USD ~10,000, DJF ~100) replaces the old fixed bound.
Adds a spec exercising the multi-currency CBE parse and the USD<->DJF
pivot against a fixture payload.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
CbeExchangeProvider now parses every currency CBE quotes (USD, DJF, ...)
from the single existing daily-rates fetch instead of hardcoding USD
only, and ExchangeService.getRate gains a pivot step so a pair neither
quoted directly nor as its inverse (e.g. USD->DJF) is derived by
triangulating through the provider's base currency (ETB). Fallback
rates and the load/save callbacks become per-currency instead of a
single USD->ETB scalar. ExchangeService.getRateTable resolves a whole
currency->target rate table in one call for pricing loops.
No behavior change for existing USD/ETB callers.
Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
- Implemented for Excel download of wagon performance report sections, including column width adjustment and timestamped filenames.
- Created to compute derived wagon performance figures based on movements and status logs, ensuring consistency with API data.
- Enhanced with new fields for tracking last movement and statistics window days for improved reporting capabilities.