No content changes: git diff --ignore-all-space over these files is empty. The churn came from editing on Windows against a repo checked out with LF.
164 lines
6.2 KiB
Dart
164 lines
6.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../../config/app_config.dart';
|
|
import '../../models/document_model.dart';
|
|
|
|
class DocumentCard extends StatelessWidget {
|
|
final DocumentModel doc;
|
|
final Future<void> Function(DocumentModel) onPrint;
|
|
final bool isUnsynced;
|
|
|
|
const DocumentCard({
|
|
super.key,
|
|
required this.doc,
|
|
required this.onPrint,
|
|
this.isUnsynced = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isProductScan = doc.docType == 'Product';
|
|
|
|
// Header category text
|
|
final categoryText = isProductScan
|
|
? (doc.items.isNotEmpty ? doc.items.first.namaBarang : 'Product Scan')
|
|
: (doc.namaPenerima.isNotEmpty ? doc.namaPenerima : 'Staff Toko');
|
|
|
|
// Category color: Product → green (primaryColor), DO → orange (doModeColor).
|
|
// doModeColor is the shared constant for the DO orange cue across the app
|
|
// (camera drawer, tab switcher, and here) — was an ad-hoc inline value.
|
|
final categoryColor = isProductScan ? AppConfig.primaryColor : AppConfig.doModeColor;
|
|
|
|
// Sync status badge (see docs/api-contract-map.md G6 - a document can be
|
|
// corrected locally after its PUT sync failed; the corrected copy is
|
|
// shown here, so the badge must reflect that it hasn't reached the
|
|
// server yet rather than always claiming "Terkonfirmasi").
|
|
final badgeLabel = isUnsynced ? 'Belum Tersinkron' : 'Terkonfirmasi';
|
|
final badgeColor = isUnsynced ? Colors.deepOrange : AppConfig.successColor;
|
|
|
|
// Bold title
|
|
final boldTitle = isProductScan
|
|
? doc.noDo
|
|
: doc.noPo;
|
|
|
|
// Subtitle text. Product Scan no longer links to a PO document (field
|
|
// removed - user request), so its subtitle is just the expiry date.
|
|
final subtitleText = isProductScan
|
|
? _formatDateString(doc.alamat.replaceAll("Product Scan: Expired ", ""))
|
|
: '${doc.tanggal} • ${doc.items.length} Item';
|
|
|
|
return Card(
|
|
elevation: 0,
|
|
margin: const EdgeInsets.only(bottom: AppConfig.spaceMedium),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium),
|
|
side: BorderSide(color: Colors.grey.shade200),
|
|
),
|
|
child: InkWell(
|
|
onTap: () => onPrint(doc),
|
|
borderRadius: BorderRadius.circular(AppConfig.borderRadiusMedium),
|
|
child: Padding(
|
|
padding: AppConfig.paddingLarge,
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Top category label
|
|
Text(
|
|
categoryText,
|
|
style: AppConfig.bodySmall.copyWith(
|
|
color: categoryColor,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: AppConfig.spaceXS),
|
|
// Large bold title
|
|
Text(
|
|
boldTitle,
|
|
style: AppConfig.bodyLarge.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppConfig.textPrimary,
|
|
fontSize: 16,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: AppConfig.spaceXS),
|
|
// Subtitle details
|
|
Text(
|
|
subtitleText,
|
|
style: AppConfig.bodyMedium.copyWith(
|
|
color: AppConfig.textSecondary,
|
|
fontSize: 13,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: AppConfig.spaceMedium),
|
|
// Right column with status badge and print button
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
// Status badge (Terkonfirmasi / Belum Tersinkron)
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: badgeColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(AppConfig.borderRadiusSmall),
|
|
),
|
|
child: Text(
|
|
badgeLabel,
|
|
style: AppConfig.bodySmall.copyWith(
|
|
color: badgeColor,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: AppConfig.spaceMedium),
|
|
// Print button styled as light-gray square container with print icon
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade100,
|
|
borderRadius: BorderRadius.circular(AppConfig.borderRadiusSmall),
|
|
),
|
|
child: const Icon(
|
|
Icons.print,
|
|
size: 18,
|
|
color: AppConfig.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _formatDateString(String input) {
|
|
try {
|
|
if (input.contains('/')) {
|
|
final parts = input.split('/');
|
|
if (parts.length == 3) {
|
|
final day = int.parse(parts[0]);
|
|
final month = int.parse(parts[1]);
|
|
final year = int.parse(parts[2]);
|
|
final date = DateTime(year, month, day);
|
|
return DateFormat('dd MMMM yyyy').format(date);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Fallback
|
|
}
|
|
return input;
|
|
}
|
|
}
|