mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
Add db new line support
This commit is contained in:
parent
da643df6a6
commit
028d703e98
@ -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 (
|
||||
<ul className={styles.list}>
|
||||
{volunteers.map((volunteer: Volunteer) => (
|
||||
@ -25,6 +31,12 @@ const DbEdit: FC = (): JSX.Element => {
|
||||
volunteer={volunteer}
|
||||
/>
|
||||
))}
|
||||
<MemberEdit
|
||||
key={nextId}
|
||||
addBefore={addVolunteer}
|
||||
saveVolunteer={saveVolunteer}
|
||||
volunteer={nextVolunteer}
|
||||
/>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
@ -10,16 +10,29 @@ import { toastError } from "../../store/utils"
|
||||
interface Props {
|
||||
volunteer: Volunteer
|
||||
saveVolunteer: (newVolunteer: Partial<Volunteer>) => void
|
||||
addBefore?: () => void
|
||||
}
|
||||
|
||||
const MemberEdit: FC<Props> = ({ volunteer, saveVolunteer }): JSX.Element => {
|
||||
const MemberEdit: FC<Props> = ({ volunteer, saveVolunteer, addBefore }): JSX.Element => {
|
||||
const [localVolunteer, setLocalVolunteer] = useState(volunteer)
|
||||
|
||||
async function addAndWait() {
|
||||
if (addBefore) {
|
||||
addBefore()
|
||||
await new Promise<void>((resolve) => {
|
||||
setTimeout(() => resolve(), 1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const stringDispatch =
|
||||
(propName: string) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
saveVolunteer({ id: localVolunteer.id, [propName]: e.target.value })
|
||||
setLocalVolunteer({ ...localVolunteer, [propName]: e.target.value })
|
||||
async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
|
||||
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<Props> = ({ volunteer, saveVolunteer }): JSX.Element => {
|
||||
|
||||
const numberDispatch =
|
||||
(propName: string) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const value: number = +e.target.value
|
||||
async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
|
||||
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<Props> = ({ volunteer, saveVolunteer }): JSX.Element => {
|
||||
|
||||
const booleanDispatch =
|
||||
(propName: string) =>
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const value: boolean = e.target.value !== "0" && e.target.value !== ""
|
||||
saveVolunteer({ id: localVolunteer.id, [propName]: value })
|
||||
async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
|
||||
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<Props> = ({ volunteer, saveVolunteer }): JSX.Element => {
|
||||
)
|
||||
}
|
||||
|
||||
MemberEdit.defaultProps = {
|
||||
addBefore: undefined,
|
||||
}
|
||||
export default withUserRole(ROLES.ADMIN, memo(withUserConnected(MemberEdit)))
|
||||
|
||||
export const fetchFor = []
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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`)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -19,6 +19,7 @@ export const volunteerDiscordIdGet = serviceAccessors.securedCustomGet<
|
||||
[number],
|
||||
VolunteerDiscordId
|
||||
>("DiscordId")
|
||||
export const volunteerAddNew = serviceAccessors.securedCustomPost<[]>("AddNew")
|
||||
export const volunteerPartialAdd = serviceAccessors.customPost<[Partial<Volunteer>]>("PartialAdd")
|
||||
|
||||
export const volunteerSet = serviceAccessors.securedCustomPost<[Partial<Volunteer>]>("Set")
|
||||
|
@ -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,
|
||||
|
@ -64,8 +64,8 @@ export function elementFetch<Element, ServiceInput extends Array<any>>(
|
||||
}
|
||||
}
|
||||
|
||||
export function elementAddFetch<Element>(
|
||||
elementAddService: (elementWithoutId: Omit<Element, "id">) => Promise<{
|
||||
export function elementAddFetch<Element, ServiceInput extends Array<any>>(
|
||||
elementAddService: (...idArgs: ServiceInput) => Promise<{
|
||||
data?: Element | undefined
|
||||
error?: Error | undefined
|
||||
}>,
|
||||
@ -74,12 +74,12 @@ export function elementAddFetch<Element>(
|
||||
getFailure: ActionCreatorWithPayload<string, string>,
|
||||
errorMessage?: (error: Error) => void,
|
||||
successMessage?: () => void
|
||||
): (elementWithoutId: Omit<Element, "id">) => AppThunk {
|
||||
return (elementWithoutId: Omit<Element, "id">): 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))
|
||||
|
45
src/store/volunteerAddNew.ts
Normal file
45
src/store/volunteerAddNew.ts
Normal file
@ -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<Volunteer>()
|
||||
|
||||
const volunteerAddNewSlice = createSlice({
|
||||
name: "volunteerAddNew",
|
||||
initialState: volunteerAdapter.getInitialState({
|
||||
readyStatus: "idle",
|
||||
} as StateRequest),
|
||||
reducers: {
|
||||
getRequesting: (state) => {
|
||||
state.readyStatus = "request"
|
||||
},
|
||||
getSuccess: (state, { payload }: PayloadAction<Volunteer>) => {
|
||||
state.readyStatus = "success"
|
||||
volunteerAdapter.setOne(state, payload)
|
||||
},
|
||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||
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))
|
||||
}
|
@ -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<Volunteer>()
|
||||
|
||||
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
|
||||
)
|
||||
|
@ -41,19 +41,14 @@ export const fetchVolunteerSet = elementFetch(
|
||||
() => toastSuccess("Bénévole modifié !")
|
||||
)
|
||||
|
||||
const shouldFetchVolunteerSet = (_state: AppState) => true
|
||||
|
||||
export const fetchVolunteerSetIfNeed =
|
||||
(newPartialVolunteer: Partial<Volunteer>): 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
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user