From 857ea8f196bc1eaaf3dfa143371ed4e78eecc62a Mon Sep 17 00:00:00 2001 From: sverben <59171289+sverben@users.noreply.github.com> Date: Sat, 25 May 2024 15:02:48 +0200 Subject: [PATCH 1/2] feat: register members --- package.json | 1 + src/components/register/listing.tsx | 16 +-- src/components/register/presence.tsx | 59 ++++++++++ src/screens/feed/feed.tsx | 7 +- src/screens/feed/slot.tsx | 157 ++++++++++++++++++++------- src/stores/register.ts | 19 +++- yarn.lock | 5 + 7 files changed, 215 insertions(+), 49 deletions(-) create mode 100644 src/components/register/presence.tsx diff --git a/package.json b/package.json index 8efe8a4..9b73799 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react-native-maps": "1.15.3", "react-native-pager-view": "6.3.1", "react-native-paper": "^5.12.3", + "react-native-paper-select": "^1.1.1", "react-native-safe-area-context": "4.10.1", "react-native-screens": "~3.31.1", "react-native-vector-icons": "^10.0.3", diff --git a/src/components/register/listing.tsx b/src/components/register/listing.tsx index cdafb70..9ff712d 100644 --- a/src/components/register/listing.tsx +++ b/src/components/register/listing.tsx @@ -1,7 +1,7 @@ import { useContext, useEffect } from "react"; import AuthContext, { Authed } from "../../auth"; import { useAtom } from "jotai"; -import { getSlots, Slot, slotsAtom } from "../../stores/register"; +import { getSlots, membersAtom, Slot, slotsAtom } from "../../stores/register"; import { ActivityIndicator, Avatar, @@ -39,16 +39,18 @@ function SlotListing({ slot, index }: { slot: Slot; index: number }) { export default function Listing() { const authState = useContext(AuthContext); const [slots, setSlots] = useAtom(slotsAtom); + const [_members, setMembers] = useAtom(membersAtom); useEffect(() => { async function fetchDays() { - setSlots( - await getSlots( - authState.authenticated === Authed.AUTHENTICATED - ? await authState.token - : null, - ), + const { slots, members } = await getSlots( + authState.authenticated === Authed.AUTHENTICATED + ? await authState.token + : null, ); + + setSlots(slots); + setMembers(members || []); } fetchDays().then(); diff --git a/src/components/register/presence.tsx b/src/components/register/presence.tsx new file mode 100644 index 0000000..ee8505f --- /dev/null +++ b/src/components/register/presence.tsx @@ -0,0 +1,59 @@ +import { + getSlots, + Presence as PresenceType, + Slot, + slotsAtom, +} from "../../stores/register"; +import { ActivityIndicator, Checkbox, Switch, Text } from "react-native-paper"; +import { StyleSheet, TouchableOpacity } from "react-native"; +import { useContext, useState } from "react"; +import AuthContext, { Authed } from "../../auth"; +import { useAtom } from "jotai"; +import { Platform } from "react-native"; + +export default function Presence({ + presence, + slot, +}: { + presence: PresenceType; + slot: Slot; +}) { + const [slots, setSlots] = useAtom(slotsAtom); + const [seen, setSeen] = useState(presence.seen); + const authState = useContext(AuthContext); + + async function markSeen() { + if (authState.authenticated !== Authed.AUTHENTICATED) return; + if (!slots) return; + + presence.seen = !presence.seen; + setSeen(presence.seen); + setSlots(slots); + + const token = await authState.token; + await fetch( + `https://aanmelden.djoamersfoort.nl/api/v1/seen/${presence.id}/${presence.seen ? "true" : "false"}`, + { + headers: { + authorization: `Bearer ${token}`, + }, + }, + ); + } + + return ( + + + {presence.name} + + ); +} + +const styles = StyleSheet.create({ + presence: { + display: "flex", + flexDirection: "row", + alignItems: "center", + gap: 10, + }, +}); diff --git a/src/screens/feed/feed.tsx b/src/screens/feed/feed.tsx index d7acd34..9f4fddc 100644 --- a/src/screens/feed/feed.tsx +++ b/src/screens/feed/feed.tsx @@ -10,7 +10,7 @@ import { getRSSFeed, sortFeeds, } from "../../stores/feed"; -import { getSlots, slotsAtom } from "../../stores/register"; +import { getSlots, slotsAtom, membersAtom } from "../../stores/register"; import AuthContext, { Authed } from "../../auth"; import { NativeStackNavigationProp } from "react-native-screens/native-stack"; import { StackParamList } from "../../../App"; @@ -25,6 +25,7 @@ export default function FeedScreen() { const authState = useContext(AuthContext); const [_feed, setFeed] = useAtom(feedAtom); const [_slots, setSlots] = useAtom(slotsAtom); + const [_members, setMembers] = useAtom(membersAtom); async function refresh() { setRefreshing(true); @@ -35,8 +36,10 @@ export default function FeedScreen() { await Promise.all([ new Promise(async (resolve) => { setSlots(null); - const slots = await getSlots(token); + const { slots, members } = await getSlots(token); setSlots(slots); + setMembers(members || []); + resolve(); }), new Promise(async (resolve) => { diff --git a/src/screens/feed/slot.tsx b/src/screens/feed/slot.tsx index bbd3dcf..37ba289 100644 --- a/src/screens/feed/slot.tsx +++ b/src/screens/feed/slot.tsx @@ -1,11 +1,26 @@ import { StackScreenProps } from "@react-navigation/stack"; -import { Alert, StyleSheet, View } from "react-native"; -import { Button, Card, Chip, Text } from "react-native-paper"; +import { + Alert, + ScrollView, + StyleSheet, + TouchableOpacity, + View, +} from "react-native"; +import { + Button, + Card, + Checkbox, + Chip, + Text, + useTheme, +} from "react-native-paper"; import { useContext, useEffect, useState } from "react"; -import { getSlots, Slot, slotsAtom } from "../../stores/register"; +import { getSlots, membersAtom, Slot, slotsAtom } from "../../stores/register"; import { useAtom } from "jotai"; import { StackParamList } from "../../../App"; import AuthContext, { Authed } from "../../auth"; +import Presence from "../../components/register/presence"; +import { PaperSelect } from "react-native-paper-select"; type Props = StackScreenProps; @@ -15,9 +30,27 @@ export default function SlotScreen({ route, navigation }: Props) { const [slot, setSlot] = useState(slots[route.params.slot]); const [loading, setLoading] = useState(false); + const [members] = useAtom(membersAtom); + const theme = useTheme(); const authState = useContext(AuthContext); + async function registerManual(user: string) { + if (authState.authenticated !== Authed.AUTHENTICATED) return; + + const token = await authState.token; + await fetch( + `https://aanmelden.djoamersfoort.nl/api/v1/register_manual/${slot.name}/${slot.pod}/${user}`, + { + headers: { + authorization: `Bearer ${token}`, + }, + }, + ); + + setSlots((await getSlots(token)).slots); + } + async function register() { setLoading(true); const token = @@ -49,7 +82,7 @@ export default function SlotScreen({ route, navigation }: Props) { slot.is_registered = !slot.is_registered; } - setSlots(await getSlots(token)); + setSlots((await getSlots(token)).slots); setLoading(false); } @@ -61,39 +94,83 @@ export default function SlotScreen({ route, navigation }: Props) { return ( - - {slot.announcement && ( - <> - Aankondiging - - - {slot.announcement} - - - - )} - Beschikbaarheid - - - - Er zijn {slot.available}/{slot.available + slot.taken} plekken - beschikbaar. - - - - - Begeleiders - - - {slot.tutors.length === 0 && ( - Er zijn nog geen begeleiders aangemeld - )} - {slot.tutors.map((tutor) => ( - {tutor} - ))} - - - + + + {slot.announcement && ( + <> + Aankondiging + + + {slot.announcement} + + + + )} + Beschikbaarheid + + + + Er zijn {slot.available}/{slot.available + slot.taken} plekken + beschikbaar. + + + + + Begeleiders + + + {slot.tutors.length === 0 && ( + Er zijn nog geen begeleiders aangemeld + )} + {slot.tutors.map((tutor) => ( + {tutor} + ))} + + + + {slot.presence && ( + <> + Leden + + + a.name < b.name ? -1 : 1) .map(({ id, name }) => ({ + _id: id.toString(), + value: name, + }))} + selectedArrayList={[]} + multiEnable={false} + value={""} + onSelection={async (selection) => { + if (!selection.selectedList[0]) return; + await registerManual(selection.selectedList[0]._id); + }} + theme={theme} + textInputStyle={{ + backgroundColor: theme.colors.elevation.level5, + color: theme.colors.onPrimaryContainer, + }} + searchStyle={{ + backgroundColor: theme.colors.elevation.level5, + }} + textColor={theme.colors.onPrimary} + /> + + {slot.presence.map((presence) => ( + + ))} + + + + + )} + +