diff --git a/src/components/Admin/DbEdit.tsx b/src/components/Admin/DbEdit.tsx
index e4fbbea..4256440 100644
--- a/src/components/Admin/DbEdit.tsx
+++ b/src/components/Admin/DbEdit.tsx
@@ -1,3 +1,4 @@
+import { max } from "lodash"
import { FC, memo } from "react"
import { useSelector } from "react-redux"
import withUserConnected from "../../utils/withUserConnected"
@@ -9,13 +10,18 @@ import useAction from "../../utils/useAction"
import { fetchVolunteerSetIfNeed } from "../../store/volunteerSet"
import { Volunteer } from "../../services/volunteers"
import styles from "./styles.module.scss"
+import { fetchVolunteerAddNewIfNeed } from "../../store/volunteerAddNew"
const DbEdit: FC = (): JSX.Element => {
const volunteers = useSelector(selectVolunteerList)
const saveVolunteer = useAction(fetchVolunteerSetIfNeed)
+ const addVolunteer = useAction(fetchVolunteerAddNewIfNeed)
if (!volunteers) {
return <>No member found>
}
+ const nextId = (max(volunteers.map((v) => v.id)) || 0) + 1
+ const nextVolunteer = new Volunteer()
+ nextVolunteer.id = nextId
return (
{volunteers.map((volunteer: Volunteer) => (
@@ -25,6 +31,12 @@ const DbEdit: FC = (): JSX.Element => {
volunteer={volunteer}
/>
))}
+
)
}
diff --git a/src/components/Admin/MemberEdit.tsx b/src/components/Admin/MemberEdit.tsx
index ee20498..0a538a9 100644
--- a/src/components/Admin/MemberEdit.tsx
+++ b/src/components/Admin/MemberEdit.tsx
@@ -10,16 +10,29 @@ import { toastError } from "../../store/utils"
interface Props {
volunteer: Volunteer
saveVolunteer: (newVolunteer: Partial) => void
+ addBefore?: () => void
}
-const MemberEdit: FC = ({ volunteer, saveVolunteer }): JSX.Element => {
+const MemberEdit: FC = ({ volunteer, saveVolunteer, addBefore }): JSX.Element => {
const [localVolunteer, setLocalVolunteer] = useState(volunteer)
+ async function addAndWait() {
+ if (addBefore) {
+ addBefore()
+ await new Promise((resolve) => {
+ setTimeout(() => resolve(), 1000)
+ })
+ }
+ }
+
const stringDispatch =
(propName: string) =>
- (e: React.ChangeEvent): void => {
- saveVolunteer({ id: localVolunteer.id, [propName]: e.target.value })
- setLocalVolunteer({ ...localVolunteer, [propName]: e.target.value })
+ async (e: React.ChangeEvent): Promise => {
+ const rawValue = e.target.value
+ const value = rawValue
+ await addAndWait()
+ saveVolunteer({ id: localVolunteer.id, [propName]: rawValue })
+ setLocalVolunteer({ ...localVolunteer, [propName]: value })
}
function stringInput(id: string, value: string): JSX.Element {
@@ -40,13 +53,15 @@ const MemberEdit: FC = ({ volunteer, saveVolunteer }): JSX.Element => {
const numberDispatch =
(propName: string) =>
- (e: React.ChangeEvent): void => {
- const value: number = +e.target.value
+ async (e: React.ChangeEvent): Promise => {
+ const rawValue = e.target.value
+ const value: number = +rawValue
if (!isFinite(value)) {
toastError("Should be a number")
return
}
- saveVolunteer({ id: localVolunteer.id, [propName]: +value })
+ await addAndWait()
+ saveVolunteer({ id: localVolunteer.id, [propName]: rawValue })
setLocalVolunteer({ ...localVolunteer, [propName]: +value })
}
@@ -68,9 +83,11 @@ const MemberEdit: FC = ({ volunteer, saveVolunteer }): JSX.Element => {
const booleanDispatch =
(propName: string) =>
- (e: React.ChangeEvent): void => {
- const value: boolean = e.target.value !== "0" && e.target.value !== ""
- saveVolunteer({ id: localVolunteer.id, [propName]: value })
+ async (e: React.ChangeEvent): Promise => {
+ const rawValue = e.target.value
+ const value: boolean = rawValue !== "0" && rawValue !== ""
+ await addAndWait()
+ saveVolunteer({ id: localVolunteer.id, [propName]: rawValue })
setLocalVolunteer({ ...localVolunteer, [propName]: value })
}
@@ -112,6 +129,9 @@ const MemberEdit: FC = ({ volunteer, saveVolunteer }): JSX.Element => {
)
}
+MemberEdit.defaultProps = {
+ addBefore: undefined,
+}
export default withUserRole(ROLES.ADMIN, memo(withUserConnected(MemberEdit)))
export const fetchFor = []
diff --git a/src/components/RegisterForm/index.tsx b/src/components/RegisterForm/index.tsx
index 984823c..260f49d 100644
--- a/src/components/RegisterForm/index.tsx
+++ b/src/components/RegisterForm/index.tsx
@@ -133,7 +133,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
)
const { error: volunteerError, entities: volunteer } = useSelector(
- (state: AppState) => state.volunteerAdd,
+ (state: AppState) => state.volunteerPartialAdd,
shallowEqual
)
diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts
index 5349c28..536f6ec 100644
--- a/src/server/gsheets/volunteers.ts
+++ b/src/server/gsheets/volunteers.ts
@@ -1,4 +1,4 @@
-import { assign, cloneDeep, omit, pick } from "lodash"
+import { assign, cloneDeep, max, omit, pick } from "lodash"
import bcrypt from "bcrypt"
import sgMail from "@sendgrid/mail"
@@ -31,6 +31,26 @@ export const volunteerListGet = expressAccessor.get(async (list, _body, id) => {
return list
})
+export const volunteerAddNew = expressAccessor.add(async (list, _body, _id, roles) => {
+ if (!roles.includes("admin")) {
+ throw Error(`À moins d'être admin, on ne peut pas modifier n'importe quel utilisateur`)
+ }
+ const id = (max(list.map((v) => v.id)) || 0) + 1
+ const password = generatePassword()
+ const passwordHash = await bcrypt.hash(password, 10)
+
+ const newVolunteer: Volunteer = new Volunteer()
+ newVolunteer.id = id
+ newVolunteer.password1 = passwordHash
+ newVolunteer.password2 = passwordHash
+ newVolunteer.firstname = password
+
+ return {
+ toDatabase: newVolunteer,
+ toCaller: newVolunteer,
+ }
+})
+
export const volunteerSet = expressAccessor.set(async (list, body, _id, roles) => {
if (!roles.includes("admin")) {
throw Error(`À moins d'être admin, on ne peut pas modifier n'importe quel utilisateur`)
diff --git a/src/server/index.ts b/src/server/index.ts
index c5ecdce..e264aeb 100755
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -35,6 +35,7 @@ import {
volunteerTeamAssignSet,
volunteerListGet,
volunteerKnowledgeSet,
+ volunteerAddNew,
} from "./gsheets/volunteers"
import { wishListGet, wishAdd } from "./gsheets/wishes"
import config from "../config"
@@ -114,6 +115,7 @@ app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishe
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
// Admin only
+app.post("/VolunteerAddNew", secure as RequestHandler, volunteerAddNew)
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
app.get("/GameDetailsUpdate", secure as RequestHandler, gameDetailsUpdate)
diff --git a/src/services/volunteersAccessors.ts b/src/services/volunteersAccessors.ts
index 194943e..dea12eb 100644
--- a/src/services/volunteersAccessors.ts
+++ b/src/services/volunteersAccessors.ts
@@ -19,6 +19,7 @@ export const volunteerDiscordIdGet = serviceAccessors.securedCustomGet<
[number],
VolunteerDiscordId
>("DiscordId")
+export const volunteerAddNew = serviceAccessors.securedCustomPost<[]>("AddNew")
export const volunteerPartialAdd = serviceAccessors.customPost<[Partial]>("PartialAdd")
export const volunteerSet = serviceAccessors.securedCustomPost<[Partial]>("Set")
diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts
index da91969..b05d40c 100644
--- a/src/store/rootReducer.ts
+++ b/src/store/rootReducer.ts
@@ -11,7 +11,7 @@ import miscMeetingDateList from "./miscMeetingDateList"
import postulantAdd from "./postulantAdd"
import teamList from "./teamList"
import ui from "./ui"
-import volunteerAdd from "./volunteerPartialAdd"
+import volunteerPartialAdd from "./volunteerPartialAdd"
import volunteerAsksSet from "./volunteerAsksSet"
import volunteerDayWishesSet from "./volunteerDayWishesSet"
import volunteerDiscordId from "./volunteerDiscordId"
@@ -39,7 +39,7 @@ export default (history: History) => ({
postulantAdd,
teamList,
ui,
- volunteerAdd,
+ volunteerPartialAdd,
volunteerAsksSet,
volunteerDayWishesSet,
volunteerDiscordId,
diff --git a/src/store/utils.ts b/src/store/utils.ts
index 395afba..6d29f03 100644
--- a/src/store/utils.ts
+++ b/src/store/utils.ts
@@ -64,8 +64,8 @@ export function elementFetch>(
}
}
-export function elementAddFetch(
- elementAddService: (elementWithoutId: Omit) => Promise<{
+export function elementAddFetch>(
+ elementAddService: (...idArgs: ServiceInput) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,
@@ -74,12 +74,12 @@ export function elementAddFetch(
getFailure: ActionCreatorWithPayload,
errorMessage?: (error: Error) => void,
successMessage?: () => void
-): (elementWithoutId: Omit) => AppThunk {
- return (elementWithoutId: Omit): AppThunk =>
+): (...idArgs: ServiceInput) => AppThunk {
+ return (...idArgs: ServiceInput): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
- const { error, data } = await elementAddService(elementWithoutId)
+ const { error, data } = await elementAddService(...idArgs)
if (error) {
dispatch(getFailure(error.message))
diff --git a/src/store/volunteerAddNew.ts b/src/store/volunteerAddNew.ts
new file mode 100644
index 0000000..7a952bb
--- /dev/null
+++ b/src/store/volunteerAddNew.ts
@@ -0,0 +1,45 @@
+import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
+
+import { StateRequest, elementAddFetch, toastError } from "./utils"
+import { Volunteer } from "../services/volunteers"
+import { volunteerAddNew } from "../services/volunteersAccessors"
+import { AppThunk } from "."
+
+const volunteerAdapter = createEntityAdapter()
+
+const volunteerAddNewSlice = createSlice({
+ name: "volunteerAddNew",
+ initialState: volunteerAdapter.getInitialState({
+ readyStatus: "idle",
+ } as StateRequest),
+ reducers: {
+ getRequesting: (state) => {
+ state.readyStatus = "request"
+ },
+ getSuccess: (state, { payload }: PayloadAction) => {
+ state.readyStatus = "success"
+ volunteerAdapter.setOne(state, payload)
+ },
+ getFailure: (state, { payload }: PayloadAction) => {
+ state.readyStatus = "failure"
+ state.error = payload
+ },
+ },
+})
+
+export default volunteerAddNewSlice.reducer
+export const { getRequesting, getSuccess, getFailure } = volunteerAddNewSlice.actions
+
+export const fetchVolunteerAddNew = elementAddFetch(
+ volunteerAddNew,
+ getRequesting,
+ getSuccess,
+ getFailure,
+ () => toastError("Erreur d'ajout !"),
+ () => null
+)
+
+export const fetchVolunteerAddNewIfNeed = (): AppThunk => (dispatch, getState) => {
+ const { jwt } = getState().auth
+ return dispatch(fetchVolunteerAddNew(jwt))
+}
diff --git a/src/store/volunteerPartialAdd.ts b/src/store/volunteerPartialAdd.ts
index a26ef63..9e1ca61 100644
--- a/src/store/volunteerPartialAdd.ts
+++ b/src/store/volunteerPartialAdd.ts
@@ -1,13 +1,13 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
-import { StateRequest, elementAddFetch } from "./utils"
+import { StateRequest, elementAddFetch, toastError } from "./utils"
import { Volunteer } from "../services/volunteers"
import { volunteerPartialAdd } from "../services/volunteersAccessors"
const volunteerAdapter = createEntityAdapter()
const volunteerPartialAddSlice = createSlice({
- name: "volunteerAdd",
+ name: "volunteerPartialAdd",
initialState: volunteerAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),
@@ -34,6 +34,6 @@ export const fetchVolunteerPartialAdd = elementAddFetch(
getRequesting,
getSuccess,
getFailure,
- () => null,
+ () => toastError("Erreur d'inscription !"),
() => null
)
diff --git a/src/store/volunteerSet.ts b/src/store/volunteerSet.ts
index 949f827..a4e88da 100644
--- a/src/store/volunteerSet.ts
+++ b/src/store/volunteerSet.ts
@@ -41,19 +41,14 @@ export const fetchVolunteerSet = elementFetch(
() => toastSuccess("Bénévole modifié !")
)
-const shouldFetchVolunteerSet = (_state: AppState) => true
-
export const fetchVolunteerSetIfNeed =
(newPartialVolunteer: Partial): AppThunk =>
(dispatch, getState) => {
const { jwt } = getState().auth
- if (shouldFetchVolunteerSet(getState()))
- return dispatch(fetchVolunteerSet(jwt, newPartialVolunteer))
-
- return null
+ return dispatch(fetchVolunteerSet(jwt, newPartialVolunteer))
}
export const selectVolunteerSet = createSelector(
(state: AppState) => state,
- (state): string | undefined => state.volunteerSet?.entity?.discordId
+ (state): Volunteer | undefined => state.volunteerSet?.entity
)