Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add cluster info section #135

Merged
merged 4 commits into from
Oct 23, 2024
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
46 changes: 46 additions & 0 deletions src/support_sphere/lib/data/models/clusters.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:equatable/equatable.dart';
import 'package:support_sphere/data/models/person.dart';

class Cluster extends Equatable {

const Cluster({
required this.id,
this.name = '',
this.meetingPlace = '',
this.captains,
});

/// The current user's id, which matches the auth user id
final String id;
final String? name;
final String? meetingPlace;
final Captains? captains;

@override
List<Object?> get props => [id, name, meetingPlace, captains];

copyWith({
String? id,
String? name,
String? meetingPlace,
Captains? captains,
}) {
return Cluster(
id: id ?? this.id,
name: name ?? this.name,
meetingPlace: meetingPlace ?? this.meetingPlace,
captains: captains ?? this.captains,
);
}
}

class Captains extends Equatable {
const Captains({
this.people = const [],
});

final List<Person?> people;

@override
List<Object?> get props => [people];
}
6 changes: 5 additions & 1 deletion src/support_sphere/lib/data/models/households.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Household extends Equatable {

const Household({
required this.id,
required this.cluster_id,
this.name = '',
this.address = '',
this.notes = '',
Expand All @@ -15,6 +16,7 @@ class Household extends Equatable {

/// The current user's id, which matches the auth user id
final String id;
final String cluster_id;
final String? name;
final String? address;
final String? notes;
Expand All @@ -23,7 +25,7 @@ class Household extends Equatable {
final HouseHoldMembers? houseHoldMembers;

@override
List<Object?> get props => [id, name, address, notes, pets, accessibility_needs, houseHoldMembers];
List<Object?> get props => [id, name, address, notes, pets, accessibility_needs, houseHoldMembers, cluster_id];

copyWith({
String? id,
Expand All @@ -33,6 +35,7 @@ class Household extends Equatable {
String? pets,
String? accessibility_needs,
HouseHoldMembers? houseHoldMembers,
String? cluster_id,
}) {
return Household(
id: id ?? this.id,
Expand All @@ -42,6 +45,7 @@ class Household extends Equatable {
pets: pets ?? this.pets,
accessibility_needs: accessibility_needs ?? this.accessibility_needs,
houseHoldMembers: houseHoldMembers ?? this.houseHoldMembers,
cluster_id: cluster_id ?? this.cluster_id,
);
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/support_sphere/lib/data/repositories/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import 'dart:async';

import 'package:supabase_flutter/supabase_flutter.dart' as supabase_flutter;
import 'package:support_sphere/data/models/auth_user.dart';
import 'package:support_sphere/data/models/clusters.dart';
import 'package:support_sphere/data/models/households.dart';
import 'package:support_sphere/data/models/person.dart';
import 'package:support_sphere/data/services/cluster_service.dart';
import 'package:support_sphere/data/services/user_service.dart';

/// Repository for user interactions.
/// This class is responsible for handling user-related data operations.
class UserRepository {
final UserService _userService = UserService();
final ClusterService _clusterService = ClusterService();

/// Get the household members by household id.
/// Returns a [HouseHoldMembers] object if the household members exist.
Expand Down Expand Up @@ -60,6 +63,7 @@ class UserRepository {
notes: householdData["notes"],
pets: householdData["pets"],
accessibility_needs: householdData["accessibility_needs"],
cluster_id: householdData["cluster_id"],
);
}
return null;
Expand Down Expand Up @@ -88,6 +92,46 @@ class UserRepository {
return null;
}

/// Get the cluster by cluster id retrieved from [Household].
/// Returns a [Cluster] object
Future<Cluster?> getClusterById({
required String clusterId,
}) async {
final data = await _clusterService.getClusterById(clusterId);

if (data != null) {
return Cluster(
id: data["id"],
name: data["name"],
meetingPlace: data["meeting_place"],
);
}
return null;
}

Future<Captains?> getCaptainsByClusterId({
required String clusterId,
}) async {
final data = await _clusterService.getCaptainsByClusterId(clusterId);

if (data != null) {
List<Person> captains = [];

for (var record in data) {
Map<String, dynamic> captainData = record["captain"]["user_profile"]["person"];

captains.add(Person(
id: captainData["id"],
givenName: captainData["given_name"],
familyName: captainData["family_name"],
));
}

return Captains(people: captains);
}
return null;
}


/// Create a new user with the given user info.
/// This will perform two operations:
Expand Down
36 changes: 36 additions & 0 deletions src/support_sphere/lib/data/services/cluster_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:support_sphere/utils/supabase.dart';
import 'package:uuid/v4.dart';

class ClusterService {
final SupabaseClient _supabaseClient = supabase;

/// Retrieves the cluster by cluster id.
/// Returns a [Cluster] object if the cluster exist.
/// Returns null if the cluster does not exist.
Future<PostgrestMap?> getClusterById(String clusterId) async {
/// This query will perform a join on the user_profiles and people tables
return await _supabaseClient.from('clusters').select('''
id,
name,
meeting_place
''').eq('id', clusterId).maybeSingle();
}

Future<PostgrestList?> getCaptainsByClusterId(String clusterId) async {
return await _supabaseClient.from('user_captain_clusters')
.select('''
captain:user_roles (
user_profile:user_profiles (
person:people (
id,
given_name,
family_name
)
)
)
''')
.eq('cluster_id', clusterId)
.eq('user_roles.role', 'SUBCOM_AGENT');
}
}
3 changes: 2 additions & 1 deletion src/support_sphere/lib/data/services/user_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class UserService {
address,
notes,
pets,
accessibility_needs
accessibility_needs,
cluster_id
)
''').eq('people_id', personId).maybeSingle();
}
Expand Down
24 changes: 24 additions & 0 deletions src/support_sphere/lib/logic/cubit/profile_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:support_sphere/data/models/auth_user.dart';
import 'package:support_sphere/data/models/clusters.dart';
import 'package:support_sphere/data/models/households.dart';
import 'package:support_sphere/data/models/person.dart';
import 'package:support_sphere/data/repositories/user.dart';
Expand Down Expand Up @@ -28,6 +29,10 @@ class ProfileCubit extends Cubit<ProfileState> {
emit(state.copyWith(household: household));
}

void clusterChanged(Cluster? cluster) {
emit(state.copyWith(cluster: cluster));
}

Future<void> fetchProfile() async {
try {
/// Get the user profile by user id
Expand All @@ -54,9 +59,28 @@ class ProfileCubit extends Cubit<ProfileState> {
} else {
throw Exception('Household not found');
}

/// Get the cluster and its captains information
Cluster? cluster = household == null
? null
: await _userRepository.getClusterById(clusterId: household.cluster_id);

if (cluster != null) {
/// Get the captains of the cluster
final Captains? captains = await _userRepository.getCaptainsByClusterId(clusterId: cluster.id);

if (captains != null) {
cluster = cluster.copyWith(captains: captains);
}
clusterChanged(cluster);
} else {
throw Exception('Cluster not found');
}
} catch (_) {
/// TODO: Handle error if there are no user profile or household for some reason
profileChanged(null);
householdChanged(null);
clusterChanged(null);
}
}
}
6 changes: 5 additions & 1 deletion src/support_sphere/lib/logic/cubit/profile_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@ class ProfileState extends Equatable {
this.userProfile,
this.household,
this.authUser,
this.cluster,
});

final Person? userProfile;
final AuthUser? authUser;
final Household? household;
final Cluster? cluster;

@override
List<Object?> get props => [userProfile, authUser, household];
List<Object?> get props => [userProfile, authUser, household, cluster];

ProfileState copyWith({
Person? userProfile,
AuthUser? authUser,
Household? household,
Cluster? cluster,
}) {
return ProfileState(
userProfile: userProfile ?? this.userProfile,
authUser: authUser ?? this.authUser,
household: household ?? this.household,
cluster: cluster ?? this.cluster,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:ionicons/ionicons.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:support_sphere/data/models/auth_user.dart';
import 'package:support_sphere/data/models/clusters.dart';
import 'package:support_sphere/data/models/households.dart';
import 'package:support_sphere/data/models/person.dart';
import 'package:support_sphere/logic/bloc/auth/authentication_bloc.dart';
Expand Down Expand Up @@ -288,39 +289,53 @@ class _ClusterInformation extends StatelessWidget {

@override
Widget build(BuildContext context) {
// Cluster Information
return _ProfileSection(
title: "Cluster Information",
readOnly: true,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Name"),
Text("Cluster 1"),
],
),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Meeting place"),
Text("410 Example Street"),
],
),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
return BlocBuilder<ProfileCubit, ProfileState>(
buildWhen: (previous, current) => previous.cluster != current.cluster,
builder: (context, state) {
Cluster? cluster = state.cluster;
String name = cluster?.name ?? '';
String meetingPlace = cluster?.meetingPlace ?? '';
List<Person?> captains = cluster?.captains?.people ?? [];
List<String> captainsNames = captains.map((captain) {
String givenName = captain?.givenName ?? '';
String familyName = captain?.familyName ?? '';
String fullName = '$givenName $familyName';
return fullName;
}).toList();
return _ProfileSection(
title: "Cluster Information",
readOnly: true,
children: [
Text("Captain(s)"),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Name"),
Text(name),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Meeting place"),
Text(meetingPlace),
],
),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Captain(s)"),
],
),
Container(
height: 50.0,
child: ListView(
shrinkWrap: true,
children: captainsNames.map((n) => Text(n)).toList(),
),
),
],
),
Container(
height: 50.0,
child: ListView(shrinkWrap: true, children: const [
Text("Jane Smith"),
Text("John Smith"),
]),
),
],
);
},
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@

GRANT ALL ON TABLE public.user_roles TO supabase_auth_admin;

REVOKE ALL ON TABLE public.user_roles FROM authenticated, anon, public;

CREATE POLICY "Allow auth admin to read user roles" ON public.user_roles AS PERMISSIVE FOR SELECT TO supabase_auth_admin USING (true);

ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY;

COMMIT;
"""

Expand Down