Adds redux list add example

This commit is contained in:
forceoranj 2021-11-10 00:47:24 +01:00
parent 2616b109d7
commit 1ddb710a6c
19 changed files with 1809 additions and 1141 deletions

View File

@ -1,3 +1,4 @@
/* eslint-disable max-classes-per-file */
import * as _ from "lodash"
import path from "path"
import { promises as fs } from "fs"
@ -329,6 +330,20 @@ class Test {
tictactoe: boolean[] = []
}
// class Membre {
// membreId = 0
// nom = ""
// prenom = ""
// mail = ""
// telephone = ""
// photo = ""
// }
// Can't run it on every test, it requires private access to a google sheet
async function testGSheetAPi(): Promise<void> {
const dataset: Test[] = [
@ -368,6 +383,15 @@ async function testGSheetAPi(): Promise<void> {
},
]
// console.log("Lecture des Membres...")
// const datasetMembresLu = await getList<Membre>("Membres", new Membre())
// if (!datasetMembresLu) {
// console.log("ECHEC de la lecture des membres", datasetMembresLu)
// return
// }
// console.log("Extraction des membres réussie")
// await fs.writeFile("membres.json", JSON.stringify(datasetMembresLu))
console.log("Test d'écriture...")
const resultatEcriture = await setList<Test>("Tests de l'API", dataset)
if (!resultatEcriture) {

View File

@ -99,6 +99,7 @@
"react-router": "^5.2.0",
"react-router-config": "^5.1.1",
"react-router-dom": "^5.3.0",
"react-toastify": "^8.1.0",
"readline": "^1.3.0",
"redux-thunk": "^2.3.0",
"serialize-javascript": "^6.0.0",

View File

@ -1,12 +1,14 @@
import { Link } from "react-router-dom"
import { RouteConfig, renderRoutes } from "react-router-config"
import { Helmet } from "react-helmet"
import { ToastContainer } from "react-toastify"
import logo from "../static/logo.svg"
import config from "../config"
// Import your global styles here
import "normalize.css/normalize.css"
import styles from "./styles.module.scss"
import "react-toastify/dist/ReactToastify.css"
interface Route {
route: { routes: RouteConfig[] }
@ -24,6 +26,7 @@ const App = ({ route }: Route): JSX.Element => (
<hr />
{/* Child routes won't render without this */}
{renderRoutes(route.routes)}
<ToastContainer />
</div>
)

View File

@ -0,0 +1,20 @@
/**
* @jest-environment jsdom
*/
import { render } from "@testing-library/react"
import { MemoryRouter } from "react-router-dom"
import AddEnvie from "../index"
describe("<AddEnvie />", () => {
it("renders", () => {
const dispatch = jest.fn()
const tree = render(
<MemoryRouter>
<AddEnvie dispatch={dispatch} />
</MemoryRouter>
).container.firstChild
expect(tree).toMatchSnapshot()
})
})

View File

@ -0,0 +1,69 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<AddEnvie /> renders 1`] = `
<section
class="EnvieList"
>
<h2>
Ajouter une nouvelle envie
</h2>
<form>
<label
for="postDomaine"
>
Domaine:
<input
id="postDomaine"
name="postDomaine"
type="text"
value=""
/>
</label>
<label
for="postEnvies"
>
Envies:
<textarea
id="postEnvies"
name="postEnvies"
/>
</label>
<label
for="postPrecisions"
>
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
/>
</label>
<label
for="postEquipes"
>
Equipes:
<input
id="postEquipes"
name="postEquipes"
type="text"
value=""
/>
</label>
<label
for="postDateAjout"
>
DateAjout:
<input
id="postDateAjout"
name="postDateAjout"
type="date"
value=""
/>
</label>
<button
type="button"
>
Save Post
</button>
</form>
</section>
`;

View File

@ -0,0 +1,117 @@
import React, { useState, memo } from "react"
import { toast } from "react-toastify"
import { AppDispatch } from "../../store"
import { postEnvie } from "../../store/envieAdd"
import styles from "./styles.module.scss"
interface Props {
dispatch: AppDispatch
}
const AddEnvie = ({ dispatch }: Props) => {
const [domaine, setDomaine] = useState("")
const [envies, setEnvies] = useState("")
const [precisions, setPrecisions] = useState("")
const [equipes, setEquipes] = useState([""])
const [dateAjout, setDateAjout] = useState("")
const onDomaineChanged = (e: React.ChangeEvent<HTMLInputElement>) => setDomaine(e.target.value)
const onEnviesChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => setEnvies(e.target.value)
const onPrecisionsChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
setPrecisions(e.target.value)
const onEquipesChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setEquipes(e.target.value.split(/, ?/))
const onDateAjoutChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setDateAjout(e.target.value)
const onSavePostClicked = () => {
if (domaine && envies) {
dispatch(
postEnvie({
domaine,
envies,
precisions,
equipes,
dateAjout,
})
)
setDomaine("")
setEnvies("")
setPrecisions("")
setEquipes([""])
setDateAjout("")
} else {
toast.warning("Il faut au moins préciser un domaine et l'envie", {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
}
return (
<section className={styles.EnvieList}>
<h2>Ajouter une nouvelle envie</h2>
<form>
<label htmlFor="postDomaine">
Domaine:
<input
type="text"
id="postDomaine"
name="postDomaine"
value={domaine}
onChange={onDomaineChanged}
/>
</label>
<label htmlFor="postEnvies">
Envies:
<textarea
id="postEnvies"
name="postEnvies"
value={envies}
onChange={onEnviesChanged}
/>
</label>
<label htmlFor="postPrecisions">
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
value={precisions}
onChange={onPrecisionsChanged}
/>
</label>
<label htmlFor="postEquipes">
Equipes:
<input
type="text"
id="postEquipes"
name="postEquipes"
value={equipes.join(", ")}
onChange={onEquipesChanged}
/>
</label>
<label htmlFor="postDateAjout">
DateAjout:
<input
type="date"
id="postDateAjout"
name="postDateAjout"
value={dateAjout}
onChange={onDateAjoutChanged}
/>
</label>
<button type="button" onClick={onSavePostClicked}>
Save Post
</button>
</form>
</section>
)
}
export default memo(AddEnvie)

View File

@ -0,0 +1,17 @@
@import "../../theme/variables";
.jav-game-list {
color: $color-white;
ul {
padding-left: 17px;
li {
margin-bottom: 0.5em;
}
}
a {
color: $color-white;
}
}

View File

@ -3,5 +3,6 @@ import JeuxJavList from "./JeuxJavList"
import Info from "./Info"
import ErrorBoundary from "./ErrorBoundary"
import Loading from "./Loading"
import AddEnvie from "./AddEnvie"
export { List, JeuxJavList, Info, ErrorBoundary, Loading }
export { List, JeuxJavList, Info, ErrorBoundary, Loading, AddEnvie }

View File

@ -1,25 +1,33 @@
import { Request, Response, NextFunction } from "express"
import { getList, setList } from "./utils"
import { Envie } from "../services/envies"
import { getList, add } from "./utils"
import { Envie, EnvieWithoutId } from "../services/envies"
export const getEnvieList = async (
_request: Request,
response: Response,
_next: NextFunction
): Promise<void> => {
const list = await getList<Envie>("Envies d'aider", new Envie())
if (list) {
response.status(200).json(list)
try {
const list = await getList<Envie>("Envies d'aider", new Envie())
if (list) {
response.status(200).json(list)
}
} catch (e: unknown) {
response.status(400).json(e)
}
}
export const setEnvieList = async (
export const addEnvie = async (
request: Request,
response: Response,
_next: NextFunction
): Promise<void> => {
const success = await setList<Envie>("Envies d'aider", request.body)
if (success) {
response.status(200).json()
try {
const envie = await add<EnvieWithoutId, Envie>("Envies d'aider", "envieId", request.body)
if (envie) {
response.status(200).json(envie)
}
} catch (e: unknown) {
response.status(400).json(e)
}
}

View File

@ -8,9 +8,13 @@ export const getJeuxJavList = async (
response: Response,
_next: NextFunction
): Promise<void> => {
const list = await getList<JeuxJav>("Jeux JAV", new JeuxJav())
if (list) {
response.status(200).json(list)
try {
const list = await getList<JeuxJav>("Jeux JAV", new JeuxJav())
if (list) {
response.status(200).json(list)
}
} catch (e: unknown) {
response.status(400).json(e)
}
}

View File

@ -18,8 +18,7 @@ export async function getList<Element extends object>(
const rows = (await sheet.getRows()) as StringifiedElement[]
const elements: Element[] = []
if (!rows[0]) {
// TODO: Report format error to database maintainers
return []
throw new Error(`No column types defined in sheet ${sheetName}`)
}
const types = _.pick(rows[0], Object.keys(specimen)) as Record<keyof Element, string>
rows.shift()
@ -47,7 +46,7 @@ export async function setList<Element extends object>(
// Load sheet into an array of objects
const rows = await sheet.getRows()
if (!rows[0]) {
return undefined
throw new Error(`No column types defined in sheet ${sheetName}`)
}
const types = _.pick(rows[0], Object.keys(elements[0] || {})) as Record<keyof Element, string>
@ -58,10 +57,6 @@ export async function setList<Element extends object>(
const row = rows[rowid]
const stringifiedRow = stringifyElement(element, types)
if (stringifiedRow === undefined) {
return undefined
}
if (!row) {
// eslint-disable-next-line no-await-in-loop
await sheet.addRow(stringifiedRow)
@ -94,6 +89,39 @@ export async function setList<Element extends object>(
return true
}
// eslint-disable-next-line @typescript-eslint/ban-types
export async function add<ElementNoId extends object, Element extends ElementNoId>(
sheetName: string,
idFieldName: string,
partialElement: Partial<ElementNoId>
): Promise<Element | undefined> {
if (!partialElement) {
return undefined
}
const sheet = await getGSheet(sheetName)
// Load sheet into an array of objects
const rows = await sheet.getRows()
if (!rows[0]) {
throw new Error(`No column types defined in sheet ${sheetName}`)
}
const types = {
[idFieldName]: "number",
...(_.pick(rows[0], Object.keys(partialElement || {})) as Record<keyof Element, string>),
}
// Create full element
rows.shift()
const highestId = rows.reduce((id: number, row) => Math.max(id, +row[idFieldName] || 0), 0)
const element = { [idFieldName]: highestId + 1, ...partialElement } as Element
// Add element
const stringifiedRow = stringifyElement(element, types)
await sheet.addRow(stringifiedRow)
return element
}
async function getGSheet(sheetName: string): Promise<GoogleSpreadsheetWorksheet> {
const doc = new GoogleSpreadsheet("1pMMKcYx6NXLOqNn6pLHJTPMTOLRYZmSNg2QQcAu7-Pw")
const creds = await fs.readFile(CRED_PATH)
@ -108,13 +136,10 @@ function parseElement<Element extends object>(
rawElement: Record<keyof Element, string>,
types: Record<keyof Element, string>,
specimen: Element
): Element | undefined {
): Element {
const fullElement = _.reduce(
types,
(element: any, type: string, prop: string) => {
if (element === undefined) {
return undefined
}
const rawProp: string = rawElement[prop as keyof Element]
switch (type) {
case "string":
@ -136,14 +161,13 @@ function parseElement<Element extends object>(
element[prop] = new Date(+matchDate[3], +matchDate[2] - 1, +matchDate[1])
break
}
return undefined // TODO: Report format error to database maintainers
break
throw new Error(`Unable to read date from ${rawProp}`)
default:
// eslint-disable-next-line no-case-declarations
const matchArrayType = type.match(/^(number|string|boolean|date)\[([^\]]+)\]$/)
if (!matchArrayType) {
return undefined
throw new Error(`Unknown array type for ${type}`)
}
if (!rawProp) {
element[prop] = []
@ -189,10 +213,11 @@ function parseElement<Element extends object>(
return true
})
if (!rightFormat) {
return undefined
throw new Error(`One array item is not a date in ${rawProp}`)
}
break
default:
throw new Error(`Unknown array type ${arrayType}`)
}
}
}
@ -207,17 +232,10 @@ function parseElement<Element extends object>(
function stringifyElement<Element extends object>(
element: Element,
types: Record<keyof Element, string>
): Record<keyof Element, string> | undefined {
const rawElement: Record<keyof Element, string> | undefined = _.reduce(
): Record<keyof Element, string> {
const rawElement: Record<keyof Element, string> = _.reduce(
types,
(
stringifiedElement: Record<keyof Element, string> | undefined,
type: string,
prop: string
) => {
if (stringifiedElement === undefined) {
return undefined
}
(stringifiedElement: Record<keyof Element, string>, type: string, prop: string) => {
const value = element[prop as keyof Element]
switch (type) {
case "string":
@ -233,22 +251,16 @@ function stringifyElement<Element extends object>(
break
case "date":
if (value instanceof Date) {
stringifiedElement[prop as keyof Element] = `${value.getDate()}/${
value.getMonth() + 1
}/${value.getFullYear()}`
break
} else {
console.error("Wrong date format in stringifyElement")
return undefined // TODO: Report format error to database maintainers
}
stringifiedElement[prop as keyof Element] = stringifiedDate(value)
break
default:
// eslint-disable-next-line no-case-declarations
const matchArrayType = type.match(/^(number|string|boolean|date)\[([^\]]+)\]$/)
if (!matchArrayType || !_.isArray(value)) {
console.error("Unknown matchArrayType or not an array in stringifyElement")
return undefined
throw new Error(
"Unknown matchArrayType or not an array in stringifyElement"
)
}
// eslint-disable-next-line no-case-declarations
const arrayType = matchArrayType[1]
@ -258,7 +270,7 @@ function stringifyElement<Element extends object>(
switch (arrayType) {
case "string":
if (!_.every(value, _.isString)) {
return undefined
throw new Error(`Each date of ${value} is not a string`)
}
stringifiedElement[prop as keyof Element] = formulaSafe(
value.join(delimiter)
@ -267,14 +279,14 @@ function stringifyElement<Element extends object>(
case "number":
if (!_.every(value, _.isNumber)) {
return undefined
throw new Error(`Each date of ${value} is not a number`)
}
stringifiedElement[prop as keyof Element] = value.join(delimiter)
break
case "boolean":
if (!_.every(value, _.isBoolean)) {
return undefined
throw new Error(`Each date of ${value} is not a boolean`)
}
stringifiedElement[prop as keyof Element] = _.map(value, (val) =>
val ? "X" : ""
@ -283,7 +295,7 @@ function stringifyElement<Element extends object>(
case "date":
if (!_.every(value, _.isDate)) {
return undefined
throw new Error(`Each date of ${value} is not a date`)
}
stringifiedElement[prop as keyof Element] = _.map(
value,
@ -293,7 +305,7 @@ function stringifyElement<Element extends object>(
break
default:
return undefined
throw new Error(`Unknown array type ${arrayType}`)
}
}
@ -309,4 +321,19 @@ function formulaSafe(value: string): string {
return value.replace(/^=+/, "")
}
function stringifiedDate(value: unknown): string {
let date: Date
if (value instanceof Date) {
date = value
} else if (typeof value === "string") {
try {
date = new Date(value)
} catch (e) {
throw new Error("Wrong date string format in stringifyElement")
}
} else {
throw new Error("Wrong date format in stringifyElement")
}
return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
}
export { SCOPES }

View File

@ -6,7 +6,7 @@ import { Helmet } from "react-helmet"
import { AppState, AppThunk } from "../../store"
import { fetchJeuxJavListIfNeed } from "../../store/jeuxJavList"
import { fetchEnvieListIfNeed } from "../../store/envieList"
import { JeuxJavList } from "../../components"
import { JeuxJavList, AddEnvie } from "../../components"
import styles from "./styles.module.scss"
export type Props = RouteComponentProps
@ -31,16 +31,20 @@ function useList(stateToProp: (state: AppState) => any, fetchDataIfNeed: () => A
}
}
const Home: FC<Props> = (): JSX.Element => (
<div className={styles.Home}>
<Helmet title="Home" />
{/* {useList((state: AppState) => state.envieList, fetchEnvieListifNeed)()} */}
{useList((state: AppState) => state.jeuxJavList, fetchJeuxJavListIfNeed)()}
{/* <button type="button" onClick={() => setList([{id: 3, joueurs: 4, duree: 5, description: "abcd"}])}>
const Home: FC<Props> = (): JSX.Element => {
const dispatch = useDispatch()
return (
<div className={styles.Home}>
<Helmet title="Home" />
<AddEnvie dispatch={dispatch} />
{/* {useList((state: AppState) => state.envieList, fetchEnvieListifNeed)()} */}
{useList((state: AppState) => state.jeuxJavList, fetchJeuxJavListIfNeed)()}
{/* <button type="button" onClick={() => setList([{id: 3, joueurs: 4, duree: 5, description: "abcd"}])}>
Set list!
</button> */}
</div>
)
</div>
)
}
// Fetch server-side data here
export const loadData = (): AppThunk[] => [

View File

@ -4,6 +4,71 @@ exports[`<Home /> renders an error if loading failed 1`] = `
<div
class="Home"
>
<section
class="EnvieList"
>
<h2>
Ajouter une nouvelle envie
</h2>
<form>
<label
for="postDomaine"
>
Domaine:
<input
id="postDomaine"
name="postDomaine"
type="text"
value=""
/>
</label>
<label
for="postEnvies"
>
Envies:
<textarea
id="postEnvies"
name="postEnvies"
/>
</label>
<label
for="postPrecisions"
>
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
/>
</label>
<label
for="postEquipes"
>
Equipes:
<input
id="postEquipes"
name="postEquipes"
type="text"
value=""
/>
</label>
<label
for="postDateAjout"
>
DateAjout:
<input
id="postDateAjout"
name="postDateAjout"
type="date"
value=""
/>
</label>
<button
type="button"
>
Save Post
</button>
</form>
</section>
<p>
Oops, Failed to load list!
</p>
@ -14,6 +79,71 @@ exports[`<Home /> renders the <List /> if loading was successful 1`] = `
<div
class="Home"
>
<section
class="EnvieList"
>
<h2>
Ajouter une nouvelle envie
</h2>
<form>
<label
for="postDomaine"
>
Domaine:
<input
id="postDomaine"
name="postDomaine"
type="text"
value=""
/>
</label>
<label
for="postEnvies"
>
Envies:
<textarea
id="postEnvies"
name="postEnvies"
/>
</label>
<label
for="postPrecisions"
>
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
/>
</label>
<label
for="postEquipes"
>
Equipes:
<input
id="postEquipes"
name="postEquipes"
type="text"
value=""
/>
</label>
<label
for="postDateAjout"
>
DateAjout:
<input
id="postDateAjout"
name="postDateAjout"
type="date"
value=""
/>
</label>
<button
type="button"
>
Save Post
</button>
</form>
</section>
<div
class="JeuxJavList"
>
@ -36,6 +166,71 @@ exports[`<Home /> renders the loading status if data invalid 1`] = `
<div
class="Home"
>
<section
class="EnvieList"
>
<h2>
Ajouter une nouvelle envie
</h2>
<form>
<label
for="postDomaine"
>
Domaine:
<input
id="postDomaine"
name="postDomaine"
type="text"
value=""
/>
</label>
<label
for="postEnvies"
>
Envies:
<textarea
id="postEnvies"
name="postEnvies"
/>
</label>
<label
for="postPrecisions"
>
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
/>
</label>
<label
for="postEquipes"
>
Equipes:
<input
id="postEquipes"
name="postEquipes"
type="text"
value=""
/>
</label>
<label
for="postDateAjout"
>
DateAjout:
<input
id="postDateAjout"
name="postDateAjout"
type="date"
value=""
/>
</label>
<button
type="button"
>
Save Post
</button>
</form>
</section>
<p>
Loading...
</p>
@ -46,6 +241,71 @@ exports[`<Home /> renders the loading status if requesting data 1`] = `
<div
class="Home"
>
<section
class="EnvieList"
>
<h2>
Ajouter une nouvelle envie
</h2>
<form>
<label
for="postDomaine"
>
Domaine:
<input
id="postDomaine"
name="postDomaine"
type="text"
value=""
/>
</label>
<label
for="postEnvies"
>
Envies:
<textarea
id="postEnvies"
name="postEnvies"
/>
</label>
<label
for="postPrecisions"
>
Precisions:
<textarea
id="postPrecisions"
name="postPrecisions"
/>
</label>
<label
for="postEquipes"
>
Equipes:
<input
id="postEquipes"
name="postEquipes"
type="text"
value=""
/>
</label>
<label
for="postDateAjout"
>
DateAjout:
<input
id="postDateAjout"
name="postDateAjout"
type="date"
value=""
/>
</label>
<button
type="button"
>
Save Post
</button>
</form>
</section>
<p>
Loading...
</p>

View File

@ -11,7 +11,7 @@ import devServer from "./devServer"
import ssr from "./ssr"
import { getJeuxJavList } from "../gsheets/jeuxJav"
import { getEnvieList, setEnvieList } from "../gsheets/envies"
import { getEnvieList, addEnvie } from "../gsheets/envies"
import config from "../config"
const app = express()
@ -32,9 +32,10 @@ app.use(express.static(path.resolve(process.cwd(), "public")))
if (__DEV__) devServer(app)
// Google Sheets requests
app.use(express.json())
app.get("/JeuxJav", getJeuxJavList)
app.get("/GetList", getEnvieList)
app.post("/SetList", setEnvieList)
app.get("/GetEnvieList", getEnvieList)
app.post("/AddEnvie", addEnvie)
// Use React server-side rendering middleware
app.get("*", ssr)

View File

@ -3,32 +3,40 @@ import axios from "axios"
import config from "../config"
export class Envie {
envieId = 0
domaine = ""
envies = ""
precisions = ""
equipes = []
equipes: string[] = []
dateAjout = new Date(0)
dateAjout = ""
}
export type EnvieWithoutId = Omit<Envie, "envieId">
export interface EnviesResponse {
export interface GetEnvieListResponse {
data?: Envie[]
error?: Error
}
export const getEnvieList = async (): Promise<EnviesResponse> => {
export const getEnvieList = async (): Promise<GetEnvieListResponse> => {
try {
const { data } = await axios.get(`${config.API_URL}/GetList`)
const { data } = await axios.get(`${config.API_URL}/GetEnvieList`)
return { data }
} catch (error) {
return { error: error as Error }
}
}
export const setEnvieList = async (list: Envie[]): Promise<EnviesResponse> => {
export interface AddEnvieResponse {
data?: Envie
error?: Error
}
export const addEnvie = async (envieWithoutId: EnvieWithoutId): Promise<AddEnvieResponse> => {
try {
const { data } = await axios.post(`${config.API_URL}/SetList`, list)
const { data } = await axios.post(`${config.API_URL}/AddEnvie`, envieWithoutId)
return { data }
} catch (error) {
return { error: error as Error }

70
src/store/envieAdd.ts Normal file
View File

@ -0,0 +1,70 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { Envie, EnvieWithoutId, addEnvie } from "../services/envies"
import { AppThunk } from "."
interface EnvieRequest {
readyStatus: string
items: Envie | null
error: string | null
}
export const initialState: EnvieRequest = {
readyStatus: "invalid",
items: null,
error: null,
}
const envieList = createSlice({
name: "addEnvie",
initialState,
reducers: {
getRequesting: (state: EnvieRequest) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<Envie>) => {
state.readyStatus = "success"
state.items = payload
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default envieList.reducer
export const { getRequesting, getSuccess, getFailure } = envieList.actions
export const postEnvie =
(envieWithoutId: EnvieWithoutId): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await addEnvie(envieWithoutId)
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors de l'ajout: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Envie))
toast.success("Envie ajoutée !", {
position: "top-center",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
}

View File

@ -3,23 +3,23 @@ import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { Envie, getEnvieList } from "../services/envies"
import { AppThunk, AppState } from "."
interface EnvieList {
interface EnvieListRequest {
readyStatus: string
items: Envie[]
error: string | null
}
export const initialState: EnvieList = {
export const initialState: EnvieListRequest = {
readyStatus: "invalid",
items: [],
error: null,
}
const envieList = createSlice({
name: "envieList",
name: "getEnvieList",
initialState,
reducers: {
getRequesting: (state: EnvieList) => {
getRequesting: (state: EnvieListRequest) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<Envie[]>) => {

View File

@ -4,7 +4,7 @@ import { JeuxJav, getJeuxJavList } from "../services/jeuxJav"
import { AppThunk, AppState } from "."
interface JeuxJavList {
readyStatus: string
readyStatus: string // TODO Change it to: "invalid" | "request" | "success" | "failure"
items: JeuxJav[]
error: string | null
}

2160
yarn.lock

File diff suppressed because it is too large Load Diff