feat: add invitations constants and service to frontend
This commit is contained in:
parent
5f6f3a5f0a
commit
c1e13d3061
@ -1,10 +1,13 @@
|
|||||||
class ApiConstants {
|
class ApiConstants {
|
||||||
static const String baseUrl = 'http://localhost:8080';
|
static const String baseUrl = 'http://localhost:8080';
|
||||||
|
|
||||||
// Auth endpoints
|
// Auth endpoints
|
||||||
static const String loginEndpoint = '/login';
|
static const String loginEndpoint = '/login';
|
||||||
|
|
||||||
// User endpoints
|
// User endpoints
|
||||||
static const String getUserEndpoint = '/getUser';
|
static const String getUserEndpoint = '/getUser';
|
||||||
static const String updateUserEndpoint = '/updateUser';
|
static const String updateUserEndpoint = '/updateUser';
|
||||||
}
|
|
||||||
|
// Invitation endpoints
|
||||||
|
static const String getAllInvitationsEndpoint = '/invitations/all';
|
||||||
|
}
|
||||||
|
|||||||
131
frontend/lib/models/invitation_models.dart
Normal file
131
frontend/lib/models/invitation_models.dart
Normal file
@ -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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> json) {
|
||||||
|
return InvitationsResponse(
|
||||||
|
status: json['status'],
|
||||||
|
message: json['message'],
|
||||||
|
data: json['data'] != null ? InvitationsData.fromJson(json['data']) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvitationsData {
|
||||||
|
final List<Invitation> created;
|
||||||
|
final List<Invitation> accepted;
|
||||||
|
final List<Invitation> available;
|
||||||
|
|
||||||
|
InvitationsData({
|
||||||
|
required this.created,
|
||||||
|
required this.accepted,
|
||||||
|
required this.available,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory InvitationsData.fromJson(Map<String, dynamic> 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;
|
||||||
|
}
|
||||||
115
frontend/lib/utils/invitation_utils.dart
Normal file
115
frontend/lib/utils/invitation_utils.dart
Normal file
@ -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<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user