Initial commit: Flutter app + PHP/MySQL backend on Hostinger

Replaces Firebase with a self-hosted PHP/MySQL API served from
winded.prymsolutions.com. Includes full backend (schema, auth, events,
teams, brackets, suggestions, stats, media, file upload) and updated
Flutter repositories and domain models.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 20:13:57 -07:00
commit b239ae3e5f
208 changed files with 19187 additions and 0 deletions
@@ -0,0 +1,57 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../brackets/domain/bracket.dart';
import '../../brackets/infrastructure/brackets_repository.dart';
part 'admin_brackets_notifier.g.dart';
/// Live Firestore-backed stream of every bracket, used by the admin panel.
@riverpod
Stream<List<Bracket>> adminBracketsStream(AdminBracketsStreamRef ref) {
final repo = ref.watch(bracketsRepositoryProvider);
return repo.watchBrackets();
}
/// Imperative wrapper around the brackets repository write methods.
@riverpod
class AdminBracketsNotifier extends _$AdminBracketsNotifier {
@override
Future<void> build() async {}
Future<String> create(Bracket bracket) async {
final repo = ref.read(bracketsRepositoryProvider);
state = const AsyncLoading();
try {
final id = await repo.createBracket(bracket);
state = const AsyncData(null);
return id;
} catch (e, st) {
state = AsyncError(e, st);
rethrow;
}
}
Future<void> save(Bracket bracket) async {
final repo = ref.read(bracketsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.updateBracket(bracket));
}
Future<void> delete(String id) async {
final repo = ref.read(bracketsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.deleteBracket(id));
}
Future<void> updateMatch(
String bracketId,
String roundLabel,
BracketMatch match,
) async {
final repo = ref.read(bracketsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(
() => repo.updateMatch(bracketId, roundLabel, match),
);
}
}
@@ -0,0 +1,50 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'admin_brackets_notifier.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$adminBracketsStreamHash() =>
r'2a76ca85dc76fc7514b7b9ae17a5610f1c1760d9';
/// Live Firestore-backed stream of every bracket, used by the admin panel.
///
/// Copied from [adminBracketsStream].
@ProviderFor(adminBracketsStream)
final adminBracketsStreamProvider =
AutoDisposeStreamProvider<List<Bracket>>.internal(
adminBracketsStream,
name: r'adminBracketsStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminBracketsStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AdminBracketsStreamRef = AutoDisposeStreamProviderRef<List<Bracket>>;
String _$adminBracketsNotifierHash() =>
r'ac2ba11f3c44e7feccf440538249e078c9a55031';
/// Imperative wrapper around the brackets repository write methods.
///
/// Copied from [AdminBracketsNotifier].
@ProviderFor(AdminBracketsNotifier)
final adminBracketsNotifierProvider =
AutoDisposeAsyncNotifierProvider<AdminBracketsNotifier, void>.internal(
AdminBracketsNotifier.new,
name: r'adminBracketsNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminBracketsNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AdminBracketsNotifier = AutoDisposeAsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -0,0 +1,49 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../events/domain/event.dart';
import '../../events/infrastructure/events_repository.dart';
part 'admin_events_notifier.g.dart';
/// Live Firestore-backed stream of every event in the system, used by the
/// admin panel. The public-facing [eventsStreamProvider] still emits mocked
/// data; admins read straight through to the real collection.
@riverpod
Stream<List<Event>> adminEventsStream(AdminEventsStreamRef ref) {
final repo = ref.watch(eventsRepositoryProvider);
return repo.watchEvents();
}
/// Imperative wrapper around the events repository write methods. The notifier
/// is `AsyncValue<void>`-shaped so screens can wire it up the same way as the
/// existing auth/suggestions notifiers.
@riverpod
class AdminEventsNotifier extends _$AdminEventsNotifier {
@override
Future<void> build() async {}
Future<String> create(Event event) async {
final repo = ref.read(eventsRepositoryProvider);
state = const AsyncLoading();
try {
final id = await repo.createEvent(event);
state = const AsyncData(null);
return id;
} catch (e, st) {
state = AsyncError(e, st);
rethrow;
}
}
Future<void> save(Event event) async {
final repo = ref.read(eventsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.updateEvent(event));
}
Future<void> delete(String id) async {
final repo = ref.read(eventsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.deleteEvent(id));
}
}
@@ -0,0 +1,53 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'admin_events_notifier.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$adminEventsStreamHash() => r'33d6cd9ec02f788540270db08f0933e9c46c72e8';
/// Live Firestore-backed stream of every event in the system, used by the
/// admin panel. The public-facing [eventsStreamProvider] still emits mocked
/// data; admins read straight through to the real collection.
///
/// Copied from [adminEventsStream].
@ProviderFor(adminEventsStream)
final adminEventsStreamProvider =
AutoDisposeStreamProvider<List<Event>>.internal(
adminEventsStream,
name: r'adminEventsStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminEventsStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AdminEventsStreamRef = AutoDisposeStreamProviderRef<List<Event>>;
String _$adminEventsNotifierHash() =>
r'd39031c4b14120bba5d4ea0baeed2661eb336ec0';
/// Imperative wrapper around the events repository write methods. The notifier
/// is `AsyncValue<void>`-shaped so screens can wire it up the same way as the
/// existing auth/suggestions notifiers.
///
/// Copied from [AdminEventsNotifier].
@ProviderFor(AdminEventsNotifier)
final adminEventsNotifierProvider =
AutoDisposeAsyncNotifierProvider<AdminEventsNotifier, void>.internal(
AdminEventsNotifier.new,
name: r'adminEventsNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminEventsNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AdminEventsNotifier = AutoDisposeAsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -0,0 +1,35 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../suggestions/domain/suggestion.dart';
import '../../suggestions/infrastructure/suggestions_repository.dart';
part 'admin_suggestions_notifier.g.dart';
/// Live Firestore-backed stream of every suggestion, newest first, for the
/// admin review dashboard.
@riverpod
Stream<List<Suggestion>> adminSuggestionsStream(
AdminSuggestionsStreamRef ref,
) {
final repo = ref.watch(suggestionsRepositoryProvider);
return repo.watchAllSuggestions();
}
/// Imperative wrapper around the suggestion write methods.
@riverpod
class AdminSuggestionsNotifier extends _$AdminSuggestionsNotifier {
@override
Future<void> build() async {}
Future<void> updateStatus(String id, SuggestionStatus status) async {
final repo = ref.read(suggestionsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.updateStatus(id, status));
}
Future<void> delete(String id) async {
final repo = ref.read(suggestionsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.deleteSuggestion(id));
}
}
@@ -0,0 +1,52 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'admin_suggestions_notifier.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$adminSuggestionsStreamHash() =>
r'e87ca116c64b03bf5e62df4c390ff5c3dcfb4e0a';
/// Live Firestore-backed stream of every suggestion, newest first, for the
/// admin review dashboard.
///
/// Copied from [adminSuggestionsStream].
@ProviderFor(adminSuggestionsStream)
final adminSuggestionsStreamProvider =
AutoDisposeStreamProvider<List<Suggestion>>.internal(
adminSuggestionsStream,
name: r'adminSuggestionsStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminSuggestionsStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AdminSuggestionsStreamRef =
AutoDisposeStreamProviderRef<List<Suggestion>>;
String _$adminSuggestionsNotifierHash() =>
r'fd85d538be1e2d9abad02812d9c964c2df2b547a';
/// Imperative wrapper around the suggestion write methods.
///
/// Copied from [AdminSuggestionsNotifier].
@ProviderFor(AdminSuggestionsNotifier)
final adminSuggestionsNotifierProvider =
AutoDisposeAsyncNotifierProvider<AdminSuggestionsNotifier, void>.internal(
AdminSuggestionsNotifier.new,
name: r'adminSuggestionsNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminSuggestionsNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AdminSuggestionsNotifier = AutoDisposeAsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -0,0 +1,46 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../teams/domain/team.dart';
import '../../teams/infrastructure/teams_repository.dart';
part 'admin_teams_notifier.g.dart';
/// Live Firestore-backed stream of every team (including pending and
/// rejected), used by the admin panel.
@riverpod
Stream<List<Team>> adminTeamsStream(AdminTeamsStreamRef ref) {
final repo = ref.watch(teamsRepositoryProvider);
return repo.adminWatchAllTeams();
}
/// Imperative wrapper around the teams repository write methods.
@riverpod
class AdminTeamsNotifier extends _$AdminTeamsNotifier {
@override
Future<void> build() async {}
Future<String> create(Team team) async {
final repo = ref.read(teamsRepositoryProvider);
state = const AsyncLoading();
try {
final id = await repo.createTeam(team);
state = const AsyncData(null);
return id;
} catch (e, st) {
state = AsyncError(e, st);
rethrow;
}
}
Future<void> save(Team team) async {
final repo = ref.read(teamsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.updateTeam(team));
}
Future<void> delete(String id) async {
final repo = ref.read(teamsRepositoryProvider);
state = const AsyncLoading();
state = await AsyncValue.guard(() => repo.deleteTeam(id));
}
}
@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'admin_teams_notifier.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$adminTeamsStreamHash() => r'f392e2c9de281c80912d4fccfaf56c0cbe8ef880';
/// Live Firestore-backed stream of every team (including pending and
/// rejected), used by the admin panel.
///
/// Copied from [adminTeamsStream].
@ProviderFor(adminTeamsStream)
final adminTeamsStreamProvider = AutoDisposeStreamProvider<List<Team>>.internal(
adminTeamsStream,
name: r'adminTeamsStreamProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminTeamsStreamHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AdminTeamsStreamRef = AutoDisposeStreamProviderRef<List<Team>>;
String _$adminTeamsNotifierHash() =>
r'1f5febaa0f2eb35596538db76896c96dd240a1d8';
/// Imperative wrapper around the teams repository write methods.
///
/// Copied from [AdminTeamsNotifier].
@ProviderFor(AdminTeamsNotifier)
final adminTeamsNotifierProvider =
AutoDisposeAsyncNotifierProvider<AdminTeamsNotifier, void>.internal(
AdminTeamsNotifier.new,
name: r'adminTeamsNotifierProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$adminTeamsNotifierHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AdminTeamsNotifier = AutoDisposeAsyncNotifier<void>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package