-
-
-
+
)
}
// Fetch server-side data here
-export const loadData = (): AppThunk[] => [...fetchForLoan.map((f) => f())]
+export const loadData = (): AppThunk[] => [...fetchForLoans.map((f) => f())]
-export default memo(LoanPage)
+export default memo(LoansPage)
diff --git a/src/pages/Loan/index.tsx b/src/pages/Loans/index.tsx
similarity index 65%
rename from src/pages/Loan/index.tsx
rename to src/pages/Loans/index.tsx
index c7b61dc..7c65dae 100755
--- a/src/pages/Loan/index.tsx
+++ b/src/pages/Loans/index.tsx
@@ -1,15 +1,15 @@
import loadable from "@loadable/component"
import { Loading, ErrorBoundary } from "../../components"
-import { Props, loadData } from "./LoanPage"
+import { Props, loadData } from "./LoansPage"
-const Loan = loadable(() => import("./LoanPage"), {
+const Loans = loadable(() => import("./LoansPage"), {
fallback:
,
})
export default (props: Props): JSX.Element => (
-
+
)
diff --git a/src/pages/Loans/styles.module.scss b/src/pages/Loans/styles.module.scss
new file mode 100755
index 0000000..a50181c
--- /dev/null
+++ b/src/pages/Loans/styles.module.scss
@@ -0,0 +1,13 @@
+@import "../../theme/mixins";
+
+.loaningPage {
+ @include page-wrapper-center;
+}
+
+.loaningContent {
+ @include page-content-wrapper(700px);
+}
+
+.hide {
+ display: none;
+}
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 402bf65..018de33 100755
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -10,7 +10,8 @@ import AsyncAnnouncements, { loadData as loadAnnouncementsData } from "../pages/
import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../pages/TeamAssignment"
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
-import AsyncLoan, { loadData as loadLoanData } from "../pages/Loan"
+import AsyncLoans, { loadData as loadLoansData } from "../pages/Loans"
+import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
@@ -47,8 +48,13 @@ export default [
},
{
path: "/emprunts",
- component: AsyncLoan,
- loadData: loadLoanData,
+ component: AsyncLoans,
+ loadData: loadLoansData,
+ },
+ {
+ path: "/emprunter",
+ component: AsyncLoaning,
+ loadData: loadLoaningData,
},
{
path: "/fiches",
diff --git a/src/server/gsheets/boxes.ts b/src/server/gsheets/boxes.ts
index 659830f..e9fbe4b 100644
--- a/src/server/gsheets/boxes.ts
+++ b/src/server/gsheets/boxes.ts
@@ -16,7 +16,7 @@ export const detailedBoxListGet = expressAccessor.get(async (list) => {
const toBeAsked: DetailedBox[] = []
gameList.forEach((game) => {
- const box: Box | undefined = list.find((g) => g.gameId === game.id)
+ const box: Box | undefined = list.find((b) => b.gameId === game.id)
if ((box && box.unplayable) || (!box && !game.toBeKnown)) {
return
}
diff --git a/src/server/gsheets/games.ts b/src/server/gsheets/games.ts
index c70a9ba..e947141 100644
--- a/src/server/gsheets/games.ts
+++ b/src/server/gsheets/games.ts
@@ -1,8 +1,24 @@
import axios from "axios"
import { Parser } from "xml2js"
-import { assign, cloneDeep, find } from "lodash"
+import {
+ assign,
+ cloneDeep,
+ every,
+ find,
+ groupBy,
+ last,
+ mapValues,
+ reduce,
+ some,
+ sortBy,
+} from "lodash"
import ExpressAccessors from "./expressAccessors"
-import { Game, GameWithoutId, translationGame } from "../../services/games"
+import { getSheet } from "./accessors"
+import { Game, GameWithoutId, GameWithVolunteers, translationGame } from "../../services/games"
+import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../../services/volunteers"
+import { translationBox, Box, BoxWithoutId } from "../../services/boxes"
+import { getUniqueNickname } from "./tools"
+import { gameTitleCategory, gameTitleExactCategory, gameTitleOrder } from "../../store/utils"
const expressAccessor = new ExpressAccessors
(
"Games",
@@ -10,7 +26,7 @@ const expressAccessor = new ExpressAccessors(
translationGame
)
-export const gameListGet = expressAccessor.listGet()
+// export const gameListGet = expressAccessor.listGet()
// export const gameGet = expressAccessor.get()
// export const gameAdd = expressAccessor.add()
// export const gameSet = expressAccessor.set()
@@ -18,7 +34,7 @@ export const gameListGet = expressAccessor.listGet()
export const gameDetailsUpdate = expressAccessor.listSet(
async (list, _body, _id, _roles, request) => {
request.setTimeout(500000)
- const newList = cloneDeep(list)
+ const newList: Game[] = cloneDeep(list)
const data = await getData()
const parser = new Parser()
@@ -98,3 +114,214 @@ async function getData(): Promise {
}
return data
}
+
+export const gameListGet = expressAccessor.get(async (list) => {
+ const boxSheet = await getSheet("Boxes", new Box(), translationBox)
+
+ const boxList = await boxSheet.getList()
+ if (!boxList) {
+ throw Error("Unable to load boxList")
+ }
+
+ const toBeAsked: Game[] = []
+
+ list.forEach((game) => {
+ const box: Box | undefined = boxList.find((b) => b.gameId === game.id)
+ if (!box) {
+ return
+ }
+ toBeAsked.push({
+ id: game.id,
+ title: game.title,
+ bggId: game.bggId,
+ bggIdAlternative: game.bggIdAlternative,
+ bggPhoto: game.bggPhoto,
+ poufpaf: game.poufpaf,
+ playersMin: game.playersMin,
+ playersMax: game.playersMax,
+ duration: game.duration,
+ type: game.type,
+ } as Game)
+ })
+
+ return toBeAsked
+})
+
+export const gameWithVolunteersListGet = expressAccessor.get(
+ async (list, _body, id): Promise => {
+ if (id <= 0) {
+ throw Error(`L'accès est réservé aux utilisateurs identifiés`)
+ }
+ const volunteerSheet = await getSheet(
+ "Volunteers",
+ new Volunteer(),
+ translationVolunteer
+ )
+ const volunteerList = await volunteerSheet.getList()
+ if (!volunteerList) {
+ throw Error("Unable to load volunteers")
+ }
+
+ const currentVolunteer: Volunteer | undefined = volunteerList.find((v) => v.id === id)
+ if (!currentVolunteer) {
+ throw Error(`Unknown volunteer ${id}`)
+ }
+
+ const boxSheet = await getSheet("Boxes", new Box(), translationBox)
+ const boxList = await boxSheet.getList()
+ if (!boxList) {
+ throw Error("Unable to load boxes")
+ }
+
+ const giftGameIds = list
+ .filter((game) =>
+ every(
+ volunteerList,
+ (v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
+ )
+ )
+ .map((game) => game.id)
+
+ const gamesToLoan = list
+ .filter((game) => currentVolunteer.loanable.includes(game.id))
+ .map((game) => ({
+ ...cloneDeep(game),
+ volunteerNicknames: volunteerToNicknameList(
+ volunteerList.filter((v) => v.loanable.includes(game.id)),
+ volunteerList
+ ),
+ toLoan: true,
+ boxCount: boxList.filter((b) => b.gameId === game.id).length,
+ }))
+
+ const gamesToGift = list
+ .filter(
+ (game) =>
+ giftGameIds.includes(game.id) && currentVolunteer.loanable.includes(game.id)
+ )
+ .map((game) => ({
+ ...cloneDeep(game),
+ volunteerNicknames: volunteerToNicknameList(
+ volunteerList.filter((v) => v.loanable.includes(game.id)),
+ volunteerList
+ ),
+ toLoan: false,
+ boxCount: boxList.filter((b) => b.gameId === game.id).length,
+ }))
+
+ return [...gamesToLoan, ...gamesToGift]
+ }
+)
+
+function volunteerToNicknameList(volunteers: Volunteer[], allVolunteers: Volunteer[]): string[] {
+ return volunteers
+ .map((v) => getUniqueNickname(allVolunteers, v))
+ .sort((a, b) => a.localeCompare(b))
+}
+
+export const gamesToGiveListGet = expressAccessor.get(async (list): Promise => {
+ const volunteerSheet = await getSheet(
+ "Volunteers",
+ new Volunteer(),
+ translationVolunteer
+ )
+ const volunteerList = await volunteerSheet.getList()
+ if (!volunteerList) {
+ throw Error("Unable to load volunteers")
+ }
+
+ const boxSheet = await getSheet("Boxes", new Box(), translationBox)
+ const boxList = await boxSheet.getList()
+ if (!boxList) {
+ throw Error("Unable to load boxes")
+ }
+
+ volunteerList[105].playable = []
+
+ const giftGameTitles = list
+ .filter((game) =>
+ every(
+ volunteerList,
+ (v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
+ )
+ )
+ .map((game) => game.title)
+
+ return giftGameTitles
+})
+type GameCategory = { start: string; end: string; titles: string[] }
+export const gameTitleOrderCategories = expressAccessor.get(
+ async (list): Promise => {
+ const volunteerSheet = await getSheet(
+ "Volunteers",
+ new Volunteer(),
+ translationVolunteer
+ )
+ const volunteerList = await volunteerSheet.getList()
+ if (!volunteerList) {
+ throw Error("Unable to load volunteers")
+ }
+
+ const boxSheet = await getSheet("Boxes", new Box(), translationBox)
+ const boxList = await boxSheet.getList()
+ if (!boxList) {
+ throw Error("Unable to load boxes")
+ }
+
+ volunteerList[105].playable = []
+
+ const giftGameIds = list
+ .filter((game) =>
+ every(
+ volunteerList,
+ (v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
+ )
+ )
+ .map((game) => game.id)
+
+ const gamesToLoan = sortBy(
+ list.filter((game) => !giftGameIds.includes(game.id)).map((game) => cloneDeep(game)),
+ gameTitleOrder
+ )
+
+ const exaustiveCats = mapValues(groupBy(gamesToLoan, gameTitleCategory), (gameList) =>
+ gameList.map((game) => game.title)
+ )
+
+ const cats: GameCategory[] = reduce(
+ Object.entries(exaustiveCats),
+ (res, [exaustiveCat, catTitles]) => {
+ const prevCat = last(res)
+ const prevCount = prevCat?.titles.length || 0
+ if (prevCount === 0 || prevCount + catTitles.length > 10) {
+ const newCat = {
+ start: exaustiveCat,
+ end: exaustiveCat,
+ titles: [...catTitles],
+ }
+ res.push(newCat)
+ } else if (prevCat !== undefined) {
+ prevCat.end = exaustiveCat
+ catTitles.forEach((title) => prevCat.titles.push(title))
+ }
+ return res
+ },
+ [] as GameCategory[]
+ )
+
+ const catList: string[] = cats.map((cat) => `${cat.start}-${cat.end}`)
+
+ console.log(catList.join("\n"))
+
+ const availableGiftIds = giftGameIds.filter((id) =>
+ some(volunteerList, (v: Volunteer) => v.giftable.includes(id))
+ )
+ console.log("availableGiftIds", availableGiftIds)
+
+ gamesToLoan.forEach((g) => gameTitleExactCategory(g))
+
+ return cats
+ }
+)
+
+// _.mapValues(list, cat => cat.length)
diff --git a/src/server/gsheets/localDb.ts b/src/server/gsheets/localDb.ts
index 8fb98e2..b45bfb2 100644
--- a/src/server/gsheets/localDb.ts
+++ b/src/server/gsheets/localDb.ts
@@ -1,9 +1,10 @@
// eslint-disable-next-line max-classes-per-file
import path from "path"
-import _ from "lodash"
+import _, { assign } from "lodash"
import { promises as fs } from "fs"
import { Volunteer } from "../../services/volunteers"
import { Postulant } from "../../services/postulants"
+import { Retex } from "../../services/retex"
const DB_PATH = path.resolve(process.cwd(), "access/db.json")
const DB_TO_LOAD_PATH = path.resolve(process.cwd(), "access/dbToLoad.json")
@@ -275,6 +276,11 @@ function anonimizedDb(_s: States): States {
v.comment = v.id % 3 === 0 ? "Bonjour, j'adore l'initiative!" : ""
})
}
+ if (s.Retex) {
+ ;(s.Retex as Retex[]).forEach((r) => {
+ assign(r, new Retex(), { id: r.id, dayWishes: r.dayWishes })
+ })
+ }
return s
}
diff --git a/src/server/gsheets/tools.ts b/src/server/gsheets/tools.ts
new file mode 100644
index 0000000..a9cba29
--- /dev/null
+++ b/src/server/gsheets/tools.ts
@@ -0,0 +1,16 @@
+import { remove } from "lodash"
+import { Volunteer } from "../../services/volunteers"
+
+export function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
+ const lastnameList = list
+ .filter((v) => v.firstname === volunteer.firstname)
+ .map((v) => v.lastname)
+ let lastnamePrefix = ""
+ while (lastnameList.length > 1) {
+ lastnamePrefix += volunteer.lastname.charAt(lastnamePrefix.length)
+ // eslint-disable-next-line no-loop-func
+ remove(lastnameList, (lastname) => !lastname.startsWith(lastnamePrefix))
+ }
+ const nickname = `${volunteer.firstname}${lastnamePrefix ? ` ${lastnamePrefix}.` : ""}`
+ return nickname
+}
diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts
index 76aa05c..7593f26 100644
--- a/src/server/gsheets/volunteers.ts
+++ b/src/server/gsheets/volunteers.ts
@@ -1,6 +1,6 @@
import path from "path"
import * as fs from "fs"
-import { assign, cloneDeep, max, omit, pick, remove } from "lodash"
+import { assign, cloneDeep, max, omit, pick } from "lodash"
import bcrypt from "bcrypt"
import sgMail from "@sendgrid/mail"
@@ -24,6 +24,7 @@ import {
} from "../../services/volunteers"
import { canonicalEmail, canonicalMobile, trim, validMobile } from "../../utils/standardization"
import { getJwt } from "../secure"
+import { getUniqueNickname } from "./tools"
const expressAccessor = new ExpressAccessors(
"Volunteers",
@@ -577,17 +578,3 @@ export const volunteerLoanSet = expressAccessor.set(async (list, body, id) => {
} as VolunteerLoan,
}
})
-
-function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
- const lastnameList = list
- .filter((v) => v.firstname === volunteer.firstname)
- .map((v) => v.lastname)
- let lastnamePrefix = ""
- while (lastnameList.length > 1) {
- lastnamePrefix += volunteer.lastname.charAt(lastnamePrefix.length)
- // eslint-disable-next-line no-loop-func
- remove(lastnameList, (lastname) => !lastname.startsWith(lastnamePrefix))
- }
- const nickname = `${volunteer.firstname}${lastnamePrefix ? ` ${lastnamePrefix}.` : ""}`
- return nickname
-}
diff --git a/src/server/index.ts b/src/server/index.ts
index d70b449..06cf2bb 100755
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -19,7 +19,13 @@ import certbotRouter from "../routes/certbot"
import { hasSecret, secure } from "./secure"
import { announcementListGet } from "./gsheets/announcements"
import { detailedBoxListGet } from "./gsheets/boxes"
-import { gameListGet, gameDetailsUpdate } from "./gsheets/games"
+import {
+ gameListGet,
+ gameDetailsUpdate,
+ gameWithVolunteersListGet,
+ gamesToGiveListGet,
+ gameTitleOrderCategories,
+} from "./gsheets/games"
import { postulantAdd } from "./gsheets/postulants"
import { teamListGet } from "./gsheets/teams"
import {
@@ -98,6 +104,8 @@ app.get(
app.get("/GameDetailsUpdate", gameDetailsUpdate)
app.get("/BoxDetailedListGet", detailedBoxListGet)
app.get("/GameListGet", gameListGet)
+app.get("/GamesToGiveListGet", gamesToGiveListGet)
+app.get("/GameTitleOrderCategories", gameTitleOrderCategories)
app.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
app.get("/WishListGet", wishListGet)
app.post("/WishAdd", wishAdd)
@@ -109,6 +117,7 @@ app.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
// Secured APIs
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
+app.get("/GameWithVolunteersListGet", secure as RequestHandler, gameWithVolunteersListGet)
app.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
app.post("/RetexSet", secure as RequestHandler, retexSet)
app.get("/TeamListGet", teamListGet)
diff --git a/src/services/accessors.ts b/src/services/accessors.ts
index b552e64..add1239 100644
--- a/src/services/accessors.ts
+++ b/src/services/accessors.ts
@@ -90,6 +90,37 @@ export default class ServiceAccessors<
}
}
+ securedCustomListGet, OutputType>(
+ apiName: string
+ ): (
+ jwt: string,
+ ...params: InputElements
+ ) => Promise<{
+ data?: any
+ error?: Error
+ }> {
+ interface ElementGetResponse {
+ data?: any
+ error?: Error
+ }
+ return async (jwt: string, ...params: InputElements): Promise => {
+ try {
+ const auth = { headers: { Authorization: `Bearer ${jwt}` } }
+ const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
+ const { data } = await axios.get(
+ `${config.API_URL}/${this.elementName}${apiName}`,
+ { ...fullAxiosConfig, params }
+ )
+ if (data.error) {
+ throw Error(data.error)
+ }
+ return { data } as { data: OutputType }
+ } catch (error) {
+ return { error: error as Error }
+ }
+ }
+ }
+
// eslint-disable-next-line @typescript-eslint/ban-types
add(): (volunteerWithoutId: ElementNoId) => Promise<{
data?: Element
diff --git a/src/services/games.ts b/src/services/games.ts
index f21c0fb..ad91f03 100644
--- a/src/services/games.ts
+++ b/src/services/games.ts
@@ -1,3 +1,4 @@
+/* eslint-disable max-classes-per-file */
export class Game {
id = 0
@@ -26,6 +27,14 @@ export class Game {
toBeKnown = false
}
+export class GameWithVolunteers extends Game {
+ volunteerNicknames: string[] = []
+
+ toLoan = true
+
+ boxCount = 0
+}
+
export const translationGame: { [k in keyof Game]: string } = {
id: "id",
title: "titre",
diff --git a/src/services/gamesAccessors.ts b/src/services/gamesAccessors.ts
index 959556f..0adadc8 100644
--- a/src/services/gamesAccessors.ts
+++ b/src/services/gamesAccessors.ts
@@ -1,5 +1,5 @@
import ServiceAccessors from "./accessors"
-import { elementName, Game, GameWithoutId } from "./games"
+import { elementName, Game, GameWithVolunteers, GameWithoutId } from "./games"
const serviceAccessors = new ServiceAccessors(elementName)
@@ -9,3 +9,8 @@ export const gameListGet = serviceAccessors.listGet()
// export const gameSet = serviceAccessors.set()
export const gameDetailsUpdate = serviceAccessors.securedCustomGet<[], Game[]>("DetailsUpdate")
+
+export const gameWithVolunteersListGet = serviceAccessors.securedCustomListGet<
+ [],
+ GameWithVolunteers[]
+>("WithVolunteersListGet")
diff --git a/src/store/boxList.ts b/src/store/boxList.ts
index 8b84459..442196d 100644
--- a/src/store/boxList.ts
+++ b/src/store/boxList.ts
@@ -1,7 +1,7 @@
import { PayloadAction, createSlice, createEntityAdapter, createSelector } from "@reduxjs/toolkit"
import { sortedUniqBy, sortBy } from "lodash"
-import { StateRequest, toastError, elementListFetch } from "./utils"
+import { StateRequest, toastError, elementListFetch, gameTitleOrder } from "./utils"
import { DetailedBox } from "../services/boxes"
import { AppThunk, AppState, EntitiesRequest } from "."
import { detailedBoxListGet } from "../services/boxesAccessors"
@@ -59,9 +59,10 @@ export const selectBoxList = createSelector(
}
)
-export const selectSortedUniqueDetailedBoxes = createSelector(selectBoxList, (boxes) =>
- sortedUniqBy(sortBy(boxes, "title"), "title")
-)
+export const selectSortedUniqueDetailedBoxes = createSelector(selectBoxList, (boxes) => {
+ const validBoxes = boxes.filter((box) => box) as DetailedBox[]
+ return sortedUniqBy(sortBy(validBoxes, gameTitleOrder), gameTitleOrder)
+})
export const selectContainerSortedDetailedBoxes = createSelector(selectBoxList, (boxes) =>
sortBy(boxes, "container")
diff --git a/src/store/gameWithVolunteersList.ts b/src/store/gameWithVolunteersList.ts
new file mode 100644
index 0000000..f34ee20
--- /dev/null
+++ b/src/store/gameWithVolunteersList.ts
@@ -0,0 +1,73 @@
+import { PayloadAction, createSlice, createEntityAdapter, createSelector } from "@reduxjs/toolkit"
+import { sortedUniqBy, sortBy } from "lodash"
+
+import { StateRequest, toastError, elementListFetch, gameTitleOrder } from "./utils"
+import { GameWithVolunteers } from "../services/games"
+import { AppThunk, AppState, EntitiesRequest } from "."
+import { gameWithVolunteersListGet } from "../services/gamesAccessors"
+
+const gameAdapter = createEntityAdapter()
+
+export const initialState = gameAdapter.getInitialState({
+ readyStatus: "idle",
+} as StateRequest)
+
+const gameWithVolunteersList = createSlice({
+ name: "gameWithVolunteersList",
+ initialState,
+ reducers: {
+ getRequesting: (state) => {
+ state.readyStatus = "request"
+ },
+ getSuccess: (state, { payload }: PayloadAction) => {
+ state.readyStatus = "success"
+ gameAdapter.setAll(state, payload)
+ },
+ getFailure: (state, { payload }: PayloadAction) => {
+ state.readyStatus = "failure"
+ state.error = payload
+ },
+ },
+})
+
+export default gameWithVolunteersList.reducer
+export const { getRequesting, getSuccess, getFailure } = gameWithVolunteersList.actions
+
+export const fetchGameWithVolunteersList = elementListFetch(
+ gameWithVolunteersListGet,
+ getRequesting,
+ getSuccess,
+ getFailure,
+ (error: Error) => toastError(`Erreur lors du chargement des jeux JAV: ${error.message}`)
+)
+
+const shouldFetchGameWithVolunteersList = (state: AppState) =>
+ state.gameWithVolunteersList.readyStatus !== "success"
+
+export const fetchGameWithVolunteersListIfNeed = (): AppThunk => (dispatch, getState) => {
+ const { jwt } = getState().auth
+ if (shouldFetchGameWithVolunteersList(getState()))
+ return dispatch(fetchGameWithVolunteersList(jwt))
+
+ return null
+}
+
+export const selectGameWithVolunteersListState = (
+ state: AppState
+): EntitiesRequest => state.gameWithVolunteersList
+
+export const selectGameWithVolunteersList = createSelector(
+ selectGameWithVolunteersListState,
+ ({ ids, entities, readyStatus }) => {
+ if (readyStatus !== "success") return []
+ return ids.map((id) => entities[id])
+ }
+)
+
+export const selectSortedUniqueGamesWithVolunteers = createSelector(
+ selectGameWithVolunteersList,
+ (games) => {
+ const gameWithVolunteers = games.filter((game) => game) as GameWithVolunteers[]
+ return sortedUniqBy(sortBy(gameWithVolunteers, gameTitleOrder), gameTitleOrder)
+ }
+)
diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts
index 46acc4d..72cafa2 100644
--- a/src/store/rootReducer.ts
+++ b/src/store/rootReducer.ts
@@ -5,6 +5,7 @@ import announcementList from "./announcementList"
import auth from "./auth"
import boxList from "./boxList"
import gameList from "./gameList"
+import gameWithVolunteersList from "./gameWithVolunteersList"
import gameDetailsUpdate from "./gameDetailsUpdate"
import miscDiscordInvitation from "./miscDiscordInvitation"
import miscMeetingDateList from "./miscMeetingDateList"
@@ -39,6 +40,7 @@ export default (history: History) => ({
auth,
boxList,
gameList,
+ gameWithVolunteersList,
gameDetailsUpdate,
miscDiscordInvitation,
miscMeetingDateList,
diff --git a/src/store/utils.ts b/src/store/utils.ts
index 6d29f03..aa3d81c 100644
--- a/src/store/utils.ts
+++ b/src/store/utils.ts
@@ -4,6 +4,7 @@ import {
ActionCreatorWithPayload,
ThunkDispatch,
} from "@reduxjs/toolkit"
+import { find } from "lodash"
import { toast } from "react-toastify"
import { AppState, AppThunk } from "."
@@ -143,3 +144,129 @@ export function elementValueFetch(
}
}
}
+
+export function gameTitleOrder(boxOrGame: { title: string }): string {
+ return boxOrGame.title
+ .normalize("NFD")
+ .replace(/[\u0300-\u036f]/g, "")
+ .toLowerCase()
+ .replace(/^[^a-z0-9]+/, "")
+ .replace(/^(le |la |les |l'|the )/, "")
+ .replace(/[^a-z0-9]/g, "")
+}
+
+export function gameTitleCategory(boxOrGame: { title: string }, length = 3): string {
+ return gameTitleOrder(boxOrGame)
+ .substring(0, length)
+ .replace(/(?<=[0-9]).+/, "")
+ .toUpperCase()
+}
+
+export function gameTitleExactCategory(boxOrGame: { title: string }): string {
+ const cats = [
+ ["1", "6"],
+ ["7", "AB"],
+ ["AC", "AK"],
+ ["ALA", "ALO"],
+ ["ALP", "AP"],
+ ["AQ", "ARG"],
+ ["ARH", "AT"],
+ ["AU", "AV"],
+ ["AW", "BAR"],
+ ["BAS", "BER"],
+ ["BES", "BLI"],
+ ["BLJ", "BRE"],
+ ["BRF", "CAC"],
+ ["CAD", "CAP"],
+ ["CAQ", "CEM"],
+ ["CEN", "CHIN"],
+ ["CHJ", "CK"],
+ ["CL"],
+ ["COA", "COL"],
+ ["COM"],
+ ["CON", "COP"],
+ ["COQ", "CRA"],
+ ["CRB", "DEB"],
+ ["DEC", "DES"],
+ ["DET", "DIC"],
+ ["DID", "DOM"],
+ ["DON", "DR"],
+ ["DS", "ELG"],
+ ["ELH", "EVE"],
+ ["EVD", "FAM"],
+ ["FAN", "FIC"],
+ ["FID", "FOL"],
+ ["FOM", "FUM"],
+ ["FUN", "GEE"],
+ ["GED", "GLA"],
+ ["GLB", "GQ"],
+ ["GR", "HAN"],
+ ["HAO", "HIM"],
+ ["HIN", "IL"],
+ ["IM", "IS"],
+ ["IT", "JE"],
+ ["JD", "KAB"],
+ ["KAC", "KEM"],
+ ["KEN", "KILL"],
+ ["KIM", "KOD"],
+ ["KOE", "LAB"],
+ ["LAC", "LIC"],
+ ["LID", "LIP"],
+ ["LIQ", "LOQ"],
+ ["LOR", "LZ"],
+ ["MAA", "MAN"],
+ ["MAO", "MED"],
+ ["MEE", "MIM"],
+ ["MINE", "MOR"],
+ ["MOS", "MYS"],
+ ["MYT", "NE"],
+ ["ND", "ODD"],
+ ["ODE", "ONE"],
+ ["ONF", "OU"],
+ ["OV", "PAN"],
+ ["PAO", "PEL"],
+ ["PEM", "PIO"],
+ ["PIP", "PLZ"],
+ ["PO", "PRI"],
+ ["PRJ", "PZ"],
+ ["QI", "RAT"],
+ ["RAU", "RI"],
+ ["RJ", "ROO"],
+ ["ROP", "SAL"],
+ ["SAM", "SEI"],
+ ["SEJ", "SIC"],
+ ["SID", "SL"],
+ ["SM", "SO"],
+ ["SPA", "SPX"],
+ ["SPY", "STE"],
+ ["STD", "SUN"],
+ ["SUO", "TAF"],
+ ["TAG", "TD"],
+ ["TE", "TIC"],
+ ["TID", "TIL"],
+ ["TIM"],
+ ["TIN", "TO"],
+ ["TP", "TRO"],
+ ["TRP", "UNE"],
+ ["UND", "VEM"],
+ ["VEN", "WAT"],
+ ["WAU", "WH"],
+ ["WI", "YE"],
+ ["YOGA", "ZOO"],
+ ]
+
+ const gameCat = gameTitleCategory(boxOrGame, 4)
+
+ const foundCat = find(cats, (cat) =>
+ cat[1]
+ ? gameCat.substring(0, cat[0].length) >= cat[0] &&
+ gameCat.substring(0, cat[1].length) <= `${cat[1]}`
+ : gameCat.substring(0, cat[0].length) === cat[0]
+ )
+
+ if (!foundCat) {
+ console.log("no game found for ", foundCat, boxOrGame.title)
+ }
+
+ return foundCat ? `${foundCat[0]}-${foundCat[1]}` : gameCat
+}