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

vinvoor: better data fetching #64

Merged
merged 2 commits into from
Sep 5, 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
16 changes: 8 additions & 8 deletions vingo/database/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

type BaseModel struct {
Id int `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"-"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}

Expand All @@ -24,7 +24,7 @@ type User struct {

type Settings struct {
BaseModel
ScanInOut bool `json:"scanInOut"`
ScanInOut bool `json:"scan_in_out"`
Leaderboard bool `json:"leaderboard"`
Public bool `json:"public"`
}
Expand Down Expand Up @@ -55,17 +55,17 @@ func Card_to_API(card Card) CardAPI {

type CardAPI struct {
Id int `json:"id"`
CreatedAt time.Time `json:"createdAt"`
CreatedAt time.Time `json:"created_at"`
Serial string `json:"serial"`
Name string `json:"name"`
LastUsed time.Time `json:"lastUsed"`
AmountUsed int `json:"amountUsed"`
LastUsed time.Time `json:"last_used"`
AmountUsed int `json:"amount_used"`
}

type Scan struct {
BaseModel
ScanTime time.Time `json:"scanTime"`
CardSerial string `json:"cardSerial" gorm:"index"`
ScanTime time.Time `json:"scan_time"`
CardSerial string `json:"card_serial" gorm:"index"`
Card Card `json:"-" gorm:"foreignKey:CardSerial;references:Serial"`
}

Expand Down
6 changes: 3 additions & 3 deletions vingo/database/scans.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ type Present struct {

type LeaderboardItem struct {
Position int `json:"position"`
UserId int `json:"userId"`
UserId int `json:"user_id"`
Username string `json:"username"`
TotalDays int `json:"totalDays"`
PositionChange int `json:"positionChange"`
TotalDays int `json:"total_days"`
PositionChange int `json:"position_change"`
}

type AnonymousScan struct {
Expand Down
1 change: 1 addition & 0 deletions vinvoor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@mui/icons-material": "^5.15.19",
"@mui/material": "^5.15.19",
"@mui/x-date-pickers": "^7.11.0",
"@tanstack/react-query": "^5.54.1",
"@types/js-cookie": "^3.0.6",
"@types/react-router-dom": "^5.3.3",
"@types/react-router-hash-link": "^2.4.9",
Expand Down
2 changes: 1 addition & 1 deletion vinvoor/src/WelcomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const WelcomePage = () => {
pt: 10,
}}
>
<TypographyG variant="h3">Welcome to Vinvoor!</TypographyG>
<TypographyG variant="h3">Welcome to Zess!</TypographyG>
<TypographyG variant="h4">Log in to start scanning</TypographyG>
<Box mt={2}>
<Login variant="contained" fullWidth>
Expand Down
8 changes: 4 additions & 4 deletions vinvoor/src/cards/Cards.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { LoadingSkeleton } from "../components/LoadingSkeleton";
import { useCardsContext } from "../providers/dataproviders/cardsProvider";
import { useCards } from "../hooks/useCard";
import { CardsEmpty } from "./CardsEmpty";
import { CardsTable } from "./CardsTable";

export const Cards = () => {
const { data: cards, loading } = useCardsContext();
const { data: cards, isLoading } = useCards();

return (
<LoadingSkeleton loading={loading}>
{cards.length ? <CardsTable /> : <CardsEmpty />}
<LoadingSkeleton loading={isLoading}>
{cards?.length ? <CardsTable /> : <CardsEmpty />}
</LoadingSkeleton>
);
};
17 changes: 4 additions & 13 deletions vinvoor/src/cards/CardsAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,15 @@ import { Button, Typography } from "@mui/material";
import { useConfirm } from "material-ui-confirm";
import { useSnackbar } from "notistack";
import { useEffect, useState } from "react";
import { useCardsContext } from "../providers/dataproviders/cardsProvider";
import {
Card,
CardGetRegisterResponse,
CardJSON,
CardPostResponse,
convertCardJSON,
} from "../types/cards";
import { CardGetRegisterResponse, CardPostResponse } from "../types/cards";
import { Optional } from "../types/general";
import { getApi, isResponseNot200Error, postApi } from "../util/fetch";
import { randomInt } from "../util/util";
import {
CircularTimeProgress,
CircularTimeProgressProps,
} from "./CircularTimeProgress";
import { useCards } from "../hooks/useCard";

const CHECK_INTERVAL = 1000;
const REGISTER_TIME = 60000;
Expand Down Expand Up @@ -45,7 +39,7 @@ const registerSucces = "Card registered successfully";
const registerFail = "Failed to register card";

export const CardsAdd = () => {
const { setData: setCards } = useCardsContext();
const { refetch } = useCards();
const [registering, setRegistering] = useState<boolean>(false);
const [progressProps, setProgressProps] =
useState<CircularTimeProgressProps>(defaultProgressProps);
Expand Down Expand Up @@ -117,10 +111,7 @@ export const CardsAdd = () => {
enqueueSnackbar(registerSucces, {
variant: "success",
});
void getApi<readonly Card[], CardJSON[]>(
"cards",
convertCardJSON,
).then(cards => setCards(cards));
void refetch();
} else
enqueueSnackbar(registerFail, {
variant: "error",
Expand Down
6 changes: 4 additions & 2 deletions vinvoor/src/cards/CardsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Paper, Table, TableContainer, TablePagination } from "@mui/material";
import { ChangeEvent, MouseEvent, useMemo, useState } from "react";
import { useCardsContext } from "../providers/dataproviders/cardsProvider";
import { Card } from "../types/cards";
import { TableOrder } from "../types/general";
import { CardsTableBody } from "./CardsTableBody";
import { CardsTableHead } from "./CardsTableHead";
import { CardsTableToolbar } from "./CardsTableToolbar";
import { useCards } from "../hooks/useCard";

const rowsPerPageOptions = [10, 25, 50];

Expand Down Expand Up @@ -44,7 +44,9 @@ const stableSort = <T,>(
};

export const CardsTable = () => {
const { data: cards } = useCardsContext();
const { data: cards } = useCards();
if (!cards) return null; // Can never happen

const [order, setOrder] = useState<TableOrder>("asc");
const [orderBy, setOrderBy] = useState<keyof Card>("serial");
const [selected, setSelected] = useState<readonly string[]>([]);
Expand Down
37 changes: 17 additions & 20 deletions vinvoor/src/cards/CardsTableBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ import {
import { useConfirm } from "material-ui-confirm";
import { useSnackbar } from "notistack";
import { ChangeEvent, FC, MouseEvent } from "react";
import { useCardsContext } from "../providers/dataproviders/cardsProvider";
import {
Card,
CardJSON,
cardsHeadCells,
convertCardJSON,
} from "../types/cards";
import { getApi, patchApi } from "../util/fetch";
import { Card, cardsHeadCells } from "../types/cards";
import { useCards, usePatchCards } from "../hooks/useCard";

interface CardsTableBodyProps {
rows: readonly Card[];
Expand All @@ -39,7 +33,8 @@ export const CardsTableBody: FC<CardsTableBodyProps> = ({
handleClick,
emptyRows,
}) => {
const { setData: setCards } = useCardsContext();
const { refetch } = useCards();
const patchCard = usePatchCards();
const confirm = useConfirm();
const { enqueueSnackbar } = useSnackbar();

Expand All @@ -64,17 +59,19 @@ export const CardsTableBody: FC<CardsTableBodyProps> = ({
return;
}

patchApi(`cards/${id}`, { name: newName })
.then(() => {
enqueueSnackbar(nameSaveSuccess, {
variant: "success",
});
void getApi<readonly Card[], CardJSON[]>(
"cards",
convertCardJSON,
).then(cards => setCards(cards));
})
.catch(() => enqueueSnackbar(nameSaveFailure, { variant: "error" }));
patchCard.mutate(
{ id: id, newName: newName },
{
onSuccess: () => {
enqueueSnackbar(nameSaveSuccess, {
variant: "success",
});
void refetch();
},
onError: () =>
enqueueSnackbar(nameSaveFailure, { variant: "error" }),
},
);
})
.catch(() => {
// Required otherwise the confirm dialog will throw an error in the console
Expand Down
20 changes: 20 additions & 0 deletions vinvoor/src/hooks/useCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { Card, CardJSON, convertCardJSON } from "../types/cards";
import { getApi, patchApi } from "../util/fetch";

const ENDPOINT = "cards";

export const useCards = () =>
useQuery<Card[]>({
queryKey: ["cards"],
queryFn: () => getApi<Card[], CardJSON[]>(ENDPOINT, convertCardJSON),
});

export const usePatchCards = () => {
return useMutation({
mutationFn: (args: { id: number; newName: string }) =>
patchApi(`${ENDPOINT}/${args.id}`, {
name: args.newName,
}),
});
};
17 changes: 17 additions & 0 deletions vinvoor/src/hooks/useDays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { convertDayJSON, Day, DayJSON } from "../types/days";
import { deleteAPI, getApi } from "../util/fetch";

const ENDPOINT = "admin/days";

export const useDays = () =>
useQuery({
queryKey: ["days"],
queryFn: () => getApi<Day[], DayJSON[]>(ENDPOINT, convertDayJSON),
});

export const useDeleteDay = () => {
return useMutation({
mutationFn: (id: number) => deleteAPI(`${ENDPOINT}/${id}`),
});
};
29 changes: 0 additions & 29 deletions vinvoor/src/hooks/useFetch.ts

This file was deleted.

19 changes: 19 additions & 0 deletions vinvoor/src/hooks/useLeaderboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useQuery } from "@tanstack/react-query";
import { getApi } from "../util/fetch";
import {
convertLeaderboardItemJSON,
LeaderboardItem,
LeaderboardItemJSON,
} from "../types/leaderboard";

const ENDPOINT = "leaderboard";

export const useLeaderboardItems = () =>
useQuery({
queryKey: ["leaderboard"],
queryFn: () =>
getApi<LeaderboardItem[], LeaderboardItemJSON[]>(
ENDPOINT,
convertLeaderboardItemJSON,
),
});
11 changes: 11 additions & 0 deletions vinvoor/src/hooks/useScan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useQuery } from "@tanstack/react-query";
import { getApi } from "../util/fetch";
import { convertScanJSON, Scan, ScanJSON } from "../types/scans";

const ENDPOINT = "scans";

export const useScans = () =>
useQuery({
queryKey: ["scans"],
queryFn: () => getApi<Scan[], ScanJSON[]>(ENDPOINT, convertScanJSON),
});
26 changes: 26 additions & 0 deletions vinvoor/src/hooks/useSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { getApi, patchApi } from "../util/fetch";
import { converSettingsJSON, Settings, SettingsJSON } from "../types/settings";

const ENDPOINT = "settings";

export const useSettings = () =>
useQuery({
queryKey: ["settings"],
queryFn: () => getApi<Settings, SettingsJSON>(ENDPOINT, converSettingsJSON),
});

export const usePatchSettings = () => {
return useMutation({
mutationFn: (args: {
scanInOut: boolean;
leaderboard: boolean;
public: boolean;
}) =>
patchApi(ENDPOINT, {
scanInOut: args.scanInOut,
leaderboard: args.leaderboard,
public: args.public,
}),
});
};
8 changes: 4 additions & 4 deletions vinvoor/src/leaderboard/Leaderboard.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Divider, Paper, Table, TableContainer } from "@mui/material";
import { LoadingSkeleton } from "../components/LoadingSkeleton";
import { useLeaderboardContext } from "../providers/dataproviders/leaderboardProvider";
import { LeaderboardTableBody } from "./LeaderboardTableBody";
import { LeaderboardTableToolbar } from "./LeaderboardTableToolbar";
import { useLeaderboardItems } from "../hooks/useLeaderboard";

export const Leaderboard = () => {
const { data: leaderboardItems, loading } = useLeaderboardContext();
const { isLoading } = useLeaderboardItems();

return (
<LoadingSkeleton loading={loading}>
<LoadingSkeleton loading={isLoading}>
<Paper elevation={4}>
<LeaderboardTableToolbar />
<Divider sx={{ borderColor: "primary.main", borderBottomWidth: 3 }} />
<TableContainer>
<Table>
<LeaderboardTableBody leaderboardItems={leaderboardItems} />
<LeaderboardTableBody />
</Table>
</TableContainer>
</Paper>
Expand Down
Loading
Loading