Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions client/lib/generated/api/settings.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
$core.String? timezone,
$core.String? primaryColor,
$core.String? secondaryColor,
$core.bool? leaderboardShowOvertime,
$core.Iterable<$1.TeamMemberType>? leaderboardMemberTypes,
}) {
final result = create();
if (nextSessionThresholdSecs != null)
Expand Down Expand Up @@ -171,6 +173,10 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
if (timezone != null) result.timezone = timezone;
if (primaryColor != null) result.primaryColor = primaryColor;
if (secondaryColor != null) result.secondaryColor = secondaryColor;
if (leaderboardShowOvertime != null)
result.leaderboardShowOvertime = leaderboardShowOvertime;
if (leaderboardMemberTypes != null)
result.leaderboardMemberTypes.addAll(leaderboardMemberTypes);
return result;
}

Expand Down Expand Up @@ -207,6 +213,12 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
..aOS(18, _omitFieldNames ? '' : 'timezone')
..aOS(19, _omitFieldNames ? '' : 'primaryColor')
..aOS(20, _omitFieldNames ? '' : 'secondaryColor')
..aOB(21, _omitFieldNames ? '' : 'leaderboardShowOvertime')
..pc<$1.TeamMemberType>(
22, _omitFieldNames ? '' : 'leaderboardMemberTypes', $pb.PbFieldType.KE,
valueOf: $1.TeamMemberType.valueOf,
enumValues: $1.TeamMemberType.values,
defaultEnumValue: $1.TeamMemberType.STUDENT)
..hasRequiredFields = false;

@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
Expand Down Expand Up @@ -409,6 +421,18 @@ class UpdateSettingsRequest extends $pb.GeneratedMessage {
$core.bool hasSecondaryColor() => $_has(19);
@$pb.TagNumber(20)
void clearSecondaryColor() => $_clearField(20);

@$pb.TagNumber(21)
$core.bool get leaderboardShowOvertime => $_getBF(20);
@$pb.TagNumber(21)
set leaderboardShowOvertime($core.bool value) => $_setBool(20, value);
@$pb.TagNumber(21)
$core.bool hasLeaderboardShowOvertime() => $_has(20);
@$pb.TagNumber(21)
void clearLeaderboardShowOvertime() => $_clearField(21);

@$pb.TagNumber(22)
$pb.PbList<$1.TeamMemberType> get leaderboardMemberTypes => $_getList(21);
}

class UpdateSettingsResponse extends $pb.GeneratedMessage {
Expand Down
19 changes: 18 additions & 1 deletion client/lib/generated/api/settings.pbjson.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,21 @@ const UpdateSettingsRequest$json = {
{'1': 'timezone', '3': 18, '4': 1, '5': 9, '10': 'timezone'},
{'1': 'primary_color', '3': 19, '4': 1, '5': 9, '10': 'primaryColor'},
{'1': 'secondary_color', '3': 20, '4': 1, '5': 9, '10': 'secondaryColor'},
{
'1': 'leaderboard_show_overtime',
'3': 21,
'4': 1,
'5': 8,
'10': 'leaderboardShowOvertime'
},
{
'1': 'leaderboard_member_types',
'3': 22,
'4': 3,
'5': 14,
'6': '.tk.db.TeamMemberType',
'10': 'leaderboardMemberTypes'
},
],
};

Expand All @@ -177,7 +192,9 @@ final $typed_data.Uint8List updateSettingsRequestDescriptor = $convert.base64Dec
'NoZWNrb3V0RG1NZXNzYWdlEjgKGGRpc2NvcmRfY2hlY2tvdXRfZW5hYmxlZBgQIAEoCFIWZGlz'
'Y29yZENoZWNrb3V0RW5hYmxlZBIaCgh0aW1lem9uZRgSIAEoCVIIdGltZXpvbmUSIwoNcHJpbW'
'FyeV9jb2xvchgTIAEoCVIMcHJpbWFyeUNvbG9yEicKD3NlY29uZGFyeV9jb2xvchgUIAEoCVIO'
'c2Vjb25kYXJ5Q29sb3I=');
'c2Vjb25kYXJ5Q29sb3ISOgoZbGVhZGVyYm9hcmRfc2hvd19vdmVydGltZRgVIAEoCFIXbGVhZG'
'VyYm9hcmRTaG93T3ZlcnRpbWUSTwoYbGVhZGVyYm9hcmRfbWVtYmVyX3R5cGVzGBYgAygOMhUu'
'dGsuZGIuVGVhbU1lbWJlclR5cGVSFmxlYWRlcmJvYXJkTWVtYmVyVHlwZXM=');

