Skip to content

Commit

Permalink
Feature/dot 677 refactor queries and mutations into apits (#341)
Browse files Browse the repository at this point in the history
* Move groupLeaderboard query options to api.ts

* Move public group query options to api.ts

* Renamed options to query

* Refactored user query options into api.ts

* Refactored committees query options into api.ts

* Refactored leaderboard query options into api.ts

* Minor

* Move request to join mutation options to api.ts

* Refactored all the rest of mutations to api.ts

* Fix several bugs

* Bugfix

* Fixed prefetching and changed caching times

* Fix permission always off
  • Loading branch information
jotjern authored Mar 17, 2024
1 parent 0f2d595 commit 62fcb53
Show file tree
Hide file tree
Showing 21 changed files with 771 additions and 797 deletions.
9 changes: 7 additions & 2 deletions frontend/src/components/committees/CommitteeList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { CommitteeCard } from "./CommitteeCard"
import { useAuth } from "react-oidc-context"
import { useCommittees } from "../../helpers/api"
import { GroupStatistics } from "../../helpers/types"
import NoImage from "../../assets/NoImage.png"
import { committeesQuery } from "../../helpers/api"
import { useQuery } from "@tanstack/react-query"

export const CommitteeList = () => {
const auth = useAuth()
const { data: groupsStatistics } = useCommittees({ enabled: auth.isAuthenticated })

const { data: groupsStatistics } = useQuery({
...committeesQuery(),
enabled: auth.isAuthenticated,
})

return (
<div className="flex flex-wrap -m-2 gap-16 justify-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const LeaderboardTable = ({
</div>
)}

{isFetching && [...Array(3)].map((e, i) => <SkeletonTableItem key={i} />)}
{isFetching && !leaderboardUsers && [...Array(3)].map((e, i) => <SkeletonTableItem key={i} />)}
</Accordion.Root>
</ul>
)
Expand Down
28 changes: 7 additions & 21 deletions frontend/src/components/nav/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"
import { Disclosure, Menu, Transition } from "@headlessui/react"
import { LEADERBOARD_URL, useUser } from "../../helpers/api"
import { committeesQuery, leaderboardQuery, userQuery } from "../../helpers/api"

import { AuthContextProps } from "react-oidc-context"
import { AvatarIcon } from "@radix-ui/react-icons"
import BugIcon from "../../icons/BugIcon"
import { Fragment } from "react"
import { Leaderboard } from "../../helpers/types"
import { Link, useLocation, useNavigate } from "react-router-dom"
import { NavLink } from "./NavLink"
import OnlineLogo from "../../assets/online-logo-blue.png"
import OnlineLogoWhite from "../../assets/online-logo-white.png"
import axios from "axios"
import { classNames } from "../../helpers/classNames"
import { useQueryClient } from "@tanstack/react-query"
import { useQuery, useQueryClient } from "@tanstack/react-query"
import { useDarkMode } from "../../DarkModeContext"

interface NavItem {
Expand All @@ -32,24 +30,11 @@ export const Nav = ({ auth }: NavProps) => {
const queryClient = useQueryClient()
const location = useLocation()

const { data: groups } = useUser({
enabled: auth.isAuthenticated,
})
const isInAnyOWGroup = groups?.groups.some((group) => group.ow_group_id !== null) ?? false
const { data: user } = useQuery(userQuery())
const isInAnyOWGroup = user?.groups.some((group) => group.ow_group_id !== null) ?? false

const prefetchWallOfShame = () => {
queryClient.prefetchInfiniteQuery(
["leaderboard", { pageParam: LEADERBOARD_URL }],
({ pageParam = LEADERBOARD_URL }) => axios.get(pageParam).then((res) => res.data),
{
getNextPageParam: (lastPage: Leaderboard, _) => lastPage.next,
}
)
}

const { data: user } = useUser({
enabled: auth.isAuthenticated,
})
const prefetchWallOfShame = () => queryClient.prefetchInfiniteQuery(leaderboardQuery())
const prefetchStatistics = () => queryClient.prefetchQuery(committeesQuery())

const homeLocation = user && user.groups.length > 0 ? `/gruppe/${user.groups[0].name_short.toLowerCase()}` : "/"

Expand All @@ -71,6 +56,7 @@ export const Nav = ({ auth }: NavProps) => {
url: "/committees",
isActivePredicate: (item, currentLocation) => currentLocation.toLowerCase().startsWith(`${item.url}`),
shouldShowPredicate: () => isInAnyOWGroup,
prefetch: prefetchStatistics,
},
]

Expand Down
40 changes: 6 additions & 34 deletions frontend/src/components/punishment/PunishmentActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Menu, Switch, Transition } from "@headlessui/react"
import { EllipsisHorizontalIcon, PlusIcon } from "@heroicons/react/24/outline"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import axios, { AxiosResponse } from "axios"
import { useMutation, useQuery } from "@tanstack/react-query"
import { Dispatch, Fragment, ReactNode, SetStateAction } from "react"
import { GroupUser } from "../../helpers/types"

