import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "main.dart"; import "models.dart"; import "theme.dart"; final _numFmt = NumberFormat.decimalPattern(); /// Formats a date per the signed-in user's chosen date format (appSettings), /// mirroring the web app's format.js. String formatDate(DateTime? d) { if (d == null) return "—"; final pattern = switch (appSettings.dateFormat) { "DMY_NUM" => "dd-MM-yyyy", "DMY" => "dd MMM yyyy", "MDY" => "MMM dd, yyyy", _ => "yyyy-MM-dd", // YMD }; return DateFormat(pattern).format(d); } String formatKm(int? km) => (km == null || km == 0) ? "—" : "${_numFmt.format(km)} km"; const int _kmSoon = 1000; enum StatusKey { unknown, ok, soon, overdue } class Status { final StatusKey key; final String label; const Status(this.key, this.label); /// Soft tint background for the status pill — DriverVault semantic colours, /// re-cut for dark surfaces. Color bg(bool dark) => switch (key) { StatusKey.overdue => dark ? DriverVault.dangerSoftDark : DriverVault.dangerSoft, StatusKey.soon => dark ? DriverVault.warningSoftDark : DriverVault.warningSoft, StatusKey.ok => dark ? DriverVault.successSoftDark : DriverVault.successSoft, StatusKey.unknown => dark ? DriverVault.darkSunken : DriverVault.ink50, }; Color fg(bool dark) => switch (key) { StatusKey.overdue => DriverVault.danger, StatusKey.soon => DriverVault.warning, StatusKey.ok => DriverVault.success, StatusKey.unknown => dark ? DriverVault.darkTextMuted : DriverVault.ink500, }; } int _rank(StatusKey k) => switch (k) { StatusKey.unknown => 0, StatusKey.ok => 1, StatusKey.soon => 2, StatusKey.overdue => 3, }; Status _dateSignal(DateTime? nextDate) { if (nextDate == null) return const Status(StatusKey.unknown, "No data"); final today = DateTime.now(); final days = DateTime(nextDate.year, nextDate.month, nextDate.day) .difference(DateTime(today.year, today.month, today.day)) .inDays; if (days < 0) return Status(StatusKey.overdue, "Service Overdue ${days.abs()}d"); if (days <= 30) return Status(StatusKey.soon, "Due in ${days}d"); return Status(StatusKey.ok, "OK · ${days}d"); } Status _kmSignal(int currentKm, int? nextKm) { if (currentKm == 0 || nextKm == null) return const Status(StatusKey.unknown, "No km"); final remaining = nextKm - currentKm; if (remaining < 0) return Status(StatusKey.overdue, "Service Overdue ${_numFmt.format(remaining.abs())} km"); if (remaining <= _kmSoon) return Status(StatusKey.soon, "In ${_numFmt.format(remaining)} km"); return Status(StatusKey.ok, "${_numFmt.format(remaining)} km left"); } /// Combines the date- and km-based signals, returning the worse of the two — /// the same logic as the web app and the spreadsheet idea. Status serviceStatus(ServiceRecord? latest, Car car) { final date = _dateSignal(latest?.nextServiceDate); final km = _kmSignal(car.currentKm, latest?.nextServiceKm); final worse = _rank(km.key) > _rank(date.key) ? km : date; if (date.key == StatusKey.unknown && km.key != StatusKey.unknown) return km; if (km.key == StatusKey.unknown && date.key != StatusKey.unknown) return date; return worse; }