chnaged make expense flow
This commit is contained in:
parent
1c253df5f9
commit
fd861b3adb
@ -327,7 +327,8 @@ class PaymentRequestDetailController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Submit Expense ---
|
// --- Submit Expense ---
|
||||||
Future<bool> submitExpense() async {
|
Future<bool> submitExpense(
|
||||||
|
{required String statusId, String? comment}) async {
|
||||||
if (selectedPaymentMode.value == null) return false;
|
if (selectedPaymentMode.value == null) return false;
|
||||||
|
|
||||||
isSubmitting.value = true;
|
isSubmitting.value = true;
|
||||||
@ -355,6 +356,8 @@ class PaymentRequestDetailController extends GetxController {
|
|||||||
gstNumber: gstNumberController.text,
|
gstNumber: gstNumberController.text,
|
||||||
paymentRequestId: _requestId,
|
paymentRequestId: _requestId,
|
||||||
billAttachments: attachmentsPayload,
|
billAttachments: attachmentsPayload,
|
||||||
|
statusId: statusId,
|
||||||
|
comment: comment ?? '',
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
isSubmitting.value = false;
|
isSubmitting.value = false;
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class ApiEndpoints {
|
|||||||
static const String updateExpensePaymentRequestStatus =
|
static const String updateExpensePaymentRequestStatus =
|
||||||
"/Expense/payment-request/action";
|
"/Expense/payment-request/action";
|
||||||
static const String createExpenseforPR =
|
static const String createExpenseforPR =
|
||||||
"/Expense/payment-request/expense/create";
|
"/expense/payment-request/action";
|
||||||
|
|
||||||
|
|
||||||
static const String getDashboardProjectProgress = "/dashboard/progression";
|
static const String getDashboardProjectProgress = "/dashboard/progression";
|
||||||
@ -96,7 +96,7 @@ class ApiEndpoints {
|
|||||||
static const String editExpense = "/Expense/edit";
|
static const String editExpense = "/Expense/edit";
|
||||||
static const String getMasterPaymentModes = "/master/payment-modes";
|
static const String getMasterPaymentModes = "/master/payment-modes";
|
||||||
static const String getMasterExpenseStatus = "/master/expenses-status";
|
static const String getMasterExpenseStatus = "/master/expenses-status";
|
||||||
static const String getMasterExpenseTypes = "/master/expenses-types";
|
static const String getMasterExpenseCategory = "/master/expenses-categories";
|
||||||
static const String updateExpenseStatus = "/expense/action";
|
static const String updateExpenseStatus = "/expense/action";
|
||||||
static const String deleteExpense = "/expense/delete";
|
static const String deleteExpense = "/expense/delete";
|
||||||
|
|
||||||
|
|||||||
@ -307,7 +307,9 @@ class ApiService {
|
|||||||
required String paymentModeId,
|
required String paymentModeId,
|
||||||
required String location,
|
required String location,
|
||||||
required String gstNumber,
|
required String gstNumber,
|
||||||
|
required String statusId,
|
||||||
required String paymentRequestId,
|
required String paymentRequestId,
|
||||||
|
required String comment,
|
||||||
List<Map<String, dynamic>> billAttachments = const [],
|
List<Map<String, dynamic>> billAttachments = const [],
|
||||||
}) async {
|
}) async {
|
||||||
const endpoint = ApiEndpoints.createExpenseforPR;
|
const endpoint = ApiEndpoints.createExpenseforPR;
|
||||||
@ -316,6 +318,8 @@ class ApiService {
|
|||||||
"paymentModeId": paymentModeId,
|
"paymentModeId": paymentModeId,
|
||||||
"location": location,
|
"location": location,
|
||||||
"gstNumber": gstNumber,
|
"gstNumber": gstNumber,
|
||||||
|
"statusId": statusId,
|
||||||
|
"comment": comment,
|
||||||
"paymentRequestId": paymentRequestId,
|
"paymentRequestId": paymentRequestId,
|
||||||
"billAttachments": billAttachments,
|
"billAttachments": billAttachments,
|
||||||
};
|
};
|
||||||
@ -789,7 +793,8 @@ class ApiService {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/// Create Project API
|
|
||||||
|
/// Create Project API
|
||||||
static Future<bool> createProjectApi({
|
static Future<bool> createProjectApi({
|
||||||
required String name,
|
required String name,
|
||||||
required String projectAddress,
|
required String projectAddress,
|
||||||
@ -1830,7 +1835,7 @@ class ApiService {
|
|||||||
|
|
||||||
/// Fetch Master Expense Types
|
/// Fetch Master Expense Types
|
||||||
static Future<List<dynamic>?> getMasterExpenseTypes() async {
|
static Future<List<dynamic>?> getMasterExpenseTypes() async {
|
||||||
const endpoint = ApiEndpoints.getMasterExpenseTypes;
|
const endpoint = ApiEndpoints.getMasterExpenseCategory;
|
||||||
return _getRequest(endpoint).then((res) => res != null
|
return _getRequest(endpoint).then((res) => res != null
|
||||||
? _parseResponse(res, label: 'Master Expense Types')
|
? _parseResponse(res, label: 'Master Expense Types')
|
||||||
: null);
|
: null);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:marco/helpers/widgets/my_text.dart';
|
|||||||
import 'package:marco/model/expense/expense_list_model.dart';
|
import 'package:marco/model/expense/expense_list_model.dart';
|
||||||
import 'package:marco/view/expense/expense_detail_screen.dart';
|
import 'package:marco/view/expense/expense_detail_screen.dart';
|
||||||
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
|
import 'package:marco/helpers/widgets/my_confirmation_dialog.dart';
|
||||||
|
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
||||||
|
|
||||||
class ExpenseAppBar extends StatelessWidget implements PreferredSizeWidget {
|
class ExpenseAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final ProjectController projectController;
|
final ProjectController projectController;
|
||||||
@ -72,7 +73,7 @@ class ExpenseAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchAndFilter extends StatelessWidget {
|
class SearchAndFilter extends StatefulWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final ValueChanged<String> onChanged;
|
final ValueChanged<String> onChanged;
|
||||||
final VoidCallback onFilterTap;
|
final VoidCallback onFilterTap;
|
||||||
@ -86,6 +87,11 @@ class SearchAndFilter extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SearchAndFilter> createState() => _SearchAndFilterState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchAndFilterState extends State<SearchAndFilter> with UIMixin {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -96,8 +102,8 @@ class SearchAndFilter extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 35,
|
height: 35,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: controller,
|
controller: widget.controller,
|
||||||
onChanged: onChanged,
|
onChanged: widget.onChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
prefixIcon:
|
prefixIcon:
|
||||||
@ -106,11 +112,16 @@ class SearchAndFilter extends StatelessWidget {
|
|||||||
filled: true,
|
filled: true,
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(5),
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
),
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: contentTheme.primary, width: 1.5),
|
||||||
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(5),
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -124,7 +135,7 @@ class SearchAndFilter extends StatelessWidget {
|
|||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.tune, color: Colors.black),
|
const Icon(Icons.tune, color: Colors.black),
|
||||||
if (expenseController.isFilterApplied)
|
if (widget.expenseController.isFilterApplied)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: -1,
|
top: -1,
|
||||||
right: -1,
|
right: -1,
|
||||||
@ -140,7 +151,7 @@ class SearchAndFilter extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onPressed: onFilterTap,
|
onPressed: widget.onFilterTap,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -8,14 +8,18 @@ import 'package:marco/helpers/widgets/expense/expense_form_widgets.dart';
|
|||||||
import 'package:marco/helpers/utils/validators.dart';
|
import 'package:marco/helpers/utils/validators.dart';
|
||||||
import 'package:marco/controller/finance/payment_request_detail_controller.dart';
|
import 'package:marco/controller/finance/payment_request_detail_controller.dart';
|
||||||
|
|
||||||
Future<T?> showCreateExpenseBottomSheet<T>() {
|
Future<T?> showCreateExpenseBottomSheet<T>({required String statusId}) {
|
||||||
return Get.bottomSheet<T>(
|
return Get.bottomSheet<T>(
|
||||||
_CreateExpenseBottomSheet(),
|
_CreateExpenseBottomSheet(statusId: statusId),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CreateExpenseBottomSheet extends StatefulWidget {
|
class _CreateExpenseBottomSheet extends StatefulWidget {
|
||||||
|
final String statusId;
|
||||||
|
|
||||||
|
const _CreateExpenseBottomSheet({required this.statusId, Key? key})
|
||||||
|
: super(key: key);
|
||||||
@override
|
@override
|
||||||
State<_CreateExpenseBottomSheet> createState() =>
|
State<_CreateExpenseBottomSheet> createState() =>
|
||||||
_CreateExpenseBottomSheetState();
|
_CreateExpenseBottomSheetState();
|
||||||
@ -24,8 +28,9 @@ class _CreateExpenseBottomSheet extends StatefulWidget {
|
|||||||
class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
||||||
final controller = Get.put(PaymentRequestDetailController());
|
final controller = Get.put(PaymentRequestDetailController());
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _paymentModeDropdownKey = GlobalKey();
|
final TextEditingController commentController = TextEditingController();
|
||||||
|
|
||||||
|
final _paymentModeDropdownKey = GlobalKey();
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(
|
return Obx(
|
||||||
@ -37,7 +42,10 @@ class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
|||||||
onCancel: Get.back,
|
onCancel: Get.back,
|
||||||
onSubmit: () async {
|
onSubmit: () async {
|
||||||
if (_formKey.currentState!.validate() && _validateSelections()) {
|
if (_formKey.currentState!.validate() && _validateSelections()) {
|
||||||
final success = await controller.submitExpense();
|
final success = await controller.submitExpense(
|
||||||
|
statusId: widget.statusId,
|
||||||
|
comment: commentController.text.trim(),
|
||||||
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
Get.back();
|
Get.back();
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
@ -47,6 +55,7 @@ class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
;
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -67,7 +76,7 @@ class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
|||||||
Icons.receipt_outlined,
|
Icons.receipt_outlined,
|
||||||
controller.gstNumberController,
|
controller.gstNumberController,
|
||||||
hint: "Enter GST Number",
|
hint: "Enter GST Number",
|
||||||
validator: null, // optional field
|
validator: null,
|
||||||
),
|
),
|
||||||
_gap(),
|
_gap(),
|
||||||
_buildTextField(
|
_buildTextField(
|
||||||
@ -86,6 +95,15 @@ class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
|||||||
),
|
),
|
||||||
_gap(),
|
_gap(),
|
||||||
_buildAttachmentField(),
|
_buildAttachmentField(),
|
||||||
|
_gap(),
|
||||||
|
_buildTextField(
|
||||||
|
"Comment",
|
||||||
|
Icons.comment_outlined,
|
||||||
|
commentController,
|
||||||
|
hint: "Enter a comment (optional)",
|
||||||
|
validator: null,
|
||||||
|
),
|
||||||
|
_gap(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -131,7 +149,7 @@ class _CreateExpenseBottomSheetState extends State<_CreateExpenseBottomSheet> {
|
|||||||
hint: hint ?? "",
|
hint: hint ?? "",
|
||||||
validator: validator,
|
validator: validator,
|
||||||
keyboardType: keyboardType ?? TextInputType.text,
|
keyboardType: keyboardType ?? TextInputType.text,
|
||||||
suffixIcon: suffixIcon,
|
suffixIcon: suffixIcon,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -118,8 +118,7 @@ class PaymentRequestData {
|
|||||||
expenseStatus: ExpenseStatus.fromJson(json['expenseStatus']),
|
expenseStatus: ExpenseStatus.fromJson(json['expenseStatus']),
|
||||||
paidTransactionId: json['paidTransactionId'],
|
paidTransactionId: json['paidTransactionId'],
|
||||||
paidAt: json['paidAt'] != null ? DateTime.parse(json['paidAt']) : null,
|
paidAt: json['paidAt'] != null ? DateTime.parse(json['paidAt']) : null,
|
||||||
paidBy:
|
paidBy: json['paidBy'] != null ? User.fromJson(json['paidBy']) : null,
|
||||||
json['paidBy'] != null ? User.fromJson(json['paidBy']) : null,
|
|
||||||
isAdvancePayment: json['isAdvancePayment'],
|
isAdvancePayment: json['isAdvancePayment'],
|
||||||
createdAt: DateTime.parse(json['createdAt']),
|
createdAt: DateTime.parse(json['createdAt']),
|
||||||
createdBy: User.fromJson(json['createdBy']),
|
createdBy: User.fromJson(json['createdBy']),
|
||||||
@ -373,7 +372,7 @@ class NextStatus {
|
|||||||
|
|
||||||
class UpdateLog {
|
class UpdateLog {
|
||||||
String id;
|
String id;
|
||||||
ExpenseStatus status;
|
ExpenseStatus? status;
|
||||||
ExpenseStatus nextStatus;
|
ExpenseStatus nextStatus;
|
||||||
String comment;
|
String comment;
|
||||||
DateTime updatedAt;
|
DateTime updatedAt;
|
||||||
@ -381,7 +380,7 @@ class UpdateLog {
|
|||||||
|
|
||||||
UpdateLog({
|
UpdateLog({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.status,
|
this.status,
|
||||||
required this.nextStatus,
|
required this.nextStatus,
|
||||||
required this.comment,
|
required this.comment,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
@ -390,7 +389,9 @@ class UpdateLog {
|
|||||||
|
|
||||||
factory UpdateLog.fromJson(Map<String, dynamic> json) => UpdateLog(
|
factory UpdateLog.fromJson(Map<String, dynamic> json) => UpdateLog(
|
||||||
id: json['id'],
|
id: json['id'],
|
||||||
status: ExpenseStatus.fromJson(json['status']),
|
status: json['status'] != null
|
||||||
|
? ExpenseStatus.fromJson(json['status'])
|
||||||
|
: null,
|
||||||
nextStatus: ExpenseStatus.fromJson(json['nextStatus']),
|
nextStatus: ExpenseStatus.fromJson(json['nextStatus']),
|
||||||
comment: json['comment'],
|
comment: json['comment'],
|
||||||
updatedAt: DateTime.parse(json['updatedAt']),
|
updatedAt: DateTime.parse(json['updatedAt']),
|
||||||
@ -399,7 +400,7 @@ class UpdateLog {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'id': id,
|
'id': id,
|
||||||
'status': status.toJson(),
|
'status': status?.toJson(),
|
||||||
'nextStatus': nextStatus.toJson(),
|
'nextStatus': nextStatus.toJson(),
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'updatedAt': updatedAt.toIso8601String(),
|
'updatedAt': updatedAt.toIso8601String(),
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
|||||||
with UIMixin {
|
with UIMixin {
|
||||||
final controller = Get.put(PaymentRequestDetailController());
|
final controller = Get.put(PaymentRequestDetailController());
|
||||||
final projectController = Get.find<ProjectController>();
|
final projectController = Get.find<ProjectController>();
|
||||||
final permissionController = Get.find<PermissionController>();
|
final permissionController = Get.put(PermissionController());
|
||||||
final RxBool canSubmit = false.obs;
|
final RxBool canSubmit = false.obs;
|
||||||
bool _checkedPermission = false;
|
bool _checkedPermission = false;
|
||||||
|
|
||||||
@ -163,36 +163,6 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
|||||||
.hasAnyPermission(status.permissionIds ?? []);
|
.hasAnyPermission(status.permissionIds ?? []);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
// If there are no next statuses, show "Create Expense" button
|
|
||||||
if (availableStatuses.isEmpty) {
|
|
||||||
return SafeArea(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
border: Border(top: BorderSide(color: Colors.grey.shade300)),
|
|
||||||
),
|
|
||||||
child: ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
backgroundColor: Colors.blue,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
showCreateExpenseBottomSheet();
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
"Create Expense",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal status buttons
|
// Normal status buttons
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -218,6 +188,7 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
// If status is reimbursement, show reimbursement bottom sheet
|
||||||
if (status.id == reimbursementStatusId) {
|
if (status.id == reimbursementStatusId) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
@ -232,6 +203,15 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
|||||||
onClose: () {},
|
onClose: () {},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If status is b8586f67-dc19-49c3-b4af-224149efe1d3, open create expense
|
||||||
|
} else if (status.id ==
|
||||||
|
'b8586f67-dc19-49c3-b4af-224149efe1d3') {
|
||||||
|
showCreateExpenseBottomSheet(
|
||||||
|
statusId: status.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Normal status flow
|
||||||
} else {
|
} else {
|
||||||
final comment = await showCommentBottomSheet(
|
final comment = await showCommentBottomSheet(
|
||||||
context, status.displayName);
|
context, status.displayName);
|
||||||
@ -407,8 +387,12 @@ class _Logs extends StatelessWidget {
|
|||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
final log = reversedLogs[index];
|
final log = reversedLogs[index];
|
||||||
|
|
||||||
final status = log.status.name;
|
final status = log.status?.name ?? 'Unknown';
|
||||||
final description = log.status.description;
|
final description = log.status?.description ?? '';
|
||||||
|
final statusColor = log.status != null
|
||||||
|
? colorParser(log.status!.color)
|
||||||
|
: Colors.grey;
|
||||||
|
|
||||||
final comment = log.comment;
|
final comment = log.comment;
|
||||||
final nextStatusName = log.nextStatus.name;
|
final nextStatusName = log.nextStatus.name;
|
||||||
|
|
||||||
@ -421,7 +405,6 @@ class _Logs extends StatelessWidget {
|
|||||||
final timestamp = _parseTimestamp(log.updatedAt);
|
final timestamp = _parseTimestamp(log.updatedAt);
|
||||||
final timeAgo = timeago.format(timestamp);
|
final timeAgo = timeago.format(timestamp);
|
||||||
|
|
||||||
final statusColor = colorParser(log.status.color);
|
|
||||||
final nextStatusColor = colorParser(log.nextStatus.color);
|
final nextStatusColor = colorParser(log.nextStatus.color);
|
||||||
|
|
||||||
return TimelineTile(
|
return TimelineTile(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user