import { VengefulApiError, getPostAllPunishmentsPaidForUserUrl, useGroupLeaderboard } from "../../helpers/api"
import { groupLeaderboardQuery, postAllPunishmentsPaidForUserMutation } from "../../helpers/api"
import { classNames } from "../../helpers/classNames"
import { useCurrentUser } from "../../helpers/context/currentUserContext"
import { useGivePunishmentModal } from "../../helpers/context/modal/givePunishmentModalContext"
import { useNotification } from "../../helpers/context/notificationContext"
import { useTogglePunishments } from "../../helpers/context/togglePunishmentsContext"
import { PunishmentActionBarListItem } from "./PunishmentActionBarListItem"
import { usePermission } from "../../helpers/permissions"
Expand All @@ -31,11 +29,9 @@ export const PunishmentActionBar = ({ user, label }: PunishmentActionBarProps) =
let isToggled: boolean | undefined
let setIsToggled: Dispatch<SetStateAction<boolean>> | undefined

const { setNotification } = useNotification()
const queryClient = useQueryClient()
const { currentUser } = useCurrentUser()

const { data: groupData } = useGroupLeaderboard(user.group_id, undefined, {})
const { data: groupData } = useQuery(groupLeaderboardQuery(user.group_id))

const { setOpen: newSetOpen, setPreferredSelectedPerson: newSetPreferredSelectedPerson } = useGivePunishmentModal()
setOpen = newSetOpen
Expand All @@ -45,33 +41,9 @@ export const PunishmentActionBar = ({ user, label }: PunishmentActionBarProps) =
isToggled = newIsToggled
setIsToggled = newSetIsToggled

const markAllPunishmentsAsPaidCall = async () => {
const groupUser = user as GroupUser

const POST_ALL_PUNISHMENTS_PAID_URL = getPostAllPunishmentsPaidForUserUrl(groupUser.group_id, user.user_id)
const res: AxiosResponse<string> = await axios.post(POST_ALL_PUNISHMENTS_PAID_URL)

return res.data
}

const { mutate: mutateMarkAllPunishmentsAsPaid } = useMutation(markAllPunishmentsAsPaidCall, {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["groupLeaderboard"] })
setNotification({
show: true,
type: "success",
title: "Betalinger registert",
text: `Alle straffer registrert som betalte`,
})
},
onError: (e: VengefulApiError) => {
setNotification({
type: "error",
title: "Kunne ikke registrere betalingene",
text: e.response.data.detail,
})
},
})
const { mutate: mutateMarkAllPunishmentsAsPaid } = useMutation(
postAllPunishmentsPaidForUserMutation(user.group_id, user.user_id)
)

const listItems: ActionBarItem[] = []
const currentGroupUser = groupData?.members.find((groupUser) => groupUser.user_id === currentUser?.user_id)
Expand Down
162 changes: 28 additions & 134 deletions frontend/src/components/punishment/PunishmentItem.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { QueryObserverResult, RefetchOptions, RefetchQueryFilters, useMutation } from "@tanstack/react-query"
import axios, { AxiosResponse } from "axios"
import { QueryObserverResult, RefetchOptions, RefetchQueryFilters, useMutation, useQuery } from "@tanstack/react-query"
import {
VengefulApiError,
addReaction,
getDeletePunishmentUrl,
getPostPunishmentsPaidUrl,
getPostPunishmentsUnpaidUrl,
removeReaction,
useGroupLeaderboard,
groupLeaderboardQuery,
addReactionMutation,
removeReactionMutation,
deletePunishmentMutation,
postPunishmentsUnpaidMutation,
postPunishmentsPaidMutation,
} from "../../helpers/api"
import { LeaderboardPunishment, Punishment, PunishmentType } from "../../helpers/types"

