diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/card_cell_skeleton/date_card_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/card_cell_skeleton/date_card_cell.dart index c459d8cc60487..3b7c3fdefe20d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/card_cell_skeleton/date_card_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/card_cell_skeleton/date_card_cell.dart @@ -1,8 +1,8 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/plugins/database/application/cell/bloc/date_cell_bloc.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller.dart'; import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; -import 'package:appflowy/plugins/database/application/cell/bloc/date_cell_bloc.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -57,6 +57,10 @@ class _DateCellState extends State { return const SizedBox.shrink(); } + final timeRemaining = getTimeRemainingFromCellData(state.cellData); + final isOverdue = state.cellData.dateTime != null && + state.cellData.dateTime!.isBefore(DateTime.now()); + return Container( alignment: Alignment.centerLeft, padding: widget.style.padding, @@ -73,6 +77,19 @@ class _DateCellState extends State { const HSpace(4), const FlowySvg(FlowySvgs.clock_alarm_s), ], + if (timeRemaining.isNotEmpty) ...[ + const HSpace(8), + Text( + timeRemaining, + style: widget.style.textStyle.copyWith( + fontSize: 11, + color: isOverdue + ? Colors.red.shade600 + : (widget.style.textStyle.color ?? Colors.grey) + .withValues(alpha: 0.6), + ), + ), + ], ], ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/editable_cell_skeleton/date.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/editable_cell_skeleton/date.dart index e61c759f48751..cafd37130aa65 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/editable_cell_skeleton/date.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/editable_cell_skeleton/date.dart @@ -122,6 +122,40 @@ String getDateCellStrFromCellData(FieldInfo field, DateCellData cellData) { } } +/// Returns a human-readable string representing the time remaining until the given date +/// or how long overdue it is +/// e.g., "3 days left", "2 hours left", "5 minutes left" (for future dates) +/// "2 days overdue", "3 hours overdue" (for past dates) +/// Returns an empty string if cellData.dateTime is null +String getTimeRemainingFromCellData(DateCellData cellData) { + if (cellData.dateTime == null) { + return ""; + } + + final now = DateTime.now(); + final targetDate = cellData.dateTime!; + final difference = targetDate.difference(now); + + // Calculate absolute time difference + final absDifference = difference.abs(); + final days = absDifference.inDays; + final hours = absDifference.inHours; + final minutes = absDifference.inMinutes; + + // Determine if it's overdue or upcoming + final isOverdue = difference.isNegative; + + if (days > 0) { + return "$days day${days == 1 ? '' : 's'} ${isOverdue ? 'overdue' : 'left'}"; + } else if (hours > 0) { + return "$hours hour${hours == 1 ? '' : 's'} ${isOverdue ? 'overdue' : 'left'}"; + } else if (minutes > 0) { + return "$minutes min${minutes == 1 ? '' : 's'} ${isOverdue ? 'overdue' : 'left'}"; + } else { + return isOverdue ? "Overdue" : "Less than a minute"; + } +} + extension GetDateFormatExtension on DateFormatPB { String get pattern => switch (this) { DateFormatPB.Local => 'MM/dd/y',