@$core.Deprecated('Use updateSettingsResponseDescriptor instead')
const UpdateSettingsResponse$json = {
Expand Down
24 changes: 24 additions & 0 deletions client/lib/generated/db/db.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,8 @@ class Settings extends $pb.GeneratedMessage {
$core.String? timezone,
$core.String? primaryColor,
$core.String? secondaryColor,
$core.bool? leaderboardShowOvertime,
$core.Iterable<TeamMemberType>? leaderboardMemberTypes,
}) {
final result = create();
if (nextSessionThresholdSecs != null)
Expand Down Expand Up @@ -775,6 +777,10 @@ class Settings extends $pb.GeneratedMessage {
if (timezone != null) result.timezone = timezone;
if (primaryColor != null) result.primaryColor = primaryColor;
if (secondaryColor != null) result.secondaryColor = secondaryColor;
if (leaderboardShowOvertime != null)
result.leaderboardShowOvertime = leaderboardShowOvertime;
if (leaderboardMemberTypes != null)
result.leaderboardMemberTypes.addAll(leaderboardMemberTypes);
return result;
}

Expand Down Expand Up @@ -811,6 +817,12 @@ class Settings extends $pb.GeneratedMessage {
..aOS(18, _omitFieldNames ? '' : 'timezone')
..aOS(19, _omitFieldNames ? '' : 'primaryColor')
..aOS(20, _omitFieldNames ? '' : 'secondaryColor')
..aOB(21, _omitFieldNames ? '' : 'leaderboardShowOvertime')
..pc<TeamMemberType>(
22, _omitFieldNames ? '' : 'leaderboardMemberTypes', $pb.PbFieldType.KE,
valueOf: TeamMemberType.valueOf,
enumValues: TeamMemberType.values,
defaultEnumValue: TeamMemberType.STUDENT)
..hasRequiredFields = false;

@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
Expand Down Expand Up @@ -1011,6 +1023,18 @@ class Settings extends $pb.GeneratedMessage {
$core.bool hasSecondaryColor() => $_has(19);
@$pb.TagNumber(20)
void clearSecondaryColor() => $_clearField(20);

@$pb.TagNumber(21)
$core.bool get leaderboardShowOvertime => $_getBF(20);
@$pb.TagNumber(21)
set leaderboardShowOvertime($core.bool value) => $_setBool(20, value);
@$pb.TagNumber(21)
$core.bool hasLeaderboardShowOvertime() => $_has(20);
@$pb.TagNumber(21)
void clearLeaderboardShowOvertime() => $_clearField(21);

@$pb.TagNumber(22)
$pb.PbList<TeamMemberType> get leaderboardMemberTypes => $_getList(21);
}

const $core.bool _omitFieldNames =
Expand Down
19 changes: 18 additions & 1 deletion client/lib/generated/db/db.pbjson.dart
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,21 @@ const Settings$json = {
{'1': 'timezone', '3': 18, '4': 1, '5': 9, '10': 'timezone'},
{'1': 'primary_color', '3': 19, '4': 1, '5': 9, '10': 'primaryColor'},
{'1': 'secondary_color', '3': 20, '4': 1, '5': 9, '10': 'secondaryColor'},
{
'1': 'leaderboard_show_overtime',
'3': 21,
'4': 1,
'5': 8,
'10': 'leaderboardShowOvertime'
},
{
'1': 'leaderboard_member_types',
'3': 22,
'4': 3,
'5': 14,
'6': '.tk.db.TeamMemberType',
'10': 'leaderboardMemberTypes'
},
],
};

Expand All @@ -413,4 +428,6 @@ final $typed_data.Uint8List settingsDescriptor = $convert.base64Decode(
'lzY29yZENoZWNrb3V0RW5hYmxlZBInCg9kaXNjb3JkX2VuYWJsZWQYESABKAhSDmRpc2NvcmRF'
'bmFibGVkEhoKCHRpbWV6b25lGBIgASgJUgh0aW1lem9uZRIjCg1wcmltYXJ5X2NvbG9yGBMgAS'
'gJUgxwcmltYXJ5Q29sb3ISJwoPc2Vjb25kYXJ5X2NvbG9yGBQgASgJUg5zZWNvbmRhcnlDb2xv'
'cg==');
'chI6ChlsZWFkZXJib2FyZF9zaG93X292ZXJ0aW1lGBUgASgIUhdsZWFkZXJib2FyZFNob3dPdm'
'VydGltZRJPChhsZWFkZXJib2FyZF9tZW1iZXJfdHlwZXMYFiADKA4yFS50ay5kYi5UZWFtTWVt'
'YmVyVHlwZVIWbGVhZGVyYm9hcmRNZW1iZXJUeXBlcw==');
2 changes: 2 additions & 0 deletions client/lib/helpers/settings_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Future<GrpcResult<UpdateSettingsResponse>> updateSettings(
discordCheckoutEnabled: s.discordCheckoutEnabled,
primaryColor: s.primaryColor,
secondaryColor: s.secondaryColor,
leaderboardShowOvertime: s.leaderboardShowOvertime,
leaderboardMemberTypes: s.leaderboardMemberTypes,
);

