Skip to content

Commit

Permalink
feat: config and table number
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrastopoulos committed Jan 14, 2025
1 parent d77201f commit cf0b06e
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 54 deletions.
142 changes: 109 additions & 33 deletions lib/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'models/project.dart';
import 'models/team.dart';
import 'models/discord.dart';
import 'package:http_parser/http_parser.dart';
import 'models/config.dart';

late SharedPreferences prefs;

Expand Down Expand Up @@ -534,74 +535,93 @@ Future<Project?> getProject(String id, String token) async {
}
}

Future<Project> newProject(
Future<bool> newProject(
BuildContext context,
String name,
String desc,
String teamId,
String team,
String slides,
String video,
String ghurl,
bool isVirtual,
String id,
String token) async {
Uri url = Uri.parse("${baseUrl}projects");
String github,
bool presenting,
String projId,
String token,
{int? tableNumber}
) async {
Uri url = Uri.parse("${baseUrl}projects/");
Map<String, String> headers = {
"Content-type": "application/json",
"x-access-token": token
};
String body = json.encode({

Map<String, dynamic> body = {
"name": name,
"description": desc,
"team": teamId,
"team": team,
"slides": slides,
"video": video,
"url": ghurl,
"presentingVirtually": isVirtual
});
final response = await http.post(url, headers: headers, body: body);
"url": github,
"presentingVirtually": presenting,
};
if (tableNumber != null) {
body["tableNumber"] = tableNumber;
}

final response = await http.post(
url,
headers: headers,
body: json.encode(body)
);

if (response.statusCode == 200) {
var data = json.decode(response.body);
Project project = Project.fromJson(data);
return project;
return true;
} else {
return Future.error("Error");
errorDialog(context, "Error", json.decode(response.body)['message']);
return false;
}
}

Future<Project> editProject(
Future<bool> editProject(
BuildContext context,
String name,
String desc,
String slides,
String video,
String ghurl,
bool isVirtual,
String id,
String token) async {
Uri url = Uri.parse("${baseUrl}projects/$id");
String github,
bool presenting,
String projId,
String token,
{int? tableNumber}
) async {
Uri url = Uri.parse("${baseUrl}projects/$projId");
Map<String, String> headers = {
"Content-type": "application/json",
"x-access-token": token
};
String body = json.encode({

Map<String, dynamic> body = {
"name": name,
"description": desc,
"slides": slides,
"video": video,
"url": ghurl,
"presentingVirtually": isVirtual
});
final response = await http.patch(url, headers: headers, body: body);
"url": github,
"presentingVirtually": presenting,
};
if (tableNumber != null) {
body["tableNumber"] = tableNumber;
}

final response = await http.patch(
url,
headers: headers,
body: json.encode(body)
);

if (response.statusCode == 200) {
var data = json.decode(response.body);
Project project = Project.fromJson(data);
return project;
return true;
} else {
return Future.error("Error");
//errorDialog(context, "Error", json.decode(response.body)['message']);
errorDialog(context, "Error", json.decode(response.body)['message']);
return false;
}
}

Expand Down Expand Up @@ -688,3 +708,59 @@ Future<bool> deleteProfilePic(String token) async {
return false;
}
}

// Get all projects (admin only)
Future<List<Project>> getAllProjects(String token) async {
Uri url = Uri.parse("${baseUrl}projects/all");
Map<String, String> headers = {
"Content-type": "application/json",
"x-access-token": token
};

final response = await http.get(url, headers: headers);

if (response.statusCode == 200) {
List<dynamic> projectsJson = json.decode(response.body);
return projectsJson.map((json) => Project.fromJson(json)).toList();
} else {
throw Exception('Failed to load projects');
}
}

// Update project table number (admin only)
Future<bool> updateProjectTableNumber(String projectId, int tableNumber, String token) async {
Uri url = Uri.parse("${baseUrl}projects/$projectId/table");
Map<String, String> headers = {
"Content-type": "application/json",
"x-access-token": token
};

final response = await http.patch(
url,
headers: headers,
body: json.encode({"tableNumber": tableNumber})
);

return response.statusCode == 200;
}

Future<ExpoConfig> getExpoConfig(String token) async {
Uri url = Uri.parse("${baseUrl}config/expo");
Map<String, String> headers = {
"Content-type": "application/json",
"x-access-token": token
};

final response = await http.get(url, headers: headers);

if (response.statusCode == 200) {
return ExpoConfig.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load expo configuration');
}
}

bool _canSubmitTableNumber(ExpoConfig config) {
final now = DateTime.now();
return now.isBefore(config.expoStartTime);
}
4 changes: 3 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import 'package:thdapp/providers/check_in_items_provider.dart';
import 'package:thdapp/providers/user_info_provider.dart';
import 'pages/login.dart';
import 'theme_changer.dart';
import 'package:thdapp/providers/expo_config_provider.dart';

void main() => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CheckInItemsModel()),
ChangeNotifierProvider(create: (_) => ThemeChanger(lightTheme)),
ChangeNotifierProvider(create: (context) => UserInfoModel())
ChangeNotifierProvider(create: (context) => UserInfoModel()),
ChangeNotifierProvider(create: (_) => ExpoConfigProvider()),
],
child: MyApp()
)
Expand Down
16 changes: 16 additions & 0 deletions lib/models/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class ExpoConfig {
final DateTime expoStartTime;
final DateTime submissionDeadline;

ExpoConfig({
required this.expoStartTime,
required this.submissionDeadline,
});

factory ExpoConfig.fromJson(Map<String, dynamic> json) {
return ExpoConfig(
expoStartTime: DateTime.parse(json['expoStartTime']),
submissionDeadline: DateTime.parse(json['submissionDeadline']),
);
}
}
7 changes: 5 additions & 2 deletions lib/models/project.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Project{
final String team; //objectid
final List prizes; //objectid
final bool presentingVirtually;
final int? tableNumber;

Project({
required this.id,
Expand All @@ -20,7 +21,8 @@ class Project{
required this.video,
required this.team,
required this.prizes,
required this.presentingVirtually
required this.presentingVirtually,
this.tableNumber,
});

factory Project.fromJson(Map<String, dynamic> parsedJson) {
Expand All @@ -34,7 +36,8 @@ class Project{
video: parsedJson['video'],
team: parsedJson['team'],
prizes: parsedJson['prizes'],
presentingVirtually: parsedJson['presentingVirtually']
presentingVirtually: parsedJson['presentingVirtually'],
tableNumber: parsedJson['tableNumber'],
);
return project;
}
Expand Down
118 changes: 118 additions & 0 deletions lib/pages/admin/manage_tables.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'package:thdapp/components/DefaultPage.dart';
import 'package:thdapp/components/buttons/GradBox.dart';
import 'package:thdapp/components/ErrorDialog.dart';
import 'package:thdapp/api.dart';
import 'package:thdapp/models/project.dart';
import 'package:thdapp/models/config.dart';
import 'package:provider/provider.dart';
import 'package:thdapp/providers/user_info_provider.dart';

class ManageTablesPage extends StatefulWidget {
@override
_ManageTablesPageState createState() => _ManageTablesPageState();
}

class _ManageTablesPageState extends State<ManageTablesPage> {
List<Project> projects = [];
bool isLoading = true;
ExpoConfig? expoConfig;

@override
void initState() {
super.initState();
_loadData();
}

Future<void> _loadData() async {
setState(() => isLoading = true);
try {
final token = Provider.of<UserInfoModel>(context, listen: false).token;
projects = await getAllProjects(token);
expoConfig = await getExpoConfig(token);
} catch (e) {
errorDialog(context, "Error", "Failed to load data");
} finally {
setState(() => isLoading = false);
}
}

Widget _buildExpoStatus() {
if (expoConfig == null) return const SizedBox.shrink();

final now = DateTime.now();
final isBeforeExpo = now.isBefore(expoConfig!.expoStartTime);

return Container(
padding: const EdgeInsets.all(8),
color: isBeforeExpo ? Colors.green.withOpacity(0.2) : Colors.orange.withOpacity(0.2),
child: Text(
isBeforeExpo
? "Participants can still submit table numbers"
: "Expo has started - Only admin can modify tables",
style: Theme.of(context).textTheme.bodyMedium,
),
);
}

Future<void> _updateTableNumber(Project project, String newTableNumber) async {
try {
int? tableNum = int.tryParse(newTableNumber);
if (tableNum != null) {
// TODO: Add API call to update table number
// await updateProjectTableNumber(project.id, tableNum);
await _loadData(); // Refresh the list
}
} catch (e) {
errorDialog(context, "Error", "Failed to update table number");
}
}

@override
Widget build(BuildContext context) {
return DefaultPage(
backflag: true,
child: Container(
padding: const EdgeInsets.all(16),
child: GradBox(
child: Column(
children: [
Text(
"Manage Project Tables",
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 16),
_buildExpoStatus(),
const SizedBox(height: 16),
if (isLoading)
const CircularProgressIndicator()
else
Expanded(
child: ListView.builder(
itemCount: projects.length,
itemBuilder: (context, index) {
final project = projects[index];
return ListTile(
title: Text(project.name),
subtitle: Text("Current Table: ${project.tableNumber ?? 'Not assigned'}"),
trailing: SizedBox(
width: 100,
child: TextField(
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: "Table #",
),
onSubmitted: (value) => _updateTableNumber(project, value),
),
),
);
},
),
),
],
),
),
),
);
}
}
Loading

0 comments on commit cf0b06e

Please sign in to comment.