From c1e13d30619689ae47903b2229a91d92d3a97102 Mon Sep 17 00:00:00 2001 From: sBubshait Date: Tue, 22 Jul 2025 10:22:28 +0300 Subject: [PATCH] feat: add invitations constants and service to frontend --- frontend/lib/constants/api_constants.dart | 9 +- frontend/lib/models/invitation_models.dart | 131 +++++++++++++++++++++ frontend/lib/utils/invitation_utils.dart | 115 ++++++++++++++++++ 3 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 frontend/lib/models/invitation_models.dart create mode 100644 frontend/lib/utils/invitation_utils.dart diff --git a/frontend/lib/constants/api_constants.dart b/frontend/lib/constants/api_constants.dart index 7b4f6ad..aed20db 100644 --- a/frontend/lib/constants/api_constants.dart +++ b/frontend/lib/constants/api_constants.dart @@ -1,10 +1,13 @@ class ApiConstants { static const String baseUrl = 'http://localhost:8080'; - + // Auth endpoints static const String loginEndpoint = '/login'; - + // User endpoints static const String getUserEndpoint = '/getUser'; static const String updateUserEndpoint = '/updateUser'; -} \ No newline at end of file + + // Invitation endpoints + static const String getAllInvitationsEndpoint = '/invitations/all'; +} diff --git a/frontend/lib/models/invitation_models.dart b/frontend/lib/models/invitation_models.dart new file mode 100644 index 0000000..973ef72 --- /dev/null +++ b/frontend/lib/models/invitation_models.dart @@ -0,0 +1,131 @@ +class InvitationTag { + final int id; + final String name; + final String colorHex; + final String iconName; + + InvitationTag({ + required this.id, + required this.name, + required this.colorHex, + required this.iconName, + }); + + factory InvitationTag.fromJson(Map json) { + return InvitationTag( + id: json['id'], + name: json['name'], + colorHex: json['colorHex'], + iconName: json['iconName'], + ); + } +} + +class InvitationCreator { + final int id; + final String displayName; + final String? avatar; + + InvitationCreator({ + required this.id, + required this.displayName, + this.avatar, + }); + + factory InvitationCreator.fromJson(Map json) { + return InvitationCreator( + id: json['id'], + displayName: json['displayName'], + avatar: json['avatar'], + ); + } +} + +class Invitation { + final int id; + final String title; + final String? description; + final DateTime? dateTime; + final String? location; + final int maxParticipants; + final int currentAttendees; + final InvitationTag tag; + final InvitationCreator creator; + final DateTime createdAt; + + Invitation({ + required this.id, + required this.title, + this.description, + this.dateTime, + this.location, + required this.maxParticipants, + required this.currentAttendees, + required this.tag, + required this.creator, + required this.createdAt, + }); + + factory Invitation.fromJson(Map json) { + return Invitation( + id: json['id'], + title: json['title'], + description: json['description'], + dateTime: json['dateTime'] != null ? DateTime.parse(json['dateTime']) : null, + location: json['location'], + maxParticipants: json['maxParticipants'], + currentAttendees: json['currentAttendees'], + tag: InvitationTag.fromJson(json['tag']), + creator: InvitationCreator.fromJson(json['creator']), + createdAt: DateTime.parse(json['createdAt']), + ); + } +} + +class InvitationsResponse { + final bool status; + final String? message; + final InvitationsData? data; + + InvitationsResponse({ + required this.status, + this.message, + this.data, + }); + + factory InvitationsResponse.fromJson(Map json) { + return InvitationsResponse( + status: json['status'], + message: json['message'], + data: json['data'] != null ? InvitationsData.fromJson(json['data']) : null, + ); + } +} + +class InvitationsData { + final List created; + final List accepted; + final List available; + + InvitationsData({ + required this.created, + required this.accepted, + required this.available, + }); + + factory InvitationsData.fromJson(Map json) { + return InvitationsData( + created: (json['created'] as List) + .map((item) => Invitation.fromJson(item)) + .toList(), + accepted: (json['accepted'] as List) + .map((item) => Invitation.fromJson(item)) + .toList(), + available: (json['available'] as List) + .map((item) => Invitation.fromJson(item)) + .toList(), + ); + } + + bool get isEmpty => created.isEmpty && accepted.isEmpty && available.isEmpty; +} \ No newline at end of file diff --git a/frontend/lib/utils/invitation_utils.dart b/frontend/lib/utils/invitation_utils.dart new file mode 100644 index 0000000..1450108 --- /dev/null +++ b/frontend/lib/utils/invitation_utils.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; + +class InvitationUtils { + static IconData getIconFromName(String iconName) { + switch (iconName) { + case 'sports_soccer': + return Icons.sports_soccer; + case 'restaurant': + return Icons.restaurant; + case 'games': + return Icons.games; + case 'menu_book': + return Icons.menu_book; + case 'group': + return Icons.group; + case 'flight': + return Icons.flight; + case 'music_note': + return Icons.music_note; + case 'movie': + return Icons.movie; + case 'coffee': + return Icons.coffee; + case 'local_dining': + return Icons.local_dining; + case 'sports': + return Icons.sports; + case 'school': + return Icons.school; + default: + return Icons.category; + } + } + + static Color getColorFromHex(String hexColor) { + String cleanHex = hexColor.replaceAll('#', ''); + if (cleanHex.length == 6) { + cleanHex = 'FF' + cleanHex; + } + return Color(int.parse(cleanHex, radix: 16)); + } + + static String getRelativeTime(DateTime dateTime) { + final now = DateTime.now(); + final difference = now.difference(dateTime); + + if (difference.inMinutes < 1) { + return 'just now'; + } else if (difference.inMinutes < 60) { + return '${difference.inMinutes}m ago'; + } else if (difference.inHours < 24) { + return '${difference.inHours}h ago'; + } else if (difference.inDays == 1) { + return 'yesterday'; + } else if (difference.inDays < 7) { + return '${difference.inDays}d ago'; + } else { + return '${dateTime.day}/${dateTime.month}/${dateTime.year}'; + } + } + + static String getRelativeDateTime(DateTime dateTime) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final tomorrow = today.add(Duration(days: 1)); + final eventDate = DateTime(dateTime.year, dateTime.month, dateTime.day); + + String timeString = _formatTime(dateTime); + + if (eventDate == today) { + return 'today at $timeString'; + } else if (eventDate == tomorrow) { + return 'tomorrow at $timeString'; + } else if (eventDate.isAfter(today) && eventDate.isBefore(today.add(Duration(days: 7)))) { + List weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + return '${weekdays[eventDate.weekday - 1]} at $timeString'; + } else { + return '${eventDate.day}/${eventDate.month} at $timeString'; + } + } + + static String _formatTime(DateTime dateTime) { + int hour = dateTime.hour; + String period = hour >= 12 ? 'PM' : 'AM'; + hour = hour > 12 ? hour - 12 : (hour == 0 ? 12 : hour); + String minute = dateTime.minute.toString().padLeft(2, '0'); + return '$hour:$minute $period'; + } + + static String truncateDescription(String? description, {int maxLength = 80}) { + if (description == null || description.isEmpty) return ''; + if (description.length <= maxLength) return description; + return '${description.substring(0, maxLength)}...'; + } + + static String getParticipantsStatus(int current, int max) { + int needed = max - current; + if (needed <= 2 && needed > 0) { + return '$needed more person${needed == 1 ? '' : 's'} needed'; + } else { + return '$current/$max'; + } + } + + static Color getParticipantsStatusColor(int current, int max) { + int needed = max - current; + if (needed <= 2 && needed > 0) { + return Colors.orange; + } else if (current == max) { + return Colors.green; + } else { + return Colors.blue; + } + } +} \ No newline at end of file