applyChanges(req);
Expand Down
2 changes: 1 addition & 1 deletion client/lib/views/kiosk/checked_in_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class CheckedInList extends ConsumerWidget {
padding: const EdgeInsetsGeometry.symmetric(horizontal: 40),
child: Column(
children: [
SizedBox(height: childHeight, child: TeamMemberHeader()),
const TeamMemberHeader(),
Expanded(
child: AnimatedInfiniteVerticalList(
childHeight: childHeight,
Expand Down
8 changes: 4 additions & 4 deletions client/lib/views/kiosk/team_member_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class TeamMemberHeader extends StatelessWidget {
Expanded(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 10),
child: Text(
'Team Member',
style: TextStyle(
Expand All @@ -31,7 +31,7 @@ class TeamMemberHeader extends StatelessWidget {
Expanded(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 10),
child: Text(
'Type',
style: TextStyle(
Expand All @@ -45,7 +45,7 @@ class TeamMemberHeader extends StatelessWidget {
Expanded(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 10),
child: Text(
'Location',
style: TextStyle(
Expand All @@ -59,7 +59,7 @@ class TeamMemberHeader extends StatelessWidget {
Expanded(
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 10),
child: Text(
'Time In',
style: TextStyle(
Expand Down
124 changes: 124 additions & 0 deletions client/lib/views/setup/member_setup.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,70 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:time_keeper/generated/api/settings.pbgrpc.dart';
import 'package:time_keeper/generated/api/team_member.pbgrpc.dart';
import 'package:time_keeper/generated/api/team_member_session.pbgrpc.dart';
import 'package:time_keeper/generated/db/db.pbenum.dart';
import 'package:time_keeper/helpers/grpc_call_wrapper.dart';
import 'package:time_keeper/helpers/settings_helper.dart' as settings_helper;
import 'package:time_keeper/providers/settings_provider.dart';
import 'package:time_keeper/providers/team_member_provider.dart';
import 'package:time_keeper/providers/team_member_session_provider.dart';
import 'package:time_keeper/utils/grpc_result.dart';
import 'package:time_keeper/views/setup/common/file_upload_setting.dart';
import 'package:time_keeper/views/setup/common/setting_row.dart';
import 'package:time_keeper/views/setup/common/settings_page_layout.dart';
import 'package:time_keeper/widgets/dialogs/confirm_dialog.dart';
import 'package:time_keeper/widgets/dialogs/popup_dialog.dart';

class MemberSetupTab extends HookConsumerWidget {
const MemberSetupTab({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final showOvertime = useState(true);
final selectedMemberTypes = useState<Set<TeamMemberType>>(
Set.from(TeamMemberType.values),
);

// Load current settings on mount
useEffect(() {
Future<void> loadSettings() async {
final result = await callGrpcEndpoint(
() => ref
.read(settingsServiceProvider)
.getSettings(GetSettingsRequest()),
);
if (result is GrpcSuccess<GetSettingsResponse>) {
final s = result.data.settings;
showOvertime.value = s.leaderboardShowOvertime;
if (s.leaderboardMemberTypes.isNotEmpty) {
selectedMemberTypes.value = Set.from(s.leaderboardMemberTypes);
}
}
}

loadSettings();
return null;
}, const []);

Future<void> updateLeaderboardSettings() async {
final res = await settings_helper.updateSettings(
ref.read(settingsServiceProvider),
(req) {
req
..leaderboardShowOvertime = showOvertime.value
..leaderboardMemberTypes.clear();
req.leaderboardMemberTypes.addAll(selectedMemberTypes.value);
},
);

if (context.mounted) {
PopupDialog.fromGrpcStatus(result: res).show(context);
}
}

Future<GrpcResult<UploadStudentCsvResponse>> uploadStudentCsv(
Uint8List bytes,
) async {
Expand All @@ -34,6 +83,17 @@ class MemberSetupTab extends HookConsumerWidget {
);
}

String memberTypeName(TeamMemberType type) {
switch (type) {
case TeamMemberType.STUDENT:
return 'Students';
case TeamMemberType.MENTOR:
return 'Mentors';
default:
return type.name;
}
}

return SettingsPageLayout(
title: 'Team Member Setup',
subtitle: 'Configure your team settings',
Expand Down Expand Up @@ -111,6 +171,70 @@ class MemberSetupTab extends HookConsumerWidget {
}
},
),
const SizedBox(height: 32),
const Divider(),
const SizedBox(height: 16),
Text(
'Leaderboard Configuration',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 4),
Text(
'Configure how the leaderboard calculates and displays member rankings. '
'These settings also apply to the Discord !leaderboard command.',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 16),
SettingRow(
label: 'Show Overtime Separately',
description:
'When enabled, overtime is displayed as a separate value on the leaderboard. '
'When disabled, overtime is combined into the total hours.',
child: Row(
children: [
Switch(
value: showOvertime.value,
onChanged: (value) => showOvertime.value = value,
),
const SizedBox(width: 8),
Text(showOvertime.value ? 'Enabled' : 'Disabled'),
],
),
),
const SizedBox(height: 24),
SettingRow(
label: 'Member Types on Leaderboard',
description: 'Select which member types appear on the leaderboard',
child: Wrap(
spacing: 8,
children: TeamMemberType.values.map((type) {
final isSelected = selectedMemberTypes.value.contains(type);
return FilterChip(
label: Text(memberTypeName(type)),
selected: isSelected,
onSelected: (selected) {
final updated = Set<TeamMemberType>.from(
selectedMemberTypes.value,
);
if (selected) {
updated.add(type);
} else {
updated.remove(type);
}
selectedMemberTypes.value = updated;
},
);
}).toList(),
),
),
const SizedBox(height: 24),
FilledButton.icon(
onPressed: updateLeaderboardSettings,
icon: const Icon(Icons.save),
label: const Text('Update Leaderboard Settings'),
),
],
);
}
Expand Down
2 changes: 2 additions & 0 deletions protos/api/settings.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ message UpdateSettingsRequest {
string timezone = 18;
string primary_color = 19;
string secondary_color = 20;
bool leaderboard_show_overtime = 21;
repeated db.TeamMemberType leaderboard_member_types = 22;
}
message UpdateSettingsResponse {}

Expand Down
2 changes: 2 additions & 0 deletions protos/db/db.proto
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,6 @@ message Settings {
string timezone = 18;
string primary_color = 19;
string secondary_color = 20;
bool leaderboard_show_overtime = 21;
repeated TeamMemberType leaderboard_member_types = 22;
}
Loading
Loading