Appearance
Expense Detail
Detail view for a specific expense approval request, showing expense metadata, receipt images, and approval actions.
Route
Rendered within /organisations/:organisationId/pending-approvals/:approvalId when the request type is Expense.
Layout
A horizontal flex layout (flex-row, wraps to column on small screens) with two main sections: cards on the left and receipt viewer on the right.
Request Summary Card
Top card with the request banner (coloured by mapRequestTypeToColor[approval.requestType]) showing:
- Title: "{requesterName}'s request" (i18n key:
pending-approvals.detail-view.title) - Approval stage indicator (
ApprovalStage/ApprovalStageV2component) showing first-level and second-level approver names, action status. Shown when approval is approved or rejected. - Expense title (truncated with
Ellipsescomponent) - Expense code (or "N/A" when null)
- Payment/Purchase order number (editable when pending;
inputwith max 15 characters;readonlywhen not editing) - Reimbursement type (defaults to
ReimbursementTypes.PaymentToWorkerwhen null) - Description (truncated to 150 characters via lodash
truncate) - Overall amount in local currency (
MoneyDisplaywithroundedValueWithDecimalPlaces). IflocalCurrencydiffers frominvoicingCurrency, a secondMoneyDisplayshows the invoicing currency amount in a lighter theme. - "Adjusted Rate" badge (shown when
selectedApprovalItem.incurredAmountexists) - Status display with approver/rejector name and date:
- Approved/PaymentDue: shows "by {approverName}"
- Paid: shows date and "approved by {approverName}"
- Rejected: shows "by {rejectorName}, {date}"
- Payment due date (shown for PaymentDue status, formatted via
formatDateRemundoStandard) - Approver notes link (shown when
approval.historyexists and has entries; opensApprovalHistoryModalV2)
Edit Mode
When the approval is neither approved nor rejected (rejectedAt === null && approvedAt === null):
- A pencil icon button enables editing of the payment order number and description.
- Edit mode shows a cancel button (X icon) and submit button (checkmark icon).
- Submit dispatches
EditExpenseCommandwithId,CorrelationId(EOR ID),PaymentOrder, andDescription. - Polls via
pollingGetConditionongetPendingApprovaluntil the description and payment order match the edited values. - Cancel (
onCancel) reverts to original values viacloneDeepand exits edit mode. - During submission, a spinner replaces the checkmark and both buttons are disabled.
- On error, an error notification is shown and the form reverts.
Expense Item Card
Second card showing details for the selected expense item, with a type-specific banner:
- Type of expense (
selectedApprovalItem.type) - Mileage details (shown when
mileageValueexists): unit, value (mileageValue+mileageUnit), unit cost (mileageUnitCostwithMoneyDisplay) - Location (shown when
locationis present) - Sum (transaction) -- Amount in item currency (
selectedApprovalItem.amountwithselectedApprovalItem.currency) - Sum worker (in local currency):
- When
incurredAmount,amountInLocalCurrency, andincurredAmountInLocalCurrencyall exist: shows incurred amount (dark theme), original amount (light theme), and deviation percentage. Deviation above 5% is highlighted with error style. - Otherwise: shows
amountInLocalCurrencyin dark theme. - Includes "Adjusted Rate" badge when applicable.
- When
- Sum invoicing (in invoicing currency via
incurredAmountInInvoicingCurrency) - Submitted date (formatted as US locale: "Mon DD, YYYY")
- Transaction date (shown when present; same format)
- Files attached indicator with
InfoTiptooltip listing file blob names on hover (mouseentertrigger) - Missing receipt reason (shown when present; via
stringIsNullOrEmptycheck) - Note (shown when present; with overflow-wrap styling)
Item Pagination
When multiple expense items exist (approvalItems.length > 1), an ItemPaginationSelector allows cycling through items. Changing the selected item triggers selectedItemChange, which updates selectedApprovalItem and reloads receipts.
Receipt Viewer
Displayed alongside the cards when receipts are attached (!loadingBlobs && picture):
- PDF files rendered via
PdfViewer(height: 70, toolbar hidden) - Image files (PNG, JPEG) rendered as
<img>elements (max-width: 50rem, object-fit: contain) inside a scrollable container (min-height: 40rem) - HEIC files are converted to base64 via
convertBase64IfHeicTypebefore display - Navigation arrows for cycling through multiple attached files (left/right arrow buttons). Dots indicate position; an oval highlights the current item.
- Download button (
ButtonRound) for the current receipt. Downloads using a temporary anchor element withURL.createObjectURL. - When loading blobs and files exist, a
Spinneris shown.
Approval Buttons
ApprovalButtons component shown when:
- The approval status is
PendingorOverdue, AND - The current user is permitted to act (checked via
allowApprovalActionon mount, which returns{ allow, pendingSecondLevel })
Dispatches approve-request and decline-request events to the parent DetailView.
Data Loading
On initialisation (pInit):
- Resolves the selected approval item by
itemIdparameter (if provided inparams) or defaults to the first item. - Calls
getItemReceiptsto fetch receipt blobs.
Receipt fetching (getItemReceipts):
- For each file in
selectedApprovalItem.files, callsgetBlobwith container name"expenses", organisation ID, requester ID, and file name. - Determines blob type from file extension (
.pdf->application/pdf,.png->image/png,.jpg/.jpeg->image/jpeg). - Converts response to base64 via
convertBase64IfHeicType, then creates aBlobobject. - Sets the blob name from the original
blobNamewith the correct file extension. - After all blobs are loaded, selects the first blob and creates an object URL for display.
Behavior notes
- The blob type is determined from the file extension (
.pdf,.png,.jpg/.jpeg). - Deviation percentages above 5% are highlighted with an
errorCSS class. - All text content (spans, paragraphs) has
user-select: textfor copy-paste support. - The receipt section has a fixed width of 40rem and min-height of 45rem.