srs/lib/core/format.dart
Aswin Biju 45fcdcebfb Initial commit
Add SRS app: results, shops, orders and users, wired to srs-api
Flutter web/PWA for the lottery agency, built on Riverpod and go_router,
talking to srs-api over HTTP (HttpApi, token kept in SharedPreferences).

- Results, Shops, Orders and Users tabs (Users for admins only), with
  phone, tablet and desktop layouts and shared widgets and theme tokens
- Users: list with search and Active/Inactive filters; user detail with
  shops, sign-in details, and an Access card
- WhatsApp results switch on each user (sendResults on
  PATCH /api/users/:id). Shows the WhatsApp logo (Simple Icons, CC0,
  drawn with flutter_svg), green only when results will really go out.
  Can't be turned on without a phone number ("Add a phone number first"
  opens the phone sheet), and is paused while the user is inactive. The
  users list marks people who get results with the logo.
- Each Access switch has a one-line description; the explanation
  paragraphs are gone
- Remove demo mode: the app always uses the real AP
  test/support and is used only by tests.
2026-11-04 23:21:50 +05:30

118 lines
3.2 KiB
Dart

const _weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const _months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const _monthsLong = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
String inr(int n) {
final neg = n < 0;
final s = n.abs().toString();
if (s.length <= 3) return '${neg ? '-' : ''}$s';
final last = s.substring(s.length - 3);
var rest = s.substring(0, s.length - 3);
final parts = <String>[];
while (rest.length > 2) {
parts.insert(0, rest.substring(rest.length - 2));
rest = rest.substring(0, rest.length - 2);
}
if (rest.isNotEmpty) parts.insert(0, rest);
return '${neg ? '-' : ''}${parts.join(',')},$last';
}
String rupees(int n) => '₹${inr(n)}';
DateTime dateOnly(DateTime d) => DateTime(d.year, d.month, d.day);
bool sameDay(DateTime a, DateTime b) =>
a.year == b.year && a.month == b.month && a.day == b.day;
DateTime addDays(DateTime d, int n) => DateTime(d.year, d.month, d.day + n);
/// Fri, 16 Oct 2026
String dateLong(DateTime d) =>
'${_weekdays[d.weekday - 1]}, ${d.day} ${_months[d.month - 1]} ${d.year}';
/// Fri, 16 Oct
String dateShort(DateTime d) =>
'${_weekdays[d.weekday - 1]}, ${d.day} ${_months[d.month - 1]}';
/// October 2026
String monthYear(DateTime d) => '${_monthsLong[d.month - 1]} ${d.year}';
/// 12:41 PM
String time12(DateTime d) {
final h = d.hour % 12 == 0 ? 12 : d.hour % 12;
final m = d.minute.toString().padLeft(2, '0');
return '$h:$m ${d.hour >= 12 ? 'PM' : 'AM'}';
}
String isoDate(DateTime d) =>
'${d.year.toString().padLeft(4, '0')}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
String plural(int n, String one, [String? many]) =>
'${inr(n)} ${n == 1 ? one : (many ?? '${one}s')}';
/// 919000012345 -> +91 90000 12345
String fmtPhone(String? digits) {
if (digits == null || digits.isEmpty) return '';
if (digits.length == 12 && digits.startsWith('91')) {
return '+91 ${digits.substring(2, 7)} ${digits.substring(7)}';
}
return '+$digits';
}
String initials(String name) {
final parts = name.split(RegExp(r'[._\- ]+')).where((p) => p.isNotEmpty);
final list = parts.toList();
if (list.isEmpty) return '?';
if (list.length == 1) {
final w = list.first;
return w.substring(0, w.length >= 2 ? 2 : 1).toUpperCase();
}
return (list[0][0] + list[1][0]).toUpperCase();
}
String tierLabel(int tier) => '${tier}th prize';
/// "5th prize", or "5th + 8th prize" for an ending in two tiers.
String tiersLabel(List<int> tiers) {
final sorted = [...tiers]..sort();
if (sorted.length < 2) return sorted.isEmpty ? '' : tierLabel(sorted.first);
return '${sorted.map((t) => '${t}th').join(' + ')} prize';
}
/// A server instant (UTC) as the Indian wall-clock time the screens show.
DateTime istWallClock(DateTime instant) {
final t = instant.toUtc().add(const Duration(hours: 5, minutes: 30));
return DateTime(t.year, t.month, t.day, t.hour, t.minute, t.second);
}
/// Parses a YYYY-MM-DD calendar date.
DateTime parseDate(String iso) => DateTime(
int.parse(iso.substring(0, 4)),
int.parse(iso.substring(5, 7)),
int.parse(iso.substring(8, 10)),
);