Expand All @@ -16,7 +14,6 @@ import timezone from "dayjs/plugin/timezone"
import utc from "dayjs/plugin/utc"
import { useState } from "react"
import { classNames } from "../../helpers/classNames"
import { useNotification } from "../../helpers/context/notificationContext"
import { Button } from "../button"
import { EmojiPicker } from "./emojies/EmojiPicker"
import { ReactionsDisplay } from "./emojies/ReactionDisplay"
Expand Down Expand Up @@ -45,11 +42,8 @@ export const PunishmentItem = ({
dataRefetch,
}: PunishmentItemProps) => {
const [_selectedEmoji, setSelectedEmoji] = useState("👍")
const { setNotification } = useNotification()

const { data: groupData } = useGroupLeaderboard(group_id, undefined, {
enabled: isGroupContext,
})
const { data: groupData } = useQuery(groupLeaderboardQuery(group_id))

let punishmentType = punishmentTypes ? punishmentTypes[punishment.punishment_type_id] : undefined

Expand All @@ -58,128 +52,28 @@ export const PunishmentItem = ({
punishmentType = innerPunishment.punishment_type
}

const addReactionCall = async (emoji: string) => addReaction(punishment.punishment_id, emoji)
const removeReactionCall = async () => removeReaction(punishment.punishment_id)

const markPunishmentAsPaidCall = async () => {
if (!punishmentType) {
throw new Error("Punishment type is undefined")
}

if (punishment.group_id === null) {
// this should never happen
return
}

const POST_PUNISHMENTS_PAID_URL = getPostPunishmentsPaidUrl(punishment.group_id)
const res: AxiosResponse<string> = await axios.post(POST_PUNISHMENTS_PAID_URL, [punishment.punishment_id])

return res.data
}

const markPunishmentAsUnpaidCall = async () => {
if (!punishmentType) {
throw new Error("Punishment type is undefined")
}

if (punishment.group_id === null) {
// this should never happen
return
}

const POST_PUNISHMENTS_PAID_URL = getPostPunishmentsUnpaidUrl(punishment.group_id)
const res: AxiosResponse<string> = await axios.post(POST_PUNISHMENTS_PAID_URL, [punishment.punishment_id])

return res.data
}

const deletePunishmentCall = async () => {
if (punishment.group_id === null) {
// this should never happen
return
}

const DELETE_PUNISHMENT_URL = getDeletePunishmentUrl(punishment.group_id, punishment.punishment_id)
const res: AxiosResponse<string> = await axios.delete(DELETE_PUNISHMENT_URL, {
data: { punishment_id: punishment.punishment_id, group_id: punishment.group_id },
})

return res.data
}

const { mutate: mutateAddReaction } = useMutation(addReactionCall, {
const { mutate: mutateAddReaction } = useMutation({
...addReactionMutation(punishment.punishment_id),
onSuccess: () => dataRefetch(),
onError: (e: VengefulApiError) =>
setNotification({
type: "error",
title: "Kunne ikke legge til reaction",
text: e.response.data.detail,
}),
})

const { mutate: mutateRemoveReaction } = useMutation(removeReactionCall, {
onSuccess: () => dataRefetch(),
onError: (e: VengefulApiError) =>
setNotification({
type: "error",
title: "Kunne ikke fjerne reaction",
text: e.response.data.detail,
}),
})
const { mutate: removeReaction } = useMutation(removeReactionMutation(punishment.punishment_id))

const { mutate: markPunishmentAsPaid } = useMutation(markPunishmentAsPaidCall, {
onSuccess: () => {
dataRefetch()
setNotification({
type: "success",
title: "Betaling registrert",
text: `Straff registrert betalt`,
})
},
onError: (e: VengefulApiError) => {
setNotification({
type: "error",
title: "Kunne ikke registrere betaling",
text: e.response.data.detail,
})
},
})
const { mutate: markPunishmentAsPaid } = useMutation(
postPunishmentsPaidMutation(punishment.group_id!, [punishment.punishment_id])
)

const { mutate: markPunishmentAsUnpaid } = useMutation(markPunishmentAsUnpaidCall, {
onSuccess: () => {
dataRefetch()
setNotification({
type: "success",
title: "Betaling registrert",
text: `Straff registrert ubetalt`,
})
},
onError: (e: VengefulApiError) => {
setNotification({
type: "error",
title: "Kunne ikke registrere som ubetalt",
text: e.response.data.detail,
})
},
})
const { mutate: markPunishmentAsUnpaid } = useMutation(
postPunishmentsUnpaidMutation(punishment.group_id!, [punishment.punishment_id])
)

const { mutate: deletePunishment } = useMutation(deletePunishmentCall, {
onSuccess: () => {
dataRefetch()
setNotification({
type: "success",
title: "Straff slettet",
text: `Straff slettet`,
})
},
onError: (e: VengefulApiError) => {
setNotification({
type: "error",
title: "Kunne ikke slette straff",
text: e.response.data.detail,
})
},
})
const { mutate: deletePunishment } = useMutation(
deletePunishmentMutation(punishment.group_id!, punishment.punishment_id)
)

const canMarkAsPaid = usePermission("group.punishments.mark_paid", groupData)
const canMarkAsUnpaid = usePermission("group.punishments.mark_unpaid", groupData)
const canDelete = usePermission("group.punishments.delete", groupData)

const date = dayjs.utc(punishment.created_at).tz("Europe/Oslo")

Expand Down Expand Up @@ -245,14 +139,14 @@ export const PunishmentItem = ({
<EmojiPicker mutate={mutateAddReaction} setSelectedEmoji={setSelectedEmoji} />
<ReactionsDisplay
mutate={mutateAddReaction}
removeMutation={mutateRemoveReaction}
removeMutation={removeReaction}
setSelectedEmoji={setSelectedEmoji}
reactions={punishment.reactions}
/>
</div>
{isGroupContext && (
<div className="flex flex-row gap-x-4 ml-auto items-center text-slate-500">
{punishment.paid && usePermission("group.punishments.mark_unpaid", groupData) && (
{punishment.paid && canMarkAsPaid && (
<Button
variant="OUTLINE"
label="Marker som ubetalt"
Expand All @@ -262,7 +156,7 @@ export const PunishmentItem = ({
/>
)}

{!punishment.paid && usePermission("group.punishments.mark_paid", groupData) && (
{!punishment.paid && canMarkAsUnpaid && (
<Button
variant="OUTLINE"
label="Marker som betalt"
Expand All @@ -272,7 +166,7 @@ export const PunishmentItem = ({
/>
)}

{usePermission("group.punishments.delete", groupData) && (
{canDelete && (
<Button
variant="OUTLINE"
color="RED"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/punishment/emojies/EmojiPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FaceSmileIcon } from "@heroicons/react/24/outline"
import { UseMutateFunction } from "@tanstack/react-query"

interface EmojiPickerProps {
mutate: UseMutateFunction<string, unknown, string, unknown>
mutate: UseMutateFunction<void, unknown, string, unknown>
setSelectedEmoji: React.Dispatch<React.SetStateAction<string>>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useCurrentUser } from "../../../helpers/context/currentUserContext"

interface ReactionsDisplayProps {
reactions: PunishmentReaction[]
mutate: UseMutateFunction<string, unknown, string, unknown>
removeMutation: UseMutateFunction<string, unknown, void, unknown>
mutate: UseMutateFunction<void, unknown, string, unknown>
removeMutation: UseMutateFunction<void, unknown, void, unknown>
setSelectedEmoji: React.Dispatch<React.SetStateAction<string>>
}

Expand Down
Loading

0 comments on commit 62fcb53

Please sign in to comment.