Remove TaskStatus enum and related extensions, refactor BoardService and BoardResponse models for improved API integration, and update BoardViewModel to handle card movements between columns. Adjust UI components to reflect these changes and enhance error handling for card operations.
This commit is contained in:
@@ -1,29 +0,0 @@
|
|||||||
enum TaskStatus { toDo, inProgress, done }
|
|
||||||
|
|
||||||
extension TaskStatusExtension on TaskStatus {
|
|
||||||
// تبدیل enum به string
|
|
||||||
String get value => name;
|
|
||||||
|
|
||||||
// تبدیل string به enum
|
|
||||||
static TaskStatus fromString(String? status) {
|
|
||||||
if (status == null) {
|
|
||||||
return TaskStatus.toDo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TaskStatus.values.firstWhere(
|
|
||||||
(e) => e.name == status,
|
|
||||||
orElse: () => TaskStatus.toDo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String get displayNameEn {
|
|
||||||
switch (this) {
|
|
||||||
case TaskStatus.toDo:
|
|
||||||
return 'To Do';
|
|
||||||
case TaskStatus.inProgress:
|
|
||||||
return 'In Progress';
|
|
||||||
case TaskStatus.done:
|
|
||||||
return 'Done';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import '../../data/services/model/board_response/board_response.dart';
|
|
||||||
import '../enums/task_status.dart';
|
|
||||||
|
|
||||||
extension BoardDataExtension on BoardData {
|
|
||||||
// تبدیل status string به TaskStatus enum
|
|
||||||
TaskStatus get statusEnum => TaskStatusExtension.fromString(status);
|
|
||||||
|
|
||||||
// کپی BoardData با status جدید
|
|
||||||
BoardData copyWithStatus(TaskStatus newStatus) {
|
|
||||||
return copyWith(status: newStatus.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,17 @@ class BoardRepository {
|
|||||||
BoardRepository({required BoardService boardService})
|
BoardRepository({required BoardService boardService})
|
||||||
: _boardService = boardService;
|
: _boardService = boardService;
|
||||||
|
|
||||||
Future<Result<BoardResponse>> getBoard() async {
|
Future<Result<BoardResponse>> getBoard() => _boardService.getBoard();
|
||||||
return _boardService.getBoard();
|
|
||||||
|
Future<Result<MoveCardResponse>> moveCard({
|
||||||
|
required String cardId,
|
||||||
|
required String columnId,
|
||||||
|
required int newOrder,
|
||||||
|
}) {
|
||||||
|
return _boardService.moveCard(
|
||||||
|
cardId: cardId,
|
||||||
|
columnId: columnId,
|
||||||
|
newOrder: newOrder,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import '../../../core/network/network_manager.dart';
|
import 'dart:convert';
|
||||||
import '../../core/config/app_config.dart';
|
|
||||||
|
import '../../core/network/network_manager.dart';
|
||||||
import '../../core/utils/result.dart';
|
import '../../core/utils/result.dart';
|
||||||
import 'model/board_response/board_response.dart';
|
import 'model/board_response/board_response.dart';
|
||||||
|
|
||||||
@@ -9,72 +10,45 @@ class BoardService {
|
|||||||
|
|
||||||
final NetworkManager _networkManager;
|
final NetworkManager _networkManager;
|
||||||
|
|
||||||
|
static const _basePath = '/admin/v1/kanban/kanban';
|
||||||
|
|
||||||
Future<Result<BoardResponse>> getBoard() async {
|
Future<Result<BoardResponse>> getBoard() async {
|
||||||
final uri = '${AppConfig.apiBaseUrl}/api/v1/board';
|
return _networkManager.makeRequest(
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
'$_basePath/boards/default',
|
||||||
|
(json) => BoardResponse.fromJson(json),
|
||||||
|
method: 'GET',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// final result = await _networkManager.makeRequest(
|
Future<Result<MoveCardResponse>> moveCard({
|
||||||
// uri,
|
required String cardId,
|
||||||
// (json) => BoardResponse.fromJson(json),
|
required String columnId,
|
||||||
// method: 'GET',
|
required int newOrder,
|
||||||
// );
|
}) async {
|
||||||
// return result;
|
final body = jsonEncode({
|
||||||
|
'card_id': cardId,
|
||||||
return Result.ok(
|
'column_id': columnId,
|
||||||
BoardResponse(
|
'new_order': newOrder,
|
||||||
success: true,
|
});
|
||||||
message: 'Board data fetched successfully',
|
return _networkManager.makeRequest(
|
||||||
data: tasks,
|
'$_basePath/cards/move',
|
||||||
error: null,
|
(json) => MoveCardResponse.fromJson(json),
|
||||||
),
|
method: 'PUT',
|
||||||
|
data: body,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BoardData> tasks = [
|
class MoveCardResponse {
|
||||||
BoardData(
|
MoveCardResponse({required this.success, required this.message});
|
||||||
id: '1',
|
|
||||||
description:
|
final bool? success;
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
|
final String? message;
|
||||||
deadline: DateTime(2025, 6, 15),
|
|
||||||
assignedTo: 'Pratyush Tewari',
|
factory MoveCardResponse.fromJson(Map<String, dynamic> json) {
|
||||||
isReady: false,
|
return MoveCardResponse(
|
||||||
status: 'toDo',
|
success: json['success'] as bool?,
|
||||||
),
|
message: json['message'] as String?,
|
||||||
BoardData(
|
);
|
||||||
id: '2',
|
}
|
||||||
description:
|
}
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
|
|
||||||
deadline: DateTime(2025, 6, 15),
|
|
||||||
assignedTo: 'Pratyush Tewari',
|
|
||||||
isReady: true,
|
|
||||||
status: 'toDo',
|
|
||||||
),
|
|
||||||
BoardData(
|
|
||||||
id: '3',
|
|
||||||
description:
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
|
|
||||||
deadline: DateTime(2025, 6, 15),
|
|
||||||
assignedTo: 'Pratyush Tewari',
|
|
||||||
isReady: false,
|
|
||||||
status: 'inProgress',
|
|
||||||
),
|
|
||||||
BoardData(
|
|
||||||
id: '4',
|
|
||||||
description:
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
|
|
||||||
deadline: DateTime(2025, 6, 15),
|
|
||||||
assignedTo: 'Pratyush Tewari',
|
|
||||||
isReady: true,
|
|
||||||
status: 'inProgress',
|
|
||||||
),
|
|
||||||
BoardData(
|
|
||||||
id: '5',
|
|
||||||
description:
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua',
|
|
||||||
deadline: DateTime(2025, 6, 15),
|
|
||||||
assignedTo: 'Pratyush Tewari',
|
|
||||||
isReady: false,
|
|
||||||
status: 'done',
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -1,34 +1,229 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import '../../../../core/utils/date_utils.dart';
|
||||||
|
|
||||||
import '../error/error_model.dart';
|
import '../error/error_model.dart';
|
||||||
|
|
||||||
part 'board_response.freezed.dart';
|
class BoardResponse {
|
||||||
part 'board_response.g.dart';
|
BoardResponse({
|
||||||
|
required this.success,
|
||||||
|
required this.message,
|
||||||
|
required this.data,
|
||||||
|
required this.error,
|
||||||
|
});
|
||||||
|
|
||||||
@freezed
|
final bool? success;
|
||||||
abstract class BoardResponse with _$BoardResponse {
|
final String? message;
|
||||||
const factory BoardResponse({
|
final BoardDetailData? data;
|
||||||
required bool? success,
|
final ErrorModel? error;
|
||||||
required String? message,
|
|
||||||
required List<BoardData>? data,
|
|
||||||
required ErrorModel? error,
|
|
||||||
}) = _BoardResponse;
|
|
||||||
|
|
||||||
factory BoardResponse.fromJson(Map<String, Object?> json) =>
|
factory BoardResponse.fromJson(Map<String, dynamic> json) {
|
||||||
_$BoardResponseFromJson(json);
|
final dataJson = json['data'];
|
||||||
|
final errorJson = json['error'];
|
||||||
|
return BoardResponse(
|
||||||
|
success: json['success'] as bool?,
|
||||||
|
message: json['message'] as String?,
|
||||||
|
data:
|
||||||
|
dataJson is Map<String, dynamic>
|
||||||
|
? BoardDetailData.fromJson(dataJson)
|
||||||
|
: null,
|
||||||
|
error:
|
||||||
|
errorJson is Map<String, dynamic>
|
||||||
|
? ErrorModel.fromJson(errorJson)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
class BoardDetailData {
|
||||||
abstract class BoardData with _$BoardData {
|
BoardDetailData({required this.board});
|
||||||
const factory BoardData({
|
|
||||||
required String? id,
|
|
||||||
required String? description,
|
|
||||||
required DateTime? deadline,
|
|
||||||
required String? assignedTo,
|
|
||||||
required bool? isReady,
|
|
||||||
required String? status,
|
|
||||||
}) = _BoardData;
|
|
||||||
|
|
||||||
factory BoardData.fromJson(Map<String, Object?> json) =>
|
final Board? board;
|
||||||
_$BoardDataFromJson(json);
|
|
||||||
|
factory BoardDetailData.fromJson(Map<String, dynamic> json) {
|
||||||
|
final boardJson = json['board'];
|
||||||
|
return BoardDetailData(
|
||||||
|
board:
|
||||||
|
boardJson is Map<String, dynamic> ? Board.fromJson(boardJson) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Board {
|
||||||
|
Board({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.columns,
|
||||||
|
required this.isDefault,
|
||||||
|
required this.userId,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? id;
|
||||||
|
final String? name;
|
||||||
|
final String? description;
|
||||||
|
final List<BoardColumn> columns;
|
||||||
|
final bool? isDefault;
|
||||||
|
final String? userId;
|
||||||
|
final int? createdAt;
|
||||||
|
final int? updatedAt;
|
||||||
|
|
||||||
|
factory Board.fromJson(Map<String, dynamic> json) {
|
||||||
|
final columnsJson = json['columns'];
|
||||||
|
return Board(
|
||||||
|
id: json['id'] as String?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
description: json['description'] as String?,
|
||||||
|
columns:
|
||||||
|
columnsJson is List
|
||||||
|
? columnsJson
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.map(BoardColumn.fromJson)
|
||||||
|
.toList()
|
||||||
|
: <BoardColumn>[],
|
||||||
|
isDefault: json['is_default'] as bool?,
|
||||||
|
userId: json['user_id'] as String?,
|
||||||
|
createdAt: unixTimestampFromJson(json['created_at']),
|
||||||
|
updatedAt: unixTimestampFromJson(json['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Board copyWith({List<BoardColumn>? columns}) {
|
||||||
|
return Board(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
description: description,
|
||||||
|
columns: columns ?? this.columns,
|
||||||
|
isDefault: isDefault,
|
||||||
|
userId: userId,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoardColumn {
|
||||||
|
BoardColumn({
|
||||||
|
required this.id,
|
||||||
|
required this.boardId,
|
||||||
|
required this.name,
|
||||||
|
required this.order,
|
||||||
|
required this.color,
|
||||||
|
required this.limit,
|
||||||
|
required this.cards,
|
||||||
|
required this.cardCount,
|
||||||
|
required this.isOverLimit,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? id;
|
||||||
|
final String? boardId;
|
||||||
|
final String? name;
|
||||||
|
final int? order;
|
||||||
|
final String? color;
|
||||||
|
final int? limit;
|
||||||
|
final List<BoardCard> cards;
|
||||||
|
final int? cardCount;
|
||||||
|
final bool? isOverLimit;
|
||||||
|
final int? createdAt;
|
||||||
|
final int? updatedAt;
|
||||||
|
|
||||||
|
factory BoardColumn.fromJson(Map<String, dynamic> json) {
|
||||||
|
final cardsJson = json['cards'];
|
||||||
|
return BoardColumn(
|
||||||
|
id: json['id'] as String?,
|
||||||
|
boardId: json['board_id'] as String?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
order: (json['order'] as num?)?.toInt(),
|
||||||
|
color: json['color'] as String?,
|
||||||
|
limit: (json['limit'] as num?)?.toInt(),
|
||||||
|
cards:
|
||||||
|
cardsJson is List
|
||||||
|
? cardsJson
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.map(BoardCard.fromJson)
|
||||||
|
.toList()
|
||||||
|
: <BoardCard>[],
|
||||||
|
cardCount: (json['card_count'] as num?)?.toInt(),
|
||||||
|
isOverLimit: json['is_over_limit'] as bool?,
|
||||||
|
createdAt: unixTimestampFromJson(json['created_at']),
|
||||||
|
updatedAt: unixTimestampFromJson(json['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardColumn copyWith({List<BoardCard>? cards, int? cardCount}) {
|
||||||
|
return BoardColumn(
|
||||||
|
id: id,
|
||||||
|
boardId: boardId,
|
||||||
|
name: name,
|
||||||
|
order: order,
|
||||||
|
color: color,
|
||||||
|
limit: limit,
|
||||||
|
cards: cards ?? this.cards,
|
||||||
|
cardCount: cardCount ?? this.cardCount,
|
||||||
|
isOverLimit: isOverLimit,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoardCard {
|
||||||
|
BoardCard({
|
||||||
|
required this.id,
|
||||||
|
required this.columnId,
|
||||||
|
required this.tenderId,
|
||||||
|
required this.title,
|
||||||
|
required this.order,
|
||||||
|
required this.priority,
|
||||||
|
required this.labels,
|
||||||
|
required this.dueDate,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? id;
|
||||||
|
final String? columnId;
|
||||||
|
final String? tenderId;
|
||||||
|
final String? title;
|
||||||
|
final int? order;
|
||||||
|
final String? priority;
|
||||||
|
final List<String> labels;
|
||||||
|
final int? dueDate;
|
||||||
|
final int? createdAt;
|
||||||
|
final int? updatedAt;
|
||||||
|
|
||||||
|
factory BoardCard.fromJson(Map<String, dynamic> json) {
|
||||||
|
final labelsJson = json['labels'];
|
||||||
|
return BoardCard(
|
||||||
|
id: json['id'] as String?,
|
||||||
|
columnId: json['column_id'] as String?,
|
||||||
|
tenderId: json['tender_id'] as String?,
|
||||||
|
title: json['title'] as String?,
|
||||||
|
order: (json['order'] as num?)?.toInt(),
|
||||||
|
priority: json['priority'] as String?,
|
||||||
|
labels:
|
||||||
|
labelsJson is List
|
||||||
|
? labelsJson.whereType<String>().toList()
|
||||||
|
: <String>[],
|
||||||
|
dueDate: unixTimestampFromJson(json['due_date']),
|
||||||
|
createdAt: unixTimestampFromJson(json['created_at']),
|
||||||
|
updatedAt: unixTimestampFromJson(json['updated_at']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoardCard copyWith({String? columnId, int? order}) {
|
||||||
|
return BoardCard(
|
||||||
|
id: id,
|
||||||
|
columnId: columnId ?? this.columnId,
|
||||||
|
tenderId: tenderId,
|
||||||
|
title: title,
|
||||||
|
order: order ?? this.order,
|
||||||
|
priority: priority,
|
||||||
|
labels: labels,
|
||||||
|
dueDate: dueDate,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,596 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// coverage:ignore-file
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'board_response.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$BoardResponse {
|
|
||||||
|
|
||||||
bool? get success; String? get message; List<BoardData>? get data; ErrorModel? get error;
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$BoardResponseCopyWith<BoardResponse> get copyWith => _$BoardResponseCopyWithImpl<BoardResponse>(this as BoardResponse, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this BoardResponse to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BoardResponse&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.error, error) || other.error == error));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,message,const DeepCollectionEquality().hash(data),error);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'BoardResponse(success: $success, message: $message, data: $data, error: $error)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $BoardResponseCopyWith<$Res> {
|
|
||||||
factory $BoardResponseCopyWith(BoardResponse value, $Res Function(BoardResponse) _then) = _$BoardResponseCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
bool? success, String? message, List<BoardData>? data, ErrorModel? error
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$ErrorModelCopyWith<$Res>? get error;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$BoardResponseCopyWithImpl<$Res>
|
|
||||||
implements $BoardResponseCopyWith<$Res> {
|
|
||||||
_$BoardResponseCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final BoardResponse _self;
|
|
||||||
final $Res Function(BoardResponse) _then;
|
|
||||||
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<BoardData>?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ErrorModel?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ErrorModelCopyWith<$Res>? get error {
|
|
||||||
if (_self.error == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ErrorModelCopyWith<$Res>(_self.error!, (value) {
|
|
||||||
return _then(_self.copyWith(error: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [BoardResponse].
|
|
||||||
extension BoardResponsePatterns on BoardResponse {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _BoardResponse value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _BoardResponse value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse():
|
|
||||||
return $default(_that);case _:
|
|
||||||
throw StateError('Unexpected subclass');
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _BoardResponse value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool? success, String? message, List<BoardData>? data, ErrorModel? error)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse() when $default != null:
|
|
||||||
return $default(_that.success,_that.message,_that.data,_that.error);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool? success, String? message, List<BoardData>? data, ErrorModel? error) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse():
|
|
||||||
return $default(_that.success,_that.message,_that.data,_that.error);case _:
|
|
||||||
throw StateError('Unexpected subclass');
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool? success, String? message, List<BoardData>? data, ErrorModel? error)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardResponse() when $default != null:
|
|
||||||
return $default(_that.success,_that.message,_that.data,_that.error);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _BoardResponse implements BoardResponse {
|
|
||||||
const _BoardResponse({required this.success, required this.message, required final List<BoardData>? data, required this.error}): _data = data;
|
|
||||||
factory _BoardResponse.fromJson(Map<String, dynamic> json) => _$BoardResponseFromJson(json);
|
|
||||||
|
|
||||||
@override final bool? success;
|
|
||||||
@override final String? message;
|
|
||||||
final List<BoardData>? _data;
|
|
||||||
@override List<BoardData>? get data {
|
|
||||||
final value = _data;
|
|
||||||
if (value == null) return null;
|
|
||||||
if (_data is EqualUnmodifiableListView) return _data;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override final ErrorModel? error;
|
|
||||||
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$BoardResponseCopyWith<_BoardResponse> get copyWith => __$BoardResponseCopyWithImpl<_BoardResponse>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$BoardResponseToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BoardResponse&&(identical(other.success, success) || other.success == success)&&(identical(other.message, message) || other.message == message)&&const DeepCollectionEquality().equals(other._data, _data)&&(identical(other.error, error) || other.error == error));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,success,message,const DeepCollectionEquality().hash(_data),error);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'BoardResponse(success: $success, message: $message, data: $data, error: $error)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$BoardResponseCopyWith<$Res> implements $BoardResponseCopyWith<$Res> {
|
|
||||||
factory _$BoardResponseCopyWith(_BoardResponse value, $Res Function(_BoardResponse) _then) = __$BoardResponseCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
bool? success, String? message, List<BoardData>? data, ErrorModel? error
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@override $ErrorModelCopyWith<$Res>? get error;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$BoardResponseCopyWithImpl<$Res>
|
|
||||||
implements _$BoardResponseCopyWith<$Res> {
|
|
||||||
__$BoardResponseCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _BoardResponse _self;
|
|
||||||
final $Res Function(_BoardResponse) _then;
|
|
||||||
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? success = freezed,Object? message = freezed,Object? data = freezed,Object? error = freezed,}) {
|
|
||||||
return _then(_BoardResponse(
|
|
||||||
success: freezed == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool?,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,data: freezed == data ? _self._data : data // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<BoardData>?,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ErrorModel?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of BoardResponse
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ErrorModelCopyWith<$Res>? get error {
|
|
||||||
if (_self.error == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ErrorModelCopyWith<$Res>(_self.error!, (value) {
|
|
||||||
return _then(_self.copyWith(error: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$BoardData {
|
|
||||||
|
|
||||||
String? get id; String? get description; DateTime? get deadline; String? get assignedTo; bool? get isReady; String? get status;
|
|
||||||
/// Create a copy of BoardData
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$BoardDataCopyWith<BoardData> get copyWith => _$BoardDataCopyWithImpl<BoardData>(this as BoardData, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this BoardData to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BoardData&&(identical(other.id, id) || other.id == id)&&(identical(other.description, description) || other.description == description)&&(identical(other.deadline, deadline) || other.deadline == deadline)&&(identical(other.assignedTo, assignedTo) || other.assignedTo == assignedTo)&&(identical(other.isReady, isReady) || other.isReady == isReady)&&(identical(other.status, status) || other.status == status));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,description,deadline,assignedTo,isReady,status);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'BoardData(id: $id, description: $description, deadline: $deadline, assignedTo: $assignedTo, isReady: $isReady, status: $status)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $BoardDataCopyWith<$Res> {
|
|
||||||
factory $BoardDataCopyWith(BoardData value, $Res Function(BoardData) _then) = _$BoardDataCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({
|
|
||||||
String? id, String? description, DateTime? deadline, String? assignedTo, bool? isReady, String? status
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class _$BoardDataCopyWithImpl<$Res>
|
|
||||||
implements $BoardDataCopyWith<$Res> {
|
|
||||||
_$BoardDataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final BoardData _self;
|
|
||||||
final $Res Function(BoardData) _then;
|
|
||||||
|
|
||||||
/// Create a copy of BoardData
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? description = freezed,Object? deadline = freezed,Object? assignedTo = freezed,Object? isReady = freezed,Object? status = freezed,}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,deadline: freezed == deadline ? _self.deadline : deadline // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,assignedTo: freezed == assignedTo ? _self.assignedTo : assignedTo // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,isReady: freezed == isReady ? _self.isReady : isReady // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Adds pattern-matching-related methods to [BoardData].
|
|
||||||
extension BoardDataPatterns on BoardData {
|
|
||||||
/// A variant of `map` that fallback to returning `orElse`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _BoardData value)? $default,{required TResult orElse(),}){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// Callbacks receives the raw object, upcasted.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case final Subclass2 value:
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _BoardData value) $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData():
|
|
||||||
return $default(_that);case _:
|
|
||||||
throw StateError('Unexpected subclass');
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `map` that fallback to returning `null`.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case final Subclass value:
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _BoardData value)? $default,){
|
|
||||||
final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData() when $default != null:
|
|
||||||
return $default(_that);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to an `orElse` callback.
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return orElse();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? id, String? description, DateTime? deadline, String? assignedTo, bool? isReady, String? status)? $default,{required TResult orElse(),}) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData() when $default != null:
|
|
||||||
return $default(_that.id,_that.description,_that.deadline,_that.assignedTo,_that.isReady,_that.status);case _:
|
|
||||||
return orElse();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A `switch`-like method, using callbacks.
|
|
||||||
///
|
|
||||||
/// As opposed to `map`, this offers destructuring.
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case Subclass2(:final field2):
|
|
||||||
/// return ...;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? id, String? description, DateTime? deadline, String? assignedTo, bool? isReady, String? status) $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData():
|
|
||||||
return $default(_that.id,_that.description,_that.deadline,_that.assignedTo,_that.isReady,_that.status);case _:
|
|
||||||
throw StateError('Unexpected subclass');
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A variant of `when` that fallback to returning `null`
|
|
||||||
///
|
|
||||||
/// It is equivalent to doing:
|
|
||||||
/// ```dart
|
|
||||||
/// switch (sealedClass) {
|
|
||||||
/// case Subclass(:final field):
|
|
||||||
/// return ...;
|
|
||||||
/// case _:
|
|
||||||
/// return null;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? id, String? description, DateTime? deadline, String? assignedTo, bool? isReady, String? status)? $default,) {final _that = this;
|
|
||||||
switch (_that) {
|
|
||||||
case _BoardData() when $default != null:
|
|
||||||
return $default(_that.id,_that.description,_that.deadline,_that.assignedTo,_that.isReady,_that.status);case _:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
|
|
||||||
class _BoardData implements BoardData {
|
|
||||||
const _BoardData({required this.id, required this.description, required this.deadline, required this.assignedTo, required this.isReady, required this.status});
|
|
||||||
factory _BoardData.fromJson(Map<String, dynamic> json) => _$BoardDataFromJson(json);
|
|
||||||
|
|
||||||
@override final String? id;
|
|
||||||
@override final String? description;
|
|
||||||
@override final DateTime? deadline;
|
|
||||||
@override final String? assignedTo;
|
|
||||||
@override final bool? isReady;
|
|
||||||
@override final String? status;
|
|
||||||
|
|
||||||
/// Create a copy of BoardData
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$BoardDataCopyWith<_BoardData> get copyWith => __$BoardDataCopyWithImpl<_BoardData>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$BoardDataToJson(this, );
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BoardData&&(identical(other.id, id) || other.id == id)&&(identical(other.description, description) || other.description == description)&&(identical(other.deadline, deadline) || other.deadline == deadline)&&(identical(other.assignedTo, assignedTo) || other.assignedTo == assignedTo)&&(identical(other.isReady, isReady) || other.isReady == isReady)&&(identical(other.status, status) || other.status == status));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType,id,description,deadline,assignedTo,isReady,status);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'BoardData(id: $id, description: $description, deadline: $deadline, assignedTo: $assignedTo, isReady: $isReady, status: $status)';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$BoardDataCopyWith<$Res> implements $BoardDataCopyWith<$Res> {
|
|
||||||
factory _$BoardDataCopyWith(_BoardData value, $Res Function(_BoardData) _then) = __$BoardDataCopyWithImpl;
|
|
||||||
@override @useResult
|
|
||||||
$Res call({
|
|
||||||
String? id, String? description, DateTime? deadline, String? assignedTo, bool? isReady, String? status
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
/// @nodoc
|
|
||||||
class __$BoardDataCopyWithImpl<$Res>
|
|
||||||
implements _$BoardDataCopyWith<$Res> {
|
|
||||||
__$BoardDataCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _BoardData _self;
|
|
||||||
final $Res Function(_BoardData) _then;
|
|
||||||
|
|
||||||
/// Create a copy of BoardData
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? description = freezed,Object? deadline = freezed,Object? assignedTo = freezed,Object? isReady = freezed,Object? status = freezed,}) {
|
|
||||||
return _then(_BoardData(
|
|
||||||
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,deadline: freezed == deadline ? _self.deadline : deadline // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,assignedTo: freezed == assignedTo ? _self.assignedTo : assignedTo // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,isReady: freezed == isReady ? _self.isReady : isReady // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool?,status: freezed == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'board_response.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_BoardResponse _$BoardResponseFromJson(Map<String, dynamic> json) =>
|
|
||||||
_BoardResponse(
|
|
||||||
success: json['success'] as bool?,
|
|
||||||
message: json['message'] as String?,
|
|
||||||
data:
|
|
||||||
(json['data'] as List<dynamic>?)
|
|
||||||
?.map((e) => BoardData.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
error:
|
|
||||||
json['error'] == null
|
|
||||||
? null
|
|
||||||
: ErrorModel.fromJson(json['error'] as Map<String, dynamic>),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$BoardResponseToJson(_BoardResponse instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'success': instance.success,
|
|
||||||
'message': instance.message,
|
|
||||||
'data': instance.data,
|
|
||||||
'error': instance.error,
|
|
||||||
};
|
|
||||||
|
|
||||||
_BoardData _$BoardDataFromJson(Map<String, dynamic> json) => _BoardData(
|
|
||||||
id: json['id'] as String?,
|
|
||||||
description: json['description'] as String?,
|
|
||||||
deadline:
|
|
||||||
json['deadline'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['deadline'] as String),
|
|
||||||
assignedTo: json['assignedTo'] as String?,
|
|
||||||
isReady: json['isReady'] as bool?,
|
|
||||||
status: json['status'] as String?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$BoardDataToJson(_BoardData instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'description': instance.description,
|
|
||||||
'deadline': instance.deadline?.toIso8601String(),
|
|
||||||
'assignedTo': instance.assignedTo,
|
|
||||||
'isReady': instance.isReady,
|
|
||||||
'status': instance.status,
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../core/enums/task_status.dart';
|
|
||||||
import '../core/utils/result.dart';
|
import '../core/utils/result.dart';
|
||||||
import '../data/repositories/board_repositories.dart';
|
import '../data/repositories/board_repositories.dart';
|
||||||
|
import '../data/services/board_service.dart';
|
||||||
import '../data/services/model/board_response/board_response.dart';
|
import '../data/services/model/board_response/board_response.dart';
|
||||||
|
|
||||||
class BoardViewModel with ChangeNotifier {
|
class BoardViewModel with ChangeNotifier {
|
||||||
@@ -12,23 +12,27 @@ class BoardViewModel with ChangeNotifier {
|
|||||||
|
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
List<BoardData> _boardData = [];
|
Board? _board;
|
||||||
|
|
||||||
bool get isLoading => _isLoading;
|
bool get isLoading => _isLoading;
|
||||||
String? get errorMessage => _errorMessage;
|
String? get errorMessage => _errorMessage;
|
||||||
List<BoardData> get boardData => _boardData;
|
Board? get board => _board;
|
||||||
bool get hasData => _boardData.isNotEmpty;
|
List<BoardColumn> get columns => _board?.columns ?? const [];
|
||||||
|
bool get hasData => _board != null && columns.isNotEmpty;
|
||||||
|
|
||||||
Future<void> getBoard() async {
|
Future<void> getBoard() async {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
_errorMessage = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
final result = await _boardRepository.getBoard();
|
final result = await _boardRepository.getBoard();
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Ok<BoardResponse>():
|
case Ok<BoardResponse>():
|
||||||
// تبدیل به لیست قابل تغییر
|
_board = result.value.data?.board;
|
||||||
_boardData = List.from(result.value.data ?? []);
|
if (_board == null) {
|
||||||
|
_errorMessage = result.value.error?.message ?? 'No board data';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Error<BoardResponse>():
|
case Error<BoardResponse>():
|
||||||
_errorMessage = result.error.toString();
|
_errorMessage = result.error.toString();
|
||||||
@@ -38,40 +42,72 @@ class BoardViewModel with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update task status
|
/// Look up the column a given card currently lives in.
|
||||||
void updateTaskStatus(String taskId, String newStatus) {
|
BoardColumn? columnForCard(String cardId) {
|
||||||
final taskIndex = _boardData.indexWhere((task) => task.id == taskId);
|
for (final column in columns) {
|
||||||
if (taskIndex != -1) {
|
if (column.cards.any((c) => c.id == cardId)) return column;
|
||||||
_boardData[taskIndex] = _boardData[taskIndex].copyWith(status: newStatus);
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move a card to [targetColumnId] at the end of that column.
|
||||||
|
/// Optimistically updates local state, then calls the API; rolls back on
|
||||||
|
/// failure.
|
||||||
|
Future<Result<MoveCardResponse>> moveCard({
|
||||||
|
required BoardCard card,
|
||||||
|
required BoardColumn targetColumn,
|
||||||
|
}) async {
|
||||||
|
final cardId = card.id;
|
||||||
|
final targetColumnId = targetColumn.id;
|
||||||
|
if (cardId == null || targetColumnId == null || _board == null) {
|
||||||
|
return Result.error(Exception('Invalid card or column'));
|
||||||
|
}
|
||||||
|
|
||||||
|
final sourceColumn = columnForCard(cardId);
|
||||||
|
if (sourceColumn == null) {
|
||||||
|
return Result.error(Exception('Card not found in any column'));
|
||||||
|
}
|
||||||
|
if (sourceColumn.id == targetColumnId) {
|
||||||
|
return Result.ok(MoveCardResponse(success: true, message: 'No change'));
|
||||||
|
}
|
||||||
|
|
||||||
|
final previousColumns = _board!.columns;
|
||||||
|
final newOrder = targetColumn.cards.length;
|
||||||
|
|
||||||
|
_board = _board!.copyWith(
|
||||||
|
columns:
|
||||||
|
previousColumns.map((column) {
|
||||||
|
if (column.id == sourceColumn.id) {
|
||||||
|
final remaining =
|
||||||
|
column.cards.where((c) => c.id != cardId).toList();
|
||||||
|
return column.copyWith(
|
||||||
|
cards: remaining,
|
||||||
|
cardCount: remaining.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (column.id == targetColumnId) {
|
||||||
|
final updated = List<BoardCard>.from(column.cards)
|
||||||
|
..add(card.copyWith(columnId: targetColumnId, order: newOrder));
|
||||||
|
return column.copyWith(
|
||||||
|
cards: updated,
|
||||||
|
cardCount: updated.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
final result = await _boardRepository.moveCard(
|
||||||
|
cardId: cardId,
|
||||||
|
columnId: targetColumnId,
|
||||||
|
newOrder: newOrder,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result is Error<MoveCardResponse>) {
|
||||||
|
_board = _board!.copyWith(columns: previousColumns);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
|
|
||||||
bool isValidMove(TaskStatus currentStatus, TaskStatus targetStatus) {
|
|
||||||
if (currentStatus == TaskStatus.toDo) {
|
|
||||||
return targetStatus == TaskStatus.inProgress;
|
|
||||||
} else if (currentStatus == TaskStatus.inProgress) {
|
|
||||||
return targetStatus == TaskStatus.toDo || targetStatus == TaskStatus.done;
|
|
||||||
} else if (currentStatus == TaskStatus.done) {
|
|
||||||
return targetStatus == TaskStatus.inProgress;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BoardData> getTasksByStatus(TaskStatus status) {
|
|
||||||
return boardData.where((task) => task.status == status.value).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateTaskStatus(BoardData task, TaskStatus newStatus) {
|
|
||||||
if (task.status == newStatus.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final oldStatus = task.status;
|
|
||||||
|
|
||||||
// update task status in ViewModel
|
|
||||||
updateTaskStatus(task.id!, newStatus.value);
|
|
||||||
|
|
||||||
// _showStatusChangeMessage(context, task, oldStatus, newStatus);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tm_app/core/extensions/board_data_extension.dart';
|
|
||||||
import 'package:tm_app/core/theme/colors.dart';
|
import 'package:tm_app/core/theme/colors.dart';
|
||||||
|
import 'package:tm_app/core/utils/date_utils.dart';
|
||||||
|
import 'package:tm_app/core/utils/result.dart';
|
||||||
import 'package:tm_app/core/utils/size_config.dart';
|
import 'package:tm_app/core/utils/size_config.dart';
|
||||||
|
import 'package:tm_app/data/services/board_service.dart';
|
||||||
import 'package:tm_app/data/services/model/board_response/board_response.dart';
|
import 'package:tm_app/data/services/model/board_response/board_response.dart';
|
||||||
import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
|
import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
|
||||||
|
|
||||||
import '../../../core/constants/assets.dart';
|
import '../../../core/constants/assets.dart';
|
||||||
import '../../../core/enums/task_status.dart';
|
|
||||||
import '../../../core/utils/app_toast.dart';
|
import '../../../core/utils/app_toast.dart';
|
||||||
import '../../../view_models/board_view_model.dart';
|
import '../../../view_models/board_view_model.dart';
|
||||||
import '../strings/board_strings.dart';
|
import '../strings/board_strings.dart';
|
||||||
@@ -32,38 +33,27 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateTaskStatus(BoardData task, TaskStatus newStatus) {
|
Future<void> _onCardDropped(BoardCard card, BoardColumn target) async {
|
||||||
if (task.statusEnum == newStatus) {
|
final source = _boardViewModel.columnForCard(card.id ?? '');
|
||||||
return;
|
if (source == null || source.id == target.id) return;
|
||||||
}
|
|
||||||
|
|
||||||
final oldStatus = task.statusEnum;
|
final result = await _boardViewModel.moveCard(
|
||||||
|
card: card,
|
||||||
// update task status in ViewModel
|
targetColumn: target,
|
||||||
_boardViewModel.updateTaskStatus(task.id!, newStatus.value);
|
|
||||||
|
|
||||||
_showStatusChangeMessage(context, task, oldStatus, newStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showStatusChangeMessage(
|
|
||||||
BuildContext context,
|
|
||||||
BoardData task,
|
|
||||||
TaskStatus oldStatus,
|
|
||||||
TaskStatus newStatus,
|
|
||||||
) {
|
|
||||||
final statusNames = {
|
|
||||||
TaskStatus.toDo: BoardStrings.toDo,
|
|
||||||
TaskStatus.inProgress: BoardStrings.inProgress,
|
|
||||||
TaskStatus.done: BoardStrings.done,
|
|
||||||
};
|
|
||||||
|
|
||||||
AppToast.success(
|
|
||||||
context,
|
|
||||||
BoardStrings.taskMovedMessage(
|
|
||||||
statusNames[oldStatus]!,
|
|
||||||
statusNames[newStatus]!,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
if (result is Ok<MoveCardResponse>) {
|
||||||
|
AppToast.success(
|
||||||
|
context,
|
||||||
|
BoardStrings.cardMovedMessage(
|
||||||
|
source.name ?? '',
|
||||||
|
target.name ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (result is Error<MoveCardResponse>) {
|
||||||
|
AppToast.error(context, BoardStrings.moveFailed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -73,9 +63,7 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
backgroundColor: AppColors.backgroundColor,
|
backgroundColor: AppColors.backgroundColor,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
const DesktopNavigationWidget(
|
const DesktopNavigationWidget(currentIndex: 2),
|
||||||
currentIndex: 2, // Tenders index
|
|
||||||
),
|
|
||||||
Consumer<BoardViewModel>(
|
Consumer<BoardViewModel>(
|
||||||
builder: (context, viewModel, child) {
|
builder: (context, viewModel, child) {
|
||||||
if (viewModel.isLoading) {
|
if (viewModel.isLoading) {
|
||||||
@@ -92,12 +80,16 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
child: Center(child: Text(viewModel.errorMessage!)),
|
child: Center(child: Text(viewModel.errorMessage!)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (viewModel.hasData == false) {
|
if (!viewModel.hasData) {
|
||||||
return const Expanded(
|
return const Expanded(
|
||||||
child: Center(child: Text(BoardStrings.emptyListText)),
|
child: Center(child: Text(BoardStrings.emptyListText)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final columns = [...viewModel.columns]..sort(
|
||||||
|
(a, b) => (a.order ?? 0).compareTo(b.order ?? 0),
|
||||||
|
);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@@ -108,7 +100,7 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
Text(
|
Text(
|
||||||
BoardStrings.boardTitle,
|
viewModel.board?.name ?? BoardStrings.boardTitle,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20.0.sp(),
|
fontSize: 20.0.sp(),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -126,50 +118,15 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// To Do
|
for (int i = 0; i < columns.length; i++) ...[
|
||||||
Expanded(
|
if (i > 0) const SizedBox(width: 12),
|
||||||
child: _BoardColumn(
|
Expanded(
|
||||||
title: BoardStrings.toDo,
|
child: _BoardColumnView(
|
||||||
color: AppColors.primary20,
|
column: columns[i],
|
||||||
textColor: const Color(0xFF1976D2),
|
onCardDropped: _onCardDropped,
|
||||||
tasks: viewModel.getTasksByStatus(
|
|
||||||
TaskStatus.toDo,
|
|
||||||
),
|
),
|
||||||
status: TaskStatus.toDo,
|
|
||||||
onTaskDropped: _updateTaskStatus,
|
|
||||||
isValidMove: viewModel.isValidMove,
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
const SizedBox(width: 12),
|
|
||||||
// In Progress
|
|
||||||
Expanded(
|
|
||||||
child: _BoardColumn(
|
|
||||||
title: BoardStrings.inProgress,
|
|
||||||
color: AppColors.orange20,
|
|
||||||
textColor: AppColors.orange,
|
|
||||||
tasks: viewModel.getTasksByStatus(
|
|
||||||
TaskStatus.inProgress,
|
|
||||||
),
|
|
||||||
status: TaskStatus.inProgress,
|
|
||||||
onTaskDropped: _updateTaskStatus,
|
|
||||||
isValidMove: viewModel.isValidMove,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
// Done
|
|
||||||
Expanded(
|
|
||||||
child: _BoardColumn(
|
|
||||||
title: BoardStrings.done,
|
|
||||||
color: AppColors.secondary20,
|
|
||||||
textColor: AppColors.secondary70,
|
|
||||||
tasks: viewModel.getTasksByStatus(
|
|
||||||
TaskStatus.done,
|
|
||||||
),
|
|
||||||
status: TaskStatus.done,
|
|
||||||
onTaskDropped: _updateTaskStatus,
|
|
||||||
isValidMove: viewModel.isValidMove,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -187,43 +144,29 @@ class _BoardScreenState extends State<BoardScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BoardColumn extends StatelessWidget {
|
class _BoardColumnView extends StatelessWidget {
|
||||||
final String title;
|
final BoardColumn column;
|
||||||
final Color color;
|
final Future<void> Function(BoardCard, BoardColumn) onCardDropped;
|
||||||
final Color textColor;
|
|
||||||
final List<BoardData> tasks;
|
|
||||||
final TaskStatus status;
|
|
||||||
final Function(BoardData, TaskStatus) onTaskDropped;
|
|
||||||
final bool Function(TaskStatus, TaskStatus) isValidMove;
|
|
||||||
|
|
||||||
const _BoardColumn({
|
const _BoardColumnView({required this.column, required this.onCardDropped});
|
||||||
required this.title,
|
|
||||||
required this.color,
|
|
||||||
required this.textColor,
|
|
||||||
required this.tasks,
|
|
||||||
required this.status,
|
|
||||||
required this.onTaskDropped,
|
|
||||||
required this.isValidMove,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DragTarget<BoardData>(
|
final accent = _parseHexColor(column.color) ?? AppColors.textBlue;
|
||||||
onWillAcceptWithDetails: (details) {
|
return DragTarget<BoardCard>(
|
||||||
return isValidMove(details.data.statusEnum, status);
|
onWillAcceptWithDetails: (details) => details.data.columnId != column.id,
|
||||||
},
|
onAcceptWithDetails: (details) => onCardDropped(details.data, column),
|
||||||
onAcceptWithDetails: (details) {
|
|
||||||
onTaskDropped(details.data, status);
|
|
||||||
},
|
|
||||||
builder: (context, candidateData, rejectedData) {
|
builder: (context, candidateData, rejectedData) {
|
||||||
final isHovering = candidateData.isNotEmpty;
|
final isHovering = candidateData.isNotEmpty;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color:
|
color:
|
||||||
isHovering ? color.withValues(alpha: 0.3) : AppColors.primary0,
|
isHovering
|
||||||
|
? accent.withValues(alpha: 0.15)
|
||||||
|
: AppColors.primary0,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isHovering ? textColor : AppColors.borderBlue,
|
color: isHovering ? accent : AppColors.borderBlue,
|
||||||
width: isHovering ? 2 : 1,
|
width: isHovering ? 2 : 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -235,26 +178,25 @@ class _BoardColumn extends StatelessWidget {
|
|||||||
height: 34,
|
height: 34,
|
||||||
margin: const EdgeInsets.all(8),
|
margin: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: accent.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
_columnHeader(column),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: textColor,
|
color: accent,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
itemCount: tasks.length,
|
itemCount: column.cards.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return _TaskCard(task: tasks[index]);
|
return _TaskCard(card: column.cards[index], accent: accent);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -264,21 +206,25 @@ class _BoardColumn extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _columnHeader(BoardColumn column) {
|
||||||
|
final name = column.name ?? '';
|
||||||
|
final limit = column.limit;
|
||||||
|
if (limit == null || limit <= 0) return name;
|
||||||
|
return '$name (${column.cards.length}/$limit)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TaskCard extends StatelessWidget {
|
class _TaskCard extends StatelessWidget {
|
||||||
final BoardData task;
|
final BoardCard card;
|
||||||
|
final Color accent;
|
||||||
|
|
||||||
const _TaskCard({required this.task});
|
const _TaskCard({required this.card, required this.accent});
|
||||||
|
|
||||||
String _formatDate(DateTime date) {
|
|
||||||
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Draggable<BoardData>(
|
return Draggable<BoardCard>(
|
||||||
data: task,
|
data: card,
|
||||||
feedback: Material(
|
feedback: Material(
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
@@ -319,91 +265,72 @@ class _TaskCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCardContent(BuildContext context) {
|
Widget _buildCardContent(BuildContext context) {
|
||||||
|
final due = card.dueDate;
|
||||||
|
final labels = card.labels;
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
if (due != null)
|
||||||
height: 24.0,
|
Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0.w()),
|
height: 24.0,
|
||||||
decoration: BoxDecoration(
|
padding: EdgeInsets.symmetric(horizontal: 10.0.w()),
|
||||||
color:
|
decoration: BoxDecoration(
|
||||||
task.statusEnum == TaskStatus.toDo
|
color: accent.withValues(alpha: 0.15),
|
||||||
? AppColors.primary20
|
borderRadius: BorderRadius.circular(4),
|
||||||
: task.statusEnum == TaskStatus.inProgress
|
),
|
||||||
? AppColors.orange10
|
child: Row(
|
||||||
: AppColors.secondary10,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
borderRadius: BorderRadius.circular(4),
|
children: [
|
||||||
),
|
Text(
|
||||||
child: Row(
|
'${BoardStrings.deadlineLabel} :',
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
style: TextStyle(
|
||||||
children: [
|
fontSize: 14.0,
|
||||||
Text(
|
fontWeight: FontWeight.w500,
|
||||||
'${BoardStrings.deadlineLabel} :',
|
color: AppColors.textBlue,
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 14.0,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.textBlue,
|
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
Text(
|
timeConvertor(due),
|
||||||
_formatDate(task.deadline ?? DateTime.now()),
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 14.0,
|
||||||
fontSize: 14.0,
|
fontWeight: FontWeight.w500,
|
||||||
fontWeight: FontWeight.w500,
|
color: AppColors.grey80,
|
||||||
color: AppColors.grey80,
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
if (due != null) const SizedBox(height: 12),
|
||||||
const SizedBox(height: 12),
|
|
||||||
Text(
|
Text(
|
||||||
task.description!,
|
card.title ?? '',
|
||||||
style: TextStyle(fontSize: 14, color: AppColors.grey70),
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: AppColors.grey70,
|
||||||
|
),
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
if (card.priority != null) _PriorityChip(priority: card.priority!),
|
||||||
Container(
|
if (labels.isNotEmpty) ...[
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
const SizedBox(height: 8),
|
||||||
decoration: BoxDecoration(
|
Wrap(
|
||||||
color: task.isReady ?? false ? AppColors.primary20 : AppColors.red0,
|
spacing: 6,
|
||||||
borderRadius: BorderRadius.circular(8),
|
runSpacing: 6,
|
||||||
|
children: [for (final label in labels) _LabelChip(label: label)],
|
||||||
),
|
),
|
||||||
child: Row(
|
],
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: task.isReady! ? AppColors.textBlue : AppColors.error,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
task.isReady!
|
|
||||||
? BoardStrings.readyLabel
|
|
||||||
: BoardStrings.incompleteLabel,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: task.isReady! ? AppColors.textBlue : AppColors.red20,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(AssetsManager.profile),
|
SvgPicture.asset(AssetsManager.profile),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
task.assignedTo!,
|
card.tenderId ?? '',
|
||||||
style: TextStyle(fontSize: 12, color: AppColors.grey60),
|
style: TextStyle(fontSize: 12, color: AppColors.grey60),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -411,3 +338,83 @@ class _TaskCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PriorityChip extends StatelessWidget {
|
||||||
|
final String priority;
|
||||||
|
const _PriorityChip({required this.priority});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final (bg, fg, dot) = _styleFor(priority);
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bg,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
decoration: BoxDecoration(color: dot, shape: BoxShape.circle),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Text(
|
||||||
|
priority,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: fg,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(Color bg, Color fg, Color dot) _styleFor(String priority) {
|
||||||
|
switch (priority.toLowerCase()) {
|
||||||
|
case 'urgent':
|
||||||
|
case 'high':
|
||||||
|
return (AppColors.red0, AppColors.red20, AppColors.error);
|
||||||
|
case 'low':
|
||||||
|
return (AppColors.primary20, AppColors.textBlue, AppColors.textBlue);
|
||||||
|
case 'medium':
|
||||||
|
default:
|
||||||
|
return (AppColors.orange10, AppColors.orange, AppColors.orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LabelChip extends StatelessWidget {
|
||||||
|
final String label;
|
||||||
|
const _LabelChip({required this.label});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.primary20,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(fontSize: 11, color: AppColors.textBlue),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color? _parseHexColor(String? hex) {
|
||||||
|
if (hex == null) return null;
|
||||||
|
var value = hex.trim();
|
||||||
|
if (value.startsWith('#')) value = value.substring(1);
|
||||||
|
if (value.length == 6) value = 'FF$value';
|
||||||
|
if (value.length != 8) return null;
|
||||||
|
final parsed = int.tryParse(value, radix: 16);
|
||||||
|
if (parsed == null) return null;
|
||||||
|
return Color(parsed);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ class BoardStrings {
|
|||||||
BoardStrings._();
|
BoardStrings._();
|
||||||
|
|
||||||
static const String boardTitle = 'My Board';
|
static const String boardTitle = 'My Board';
|
||||||
static const String toDo = 'To Do';
|
|
||||||
static const String inProgress = 'In Progress';
|
|
||||||
static const String done = 'Done';
|
|
||||||
static const String emptyListText = 'No tasks found';
|
static const String emptyListText = 'No tasks found';
|
||||||
static const String deadlineLabel = 'Deadline';
|
static const String deadlineLabel = 'Deadline';
|
||||||
static const String readyLabel = 'Ready';
|
static const String moveFailed = 'Could not move card';
|
||||||
static const String incompleteLabel = 'Incomplete';
|
static String cardMovedMessage(String fromColumn, String toColumn) =>
|
||||||
static String taskMovedMessage(String oldStatus, String newStatus) =>
|
'Card moved from $fromColumn to $toColumn';
|
||||||
'Task moved from $oldStatus to $newStatus';
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user