From bcfbf303b4ad4a250dcfab46f4d184bb98e9784e Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Mon, 25 Sep 2023 22:15:47 -0500 Subject: [PATCH] select members based on year --- src/api/attendance.ts | 44 ++++++++++++++++--- src/api/members.ts | 34 +++++++++++--- src/api/years.ts | 43 ++++++++++++++++++ src/components/YearPicker.tsx | 3 +- .../admin/AttendanceForSeasonByMember.tsx | 7 ++- src/pages/members/AttendancePage.tsx | 4 +- src/types/Api.ts | 30 +++++++++---- 7 files changed, 140 insertions(+), 25 deletions(-) create mode 100644 src/api/years.ts diff --git a/src/api/attendance.ts b/src/api/attendance.ts index fe7a2d3..3655e79 100644 --- a/src/api/attendance.ts +++ b/src/api/attendance.ts @@ -1,8 +1,31 @@ import { isBefore } from 'date-fns' import { getToday, toDate, toYMD } from '../calendar/utilities' import { Attendance, AttendanceTypesType, EventAttendance, MemberAttendance } from '../types/Api' -import { getStartEndOfSeason } from '../utilities/converters' +import { getSeason, getStartEndOfSeason } from '../utilities/converters' import { supabase } from './SupabaseClient' +import { getEventById } from './events' + +type MemberYear = { + members: MemberAttendance +} + +const flattenMemberAttendance = (memberYears: MemberYear[]) => { + if (memberYears.length === 0) { + return null; + } + let memberAttendance = [] as MemberAttendance[] + memberYears.forEach((memberYear: MemberYear) => { + let member: MemberAttendance = {} + member.member_id = memberYear.members.member_id + member.first_name = memberYear.members.first_name + member.last_name = memberYear.members.last_name + member.team_role = memberYear.members.team_role + member.sub_team = memberYear.members.sub_team + member.attendance = memberYear.members.attendance + memberAttendance.push(member) + }) + return memberAttendance +} /** * Get list of events for the season and their attendance @@ -35,18 +58,25 @@ const getMemberAttendance = async (eventId: number) => { if (eventId === -1) { return [] } + const robotEvent = await getEventById(eventId); + const year = getSeason(toDate(robotEvent.event_date)) + console.log('year', year) const { data, error } = await supabase - .from('members') - .select('member_id, first_name, last_name, sub_team, team_role, attendance(*)') - .eq('attendance.event_id', eventId) - .eq('deleted', false) + .from('member_year') + .select('members(member_id, first_name, last_name, sub_team, team_role, attendance(*))') + .eq('members.attendance.event_id', eventId) + .eq('members.deleted', false) + .eq('year', year + '') if (error) throw error if (data.length === 0) { return [] } - return data as unknown as MemberAttendance[] + const flattened = flattenMemberAttendance(data as MemberYear[]) + console.log('data', data) + console.log('flattened', flattened); + return flattened } /** @@ -123,7 +153,7 @@ const getAttendanceForMember = async ({ season, memberId }) => { /** * Fetch Attendance for all members within the season (so far) - * Seaason is the year the game comes out (in January) + * Season is the year the game comes out (in January) */ const getAttendanceForAllMembers = async (season: string) => { const { startDate, endDate } = getStartEndOfSeason(season) diff --git a/src/api/members.ts b/src/api/members.ts index 94f3fac..b89f8bb 100644 --- a/src/api/members.ts +++ b/src/api/members.ts @@ -1,15 +1,39 @@ import { Member } from '../types/Api' import { supabase } from './SupabaseClient' +type MemberYear = { + members: Member +} + +const flattenMembers = (memberYears: MemberYear[]) => { + if (memberYears.length === 0) { + return null; + } + let members = [] as Member[] + memberYears.forEach((memberYear: MemberYear) => { + let member: Member = {} + member.member_id = memberYear.members.member_id + member.first_name = memberYear.members.first_name + member.last_name = memberYear.members.last_name + member.pronouns= memberYear.members.pronouns + member.team_role = memberYear.members.team_role + member.sub_team = memberYear.members.sub_team + member.email = memberYear.members.email + member.phone = memberYear.members.phone + member.food_needs = memberYear.members.food_needs + members.push(member) + }) + return members +} + const getMembers = async (year: string) => { const { data, error } = await supabase - .from('members') - .select('member_id, first_name, last_name, pronouns, team_role, sub_team, email, phone, food_needs') - .eq('deleted', false) + .from('member_year') + .select('members(member_id, first_name, last_name, pronouns, team_role, sub_team, email, phone, food_needs)') + .eq('year', year) if (error) throw error - - return data as Member[] + return flattenMembers(data as MemberYear[]) as Member[] } const getMemberByEmail = async (email: string) => { diff --git a/src/api/years.ts b/src/api/years.ts new file mode 100644 index 0000000..a5a0792 --- /dev/null +++ b/src/api/years.ts @@ -0,0 +1,43 @@ +import { Year } from '../types/Api' +import { supabase } from './SupabaseClient' + + +const getValidYears = async (year: string) => { + const { data, error } = await supabase.from('parent').select() + + if (error) throw error + + return data as Year[] +} + +const getYearById = async (year: string) => { + const { data, error } = await supabase.from('year').select().eq('year', year) + + if (error) throw error + + if (data.length === 0) { + return null + } + return data[0] as Year +} + +const saveYear = async (year: Year) => { + const { error } = await supabase.from('parent').insert({ + year: year.year, + game: year.game + }) + if (error) throw error +} + +const updateYear = async (year: Year) => { + const { error } = await supabase + .from('year') + .update({ + game: year.game + }) + .eq('year', year.year) + + if (error) throw error +} + +export { getValidYears, getYearById, saveYear, updateYear } diff --git a/src/components/YearPicker.tsx b/src/components/YearPicker.tsx index 91f1a94..776ff00 100644 --- a/src/components/YearPicker.tsx +++ b/src/components/YearPicker.tsx @@ -1,6 +1,7 @@ import { Accessor, Component, createSignal, For, Setter } from 'solid-js' -const validYears = ['2023'] +// TODO: should get these from the db +const validYears = ['2023', '2024'] const YearPicker: Component<{ year: Accessor diff --git a/src/pages/admin/AttendanceForSeasonByMember.tsx b/src/pages/admin/AttendanceForSeasonByMember.tsx index d64eab2..8eff81b 100644 --- a/src/pages/admin/AttendanceForSeasonByMember.tsx +++ b/src/pages/admin/AttendanceForSeasonByMember.tsx @@ -18,7 +18,7 @@ const AttendanceForSeasonByMember: Component<{ season: Accessor }> = (pr const [allAttendance] = createResource(props.season, getAttendanceForAllMembers) const [members] = createResource(props.season, getMembers) const [events] = createResource(props.season, getSeasonEvents) - const [filteredMembers, setfilteredMembers] = createSignal([]) + const [filteredMembers, setFilteredMembers] = createSignal([]) const [sessionValues] = useSessionContext() const navigate = useNavigate() @@ -58,6 +58,9 @@ const AttendanceForSeasonByMember: Component<{ season: Accessor }> = (pr const filtered = events().filter((event: RobotEvent) => { return event.event_type === EventTypes.REGULAR_PRACTICE }) + if (isEmpty(filtered)) { + return 0 + } // need the date of the practice we are going by let lastDate: string // if we have less events than the number we are looking for, take the last one @@ -126,7 +129,7 @@ const AttendanceForSeasonByMember: Component<{ season: Accessor }> = (pr createEffect(() => { const filtered = filterBySubTeam(members(), sessionValues.subTeam) const sorted = sortByFirstName(filtered) - setfilteredMembers(sorted) + setFilteredMembers(sorted) }) // the show prevents race condition when solid can't tell eventMap updated on it's own diff --git a/src/pages/members/AttendancePage.tsx b/src/pages/members/AttendancePage.tsx index 88f9fe4..505925a 100644 --- a/src/pages/members/AttendancePage.tsx +++ b/src/pages/members/AttendancePage.tsx @@ -29,10 +29,10 @@ const AttendancePage: Component = () => { const navigate = useNavigate() const [sessionValues] = useSessionContext() const { isAdmin } = useNoMythicUser() - + const [team, { refetch }] = createResource(() => parseInt(params.id || '-1'), getMemberAttendance) - // meeting date is only used for getting inital data + // meeting date is only used for getting initial data const [meetingDate, setMeetingDate] = createSignal(toYMD(getToday())) // eventsToday is only used to get initial data when hitting the page for first time const [eventsToday] = createResource(meetingDate, getEventsForDay) diff --git a/src/types/Api.ts b/src/types/Api.ts index 5505d48..f719b6c 100644 --- a/src/types/Api.ts +++ b/src/types/Api.ts @@ -53,6 +53,9 @@ const CheckinStatus = { } as const type CheckinStatusType = typeof CheckinStatus[keyof typeof CheckinStatus] +export { SubTeam, TeamRole, AttendanceTypes, School, EventTypes, CheckinStatus } + + type Member = { member_id?: number auth_id?: string @@ -100,12 +103,12 @@ type Attendance = { } type MemberAttendance = { - member_id: number - first_name: string - last_name: string - sub_team: SubTeamType - team_role: TeamRoleType - attendance: Attendance + member_id?: number + first_name?: string + last_name?: string + sub_team?: SubTeamType + team_role?: TeamRoleType + attendance?: Attendance } type EventAttendance = { @@ -193,6 +196,17 @@ type MealListItem = { meals: MealInfo } +type Year = { + year: number + game?: string +} + +type MemberYear = { + member_year_id?: number + member_id: number + year: number +} + export type { MemberAttendance, Member, @@ -215,6 +229,6 @@ export type { Meal, MealInfo, MealListItem, + Year, + MemberYear, } - -export { SubTeam, TeamRole, AttendanceTypes, School, EventTypes, CheckinStatus }