mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 09:04:20 +02:00
Adds createEntityAdapter
This commit is contained in:
parent
14df6e6399
commit
193f51b54d
@ -12,7 +12,7 @@ describe("<Info />", () => {
|
|||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<Info
|
<Info
|
||||||
item={{
|
item={{
|
||||||
id: 1,
|
membreId: 1,
|
||||||
name: "PeL",
|
name: "PeL",
|
||||||
phone: "+886 0970...",
|
phone: "+886 0970...",
|
||||||
email: "forceoranj@gmail.com",
|
email: "forceoranj@gmail.com",
|
||||||
|
53
src/components/JeuJavList/__tests__/JeuJavList.tsx
Normal file
53
src/components/JeuJavList/__tests__/JeuJavList.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
import { render } from "@testing-library/react"
|
||||||
|
import { MemoryRouter } from "react-router-dom"
|
||||||
|
|
||||||
|
import mockStore from "../../../utils/mockStore"
|
||||||
|
import List from "../index"
|
||||||
|
|
||||||
|
describe("<List />", () => {
|
||||||
|
const renderHelper = (reducer = { readyStatus: "idle" }) => {
|
||||||
|
const { dispatch, ProviderWithStore } = mockStore({ jeuJavList: reducer })
|
||||||
|
const { container } = render(
|
||||||
|
<ProviderWithStore>
|
||||||
|
<MemoryRouter>
|
||||||
|
<List ids={[5]} />
|
||||||
|
</MemoryRouter>
|
||||||
|
</ProviderWithStore>
|
||||||
|
)
|
||||||
|
|
||||||
|
return { dispatch, firstChild: container.firstChild }
|
||||||
|
}
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
const reducer = {
|
||||||
|
readyStatus: "success",
|
||||||
|
ids: [5],
|
||||||
|
entities: {
|
||||||
|
"5": {
|
||||||
|
id: 5,
|
||||||
|
titre: "6 qui prend!",
|
||||||
|
auteur: "Wolfgang Kramer",
|
||||||
|
editeur: "(uncredited) , Design Edge , B",
|
||||||
|
minJoueurs: 2,
|
||||||
|
maxJoueurs: 10,
|
||||||
|
duree: 45,
|
||||||
|
type: "Ambiance",
|
||||||
|
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||||
|
photo: "https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||||
|
bggPhoto: "",
|
||||||
|
bggId: 432,
|
||||||
|
exemplaires: 1,
|
||||||
|
dispoPret: 1,
|
||||||
|
nonRangee: 0,
|
||||||
|
horodatage: "0000-00-00",
|
||||||
|
ean: "3421272101313",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
exports[`<List /> renders 1`] = `
|
exports[`<List /> renders 1`] = `
|
||||||
<div
|
<div
|
||||||
class="JeuxJavList"
|
class="JeuJavList"
|
||||||
>
|
>
|
||||||
<h4>
|
<h4>
|
||||||
Jeux JAV
|
Jeux JAV
|
36
src/components/JeuJavList/index.tsx
Normal file
36
src/components/JeuJavList/index.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { memo } from "react"
|
||||||
|
import { useSelector, shallowEqual } from "react-redux"
|
||||||
|
import { EntityId } from "@reduxjs/toolkit"
|
||||||
|
// import { Link } from "react-router-dom"
|
||||||
|
|
||||||
|
import { AppState } from "../../store"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
ids: EntityId[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const List = ({ ids }: Props) => {
|
||||||
|
const { entities: jeuxJav } = useSelector((state: AppState) => state.jeuJavList, shallowEqual)
|
||||||
|
return (
|
||||||
|
<div className={styles.JeuJavList}>
|
||||||
|
<h4>Jeux JAV</h4>
|
||||||
|
<ul>
|
||||||
|
{ids.map((id) => {
|
||||||
|
const jeu = jeuxJav[id]
|
||||||
|
if (!jeu) {
|
||||||
|
return <li key={id}>Le jeu #{id} n'existe pas</li>
|
||||||
|
}
|
||||||
|
const { titre, bggId } = jeu
|
||||||
|
return (
|
||||||
|
<li key={id}>
|
||||||
|
{titre} - [{bggId}]
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(List)
|
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { render } from "@testing-library/react"
|
|
||||||
import { MemoryRouter } from "react-router-dom"
|
|
||||||
|
|
||||||
import List from "../index"
|
|
||||||
|
|
||||||
describe("<List />", () => {
|
|
||||||
it("renders", () => {
|
|
||||||
const tree = render(
|
|
||||||
<MemoryRouter>
|
|
||||||
<List
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
titre: "6 qui prend!",
|
|
||||||
auteur: "Wolfgang Kramer",
|
|
||||||
editeur: "(uncredited) , Design Edge , B",
|
|
||||||
minJoueurs: 2,
|
|
||||||
maxJoueurs: 10,
|
|
||||||
duree: 45,
|
|
||||||
type: "Ambiance",
|
|
||||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
|
||||||
bggId: 432,
|
|
||||||
exemplaires: 1,
|
|
||||||
dispoPret: 1,
|
|
||||||
nonRangee: 0,
|
|
||||||
ean: "3421272101313",
|
|
||||||
bggPhoto:
|
|
||||||
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
|
||||||
).container.firstChild
|
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,24 +0,0 @@
|
|||||||
import { memo } from "react"
|
|
||||||
// import { Link } from "react-router-dom"
|
|
||||||
|
|
||||||
import { JeuxJav } from "../../services/jeuxJav"
|
|
||||||
import styles from "./styles.module.scss"
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
items: JeuxJav[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const List = ({ items }: Props) => (
|
|
||||||
<div className={styles.JeuxJavList}>
|
|
||||||
<h4>Jeux JAV</h4>
|
|
||||||
<ul>
|
|
||||||
{items.map(({ id, titre, bggId }) => (
|
|
||||||
<li key={id}>
|
|
||||||
{titre} - [{bggId}]
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default memo(List)
|
|
@ -13,7 +13,7 @@ describe("<List />", () => {
|
|||||||
<List
|
<List
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
id: 1,
|
membreId: 1,
|
||||||
name: "PeL",
|
name: "PeL",
|
||||||
phone: "+886 0970...",
|
phone: "+886 0970...",
|
||||||
email: "forceoranj@gmail.com",
|
email: "forceoranj@gmail.com",
|
||||||
|
@ -12,9 +12,9 @@ const List = ({ items }: Props) => (
|
|||||||
<div className={styles.UserList}>
|
<div className={styles.UserList}>
|
||||||
<h4>User List</h4>
|
<h4>User List</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{items.map(({ id, name }) => (
|
{items.map(({ membreId, name }) => (
|
||||||
<li key={id}>
|
<li key={membreId}>
|
||||||
<Link to={`/UserInfo/${id}`}>{name}</Link>
|
<Link to={`/UserInfo/${membreId}`}>{name}</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import List from "./List"
|
import List from "./List"
|
||||||
import JeuxJavList from "./JeuxJavList"
|
import JeuJavList from "./JeuJavList"
|
||||||
import Info from "./Info"
|
import Info from "./Info"
|
||||||
import ErrorBoundary from "./ErrorBoundary"
|
import ErrorBoundary from "./ErrorBoundary"
|
||||||
import Loading from "./Loading"
|
import Loading from "./Loading"
|
||||||
import AddEnvie from "./AddEnvie"
|
import AddEnvie from "./AddEnvie"
|
||||||
|
|
||||||
export { List, JeuxJavList, Info, ErrorBoundary, Loading, AddEnvie }
|
export { List, JeuJavList, Info, ErrorBoundary, Loading, AddEnvie }
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Request, Response, NextFunction } from "express"
|
import { Request, Response, NextFunction } from "express"
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { getList } from "./utils"
|
import { getList } from "./utils"
|
||||||
import { JeuxJav } from "../services/jeuxJav"
|
import { JeuJav } from "../services/jeuJav"
|
||||||
|
|
||||||
export const getJeuxJavList = async (
|
export const getJeuJavList = async (
|
||||||
_request: Request,
|
_request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
_next: NextFunction
|
_next: NextFunction
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const list = await getList<JeuxJav>("Jeux JAV", new JeuxJav())
|
const list = await getList<JeuJav>("Jeux JAV", new JeuJav())
|
||||||
if (list) {
|
if (list) {
|
||||||
response.status(200).json(list)
|
response.status(200).json(list)
|
||||||
}
|
}
|
||||||
@ -18,12 +18,12 @@ export const getJeuxJavList = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getJeuxJavData = async (
|
export const getJeuJavData = async (
|
||||||
_request: Request,
|
_request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
_next: NextFunction
|
_next: NextFunction
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const list = await getList<JeuxJav>("Jeux JAV", new JeuxJav())
|
const list = await getList<JeuJav>("Jeux JAV", new JeuJav())
|
||||||
const data = _.find(list, { id: 56 })
|
const data = _.find(list, { id: 56 })
|
||||||
if (data) {
|
if (data) {
|
||||||
response.status(200).json(data)
|
response.status(200).json(data)
|
@ -3,17 +3,20 @@ import { RouteComponentProps } from "react-router-dom"
|
|||||||
import { useDispatch, useSelector, shallowEqual } from "react-redux"
|
import { useDispatch, useSelector, shallowEqual } from "react-redux"
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
|
|
||||||
import { AppState, AppThunk } from "../../store"
|
import { AppState, AppThunk, EntitiesRequest } from "../../store"
|
||||||
import { fetchJeuxJavListIfNeed } from "../../store/jeuxJavList"
|
import { fetchJeuJavListIfNeed } from "../../store/jeuJavList"
|
||||||
import { fetchEnvieListIfNeed } from "../../store/envieList"
|
import { fetchEnvieListIfNeed } from "../../store/envieList"
|
||||||
import { JeuxJavList, AddEnvie } from "../../components"
|
import { JeuJavList, AddEnvie } from "../../components"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
export type Props = RouteComponentProps
|
export type Props = RouteComponentProps
|
||||||
|
|
||||||
function useList(stateToProp: (state: AppState) => any, fetchDataIfNeed: () => AppThunk) {
|
function useList<Entity>(
|
||||||
|
stateToProp: (state: AppState) => EntitiesRequest<Entity>,
|
||||||
|
fetchDataIfNeed: () => AppThunk
|
||||||
|
) {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const { readyStatus, items } = useSelector(stateToProp, shallowEqual)
|
const { readyStatus, ids } = useSelector(stateToProp, shallowEqual)
|
||||||
|
|
||||||
// Fetch client-side data here
|
// Fetch client-side data here
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -22,12 +25,12 @@ function useList(stateToProp: (state: AppState) => any, fetchDataIfNeed: () => A
|
|||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (!readyStatus || readyStatus === "invalid" || readyStatus === "request")
|
if (!readyStatus || readyStatus === "idle" || readyStatus === "request")
|
||||||
return <p>Loading...</p>
|
return <p>Loading...</p>
|
||||||
|
|
||||||
if (readyStatus === "failure") return <p>Oops, Failed to load list!</p>
|
if (readyStatus === "failure") return <p>Oops, Failed to load list!</p>
|
||||||
|
|
||||||
return <JeuxJavList items={items} />
|
return <JeuJavList ids={ids} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ const Home: FC<Props> = (): JSX.Element => {
|
|||||||
<Helmet title="Home" />
|
<Helmet title="Home" />
|
||||||
<AddEnvie dispatch={dispatch} />
|
<AddEnvie dispatch={dispatch} />
|
||||||
{/* {useList((state: AppState) => state.envieList, fetchEnvieListifNeed)()} */}
|
{/* {useList((state: AppState) => state.envieList, fetchEnvieListifNeed)()} */}
|
||||||
{useList((state: AppState) => state.jeuxJavList, fetchJeuxJavListIfNeed)()}
|
{useList((state: AppState) => state.jeuJavList, fetchJeuJavListIfNeed)()}
|
||||||
{/* <button type="button" onClick={() => setList([{id: 3, joueurs: 4, duree: 5, description: "abcd"}])}>
|
{/* <button type="button" onClick={() => setList([{id: 3, joueurs: 4, duree: 5, description: "abcd"}])}>
|
||||||
Set list!
|
Set list!
|
||||||
</button> */}
|
</button> */}
|
||||||
@ -49,7 +52,7 @@ const Home: FC<Props> = (): JSX.Element => {
|
|||||||
// Fetch server-side data here
|
// Fetch server-side data here
|
||||||
export const loadData = (): AppThunk[] => [
|
export const loadData = (): AppThunk[] => [
|
||||||
fetchEnvieListIfNeed(),
|
fetchEnvieListIfNeed(),
|
||||||
fetchJeuxJavListIfNeed(),
|
fetchJeuJavListIfNeed(),
|
||||||
// More pre-fetched actions...
|
// More pre-fetched actions...
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
import { render } from "@testing-library/react"
|
import { render } from "@testing-library/react"
|
||||||
import { MemoryRouter } from "react-router-dom"
|
import { MemoryRouter } from "react-router-dom"
|
||||||
|
|
||||||
import { fetchJeuxJavListIfNeed } from "../../../store/jeuxJavList"
|
import { fetchJeuJavListIfNeed } from "../../../store/jeuJavList"
|
||||||
import mockStore from "../../../utils/mockStore"
|
import mockStore from "../../../utils/mockStore"
|
||||||
import Home from "../Home"
|
import Home from "../Home"
|
||||||
|
|
||||||
describe("<Home />", () => {
|
describe("<Home />", () => {
|
||||||
const renderHelper = (reducer = { readyStatus: "invalid" }) => {
|
const renderHelper = (reducer = { readyStatus: "idle" }) => {
|
||||||
const { dispatch, ProviderWithStore } = mockStore({ jeuxJavList: reducer })
|
const { dispatch, ProviderWithStore } = mockStore({ jeuJavList: reducer })
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ProviderWithStore>
|
<ProviderWithStore>
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
@ -28,7 +28,7 @@ describe("<Home />", () => {
|
|||||||
const { dispatch } = renderHelper()
|
const { dispatch } = renderHelper()
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||||
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchJeuxJavListIfNeed().toString())
|
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchJeuJavListIfNeed().toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders the loading status if data invalid", () => {
|
it("renders the loading status if data invalid", () => {
|
||||||
@ -50,8 +50,9 @@ describe("<Home />", () => {
|
|||||||
it("renders the <List /> if loading was successful", () => {
|
it("renders the <List /> if loading was successful", () => {
|
||||||
const reducer = {
|
const reducer = {
|
||||||
readyStatus: "success",
|
readyStatus: "success",
|
||||||
items: [
|
ids: [5],
|
||||||
{
|
entities: {
|
||||||
|
"5": {
|
||||||
id: 5,
|
id: 5,
|
||||||
titre: "6 qui prend!",
|
titre: "6 qui prend!",
|
||||||
auteur: "Wolfgang Kramer",
|
auteur: "Wolfgang Kramer",
|
||||||
@ -70,7 +71,7 @@ describe("<Home />", () => {
|
|||||||
horodatage: "0000-00-00",
|
horodatage: "0000-00-00",
|
||||||
ean: "3421272101313",
|
ean: "3421272101313",
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||||
|
@ -145,7 +145,7 @@ exports[`<Home /> renders the <List /> if loading was successful 1`] = `
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
<div
|
<div
|
||||||
class="JeuxJavList"
|
class="JeuJavList"
|
||||||
>
|
>
|
||||||
<h4>
|
<h4>
|
||||||
Jeux JAV
|
Jeux JAV
|
||||||
|
@ -4,15 +4,15 @@ import { useDispatch, useSelector, shallowEqual } from "react-redux"
|
|||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
|
|
||||||
import { AppState, AppThunk } from "../../store"
|
import { AppState, AppThunk } from "../../store"
|
||||||
import { User } from "../../services/jsonPlaceholder"
|
|
||||||
import { fetchUserDataIfNeed } from "../../store/userData"
|
import { fetchUserDataIfNeed } from "../../store/userData"
|
||||||
import { Info } from "../../components"
|
import { Info } from "../../components"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
export type Props = RouteComponentProps<{ id: string }>
|
export type Props = RouteComponentProps<{ memberId: string }>
|
||||||
|
|
||||||
const UserInfo = ({ match }: Props): JSX.Element => {
|
const UserInfo = ({ match }: Props): JSX.Element => {
|
||||||
const { id } = match.params
|
const { memberId: rawId } = match.params
|
||||||
|
const id = +rawId
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const userData = useSelector((state: AppState) => state.userData, shallowEqual)
|
const userData = useSelector((state: AppState) => state.userData, shallowEqual)
|
||||||
|
|
||||||
@ -21,13 +21,14 @@ const UserInfo = ({ match }: Props): JSX.Element => {
|
|||||||
}, [dispatch, id])
|
}, [dispatch, id])
|
||||||
|
|
||||||
const renderInfo = () => {
|
const renderInfo = () => {
|
||||||
const userInfo = userData[id]
|
const userInfo = userData
|
||||||
|
|
||||||
if (!userInfo || userInfo.readyStatus === "request") return <p>Loading...</p>
|
if (!userInfo || userInfo.readyStatus === "request") return <p>Loading...</p>
|
||||||
|
|
||||||
if (userInfo.readyStatus === "failure") return <p>Oops! Failed to load data.</p>
|
if (userInfo.readyStatus === "failure" || !userInfo.entity)
|
||||||
|
return <p>Oops! Failed to load data.</p>
|
||||||
|
|
||||||
return <Info item={userInfo.item as User} />
|
return <Info item={userInfo.entity} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -39,7 +40,7 @@ const UserInfo = ({ match }: Props): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LoadDataArgs {
|
interface LoadDataArgs {
|
||||||
params: { id: string }
|
params: { id: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadData = ({ params }: LoadDataArgs): AppThunk[] => [fetchUserDataIfNeed(params.id)]
|
export const loadData = ({ params }: LoadDataArgs): AppThunk[] => [fetchUserDataIfNeed(params.id)]
|
||||||
|
@ -4,19 +4,18 @@
|
|||||||
import { render } from "@testing-library/react"
|
import { render } from "@testing-library/react"
|
||||||
import { MemoryRouter } from "react-router-dom"
|
import { MemoryRouter } from "react-router-dom"
|
||||||
|
|
||||||
import { fetchUserDataIfNeed } from "../../../store/userData"
|
|
||||||
import mockStore from "../../../utils/mockStore"
|
import mockStore from "../../../utils/mockStore"
|
||||||
import UserInfo from "../UserInfo"
|
import UserInfo from "../UserInfo"
|
||||||
|
|
||||||
describe("<UserInfo />", () => {
|
describe("<UserInfo />", () => {
|
||||||
const mockData = {
|
const mockData = {
|
||||||
id: "1",
|
memberId: 1,
|
||||||
name: "PeL",
|
name: "PeL",
|
||||||
phone: "+886 0970...",
|
phone: "+886 0970...",
|
||||||
email: "forceoranj@gmail.com",
|
email: "forceoranj@gmail.com",
|
||||||
website: "https://www.parisestludique.fr",
|
website: "https://www.parisestludique.fr",
|
||||||
}
|
}
|
||||||
const { id } = mockData
|
const { memberId } = mockData
|
||||||
|
|
||||||
const renderHelper = (reducer = {}) => {
|
const renderHelper = (reducer = {}) => {
|
||||||
const { dispatch, ProviderWithStore } = mockStore({ userData: reducer })
|
const { dispatch, ProviderWithStore } = mockStore({ userData: reducer })
|
||||||
@ -25,7 +24,7 @@ describe("<UserInfo />", () => {
|
|||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
{/*
|
{/*
|
||||||
@ts-expect-error */}
|
@ts-expect-error */}
|
||||||
<UserInfo match={{ params: { id } }} />
|
<UserInfo match={{ params: { memberId } }} />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</ProviderWithStore>
|
</ProviderWithStore>
|
||||||
)
|
)
|
||||||
@ -33,33 +32,24 @@ describe("<UserInfo />", () => {
|
|||||||
return { dispatch, firstChild: container.firstChild }
|
return { dispatch, firstChild: container.firstChild }
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should fetch data when page loaded", () => {
|
|
||||||
const { dispatch } = renderHelper()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
|
||||||
expect(dispatch.mock.calls[0][0].toString()).toBe(
|
|
||||||
fetchUserDataIfNeed(id.toString()).toString()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("renders the loading status if data invalid", () => {
|
it("renders the loading status if data invalid", () => {
|
||||||
expect(renderHelper().firstChild).toMatchSnapshot()
|
expect(renderHelper().firstChild).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders the loading status if requesting data", () => {
|
it("renders the loading status if requesting data", () => {
|
||||||
const reducer = { [id]: { readyStatus: "request" } }
|
const reducer = { readyStatus: "request" }
|
||||||
|
|
||||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders an error if loading failed", () => {
|
it("renders an error if loading failed", () => {
|
||||||
const reducer = { [id]: { readyStatus: "failure" } }
|
const reducer = { readyStatus: "failure" }
|
||||||
|
|
||||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("renders the <Info /> if loading was successful", () => {
|
it("renders the <Info /> if loading was successful", () => {
|
||||||
const reducer = { [id]: { readyStatus: "success", item: mockData } }
|
const reducer = { readyStatus: "success", entity: mockData }
|
||||||
|
|
||||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
@ -47,7 +47,7 @@ exports[`<UserInfo /> renders the loading status if data invalid 1`] = `
|
|||||||
class="UserInfo"
|
class="UserInfo"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Loading...
|
Oops! Failed to load data.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -10,7 +10,7 @@ import chalk from "chalk"
|
|||||||
import devServer from "./devServer"
|
import devServer from "./devServer"
|
||||||
import ssr from "./ssr"
|
import ssr from "./ssr"
|
||||||
|
|
||||||
import { getJeuxJavList } from "../gsheets/jeuxJav"
|
import { getJeuJavList } from "../gsheets/jeuJav"
|
||||||
import { getEnvieList, addEnvie } from "../gsheets/envies"
|
import { getEnvieList, addEnvie } from "../gsheets/envies"
|
||||||
import config from "../config"
|
import config from "../config"
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ if (__DEV__) devServer(app)
|
|||||||
|
|
||||||
// Google Sheets requests
|
// Google Sheets requests
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.get("/JeuxJav", getJeuxJavList)
|
app.get("/JeuJav", getJeuJavList)
|
||||||
app.get("/GetEnvieList", getEnvieList)
|
app.get("/GetEnvieList", getEnvieList)
|
||||||
app.post("/AddEnvie", addEnvie)
|
app.post("/AddEnvie", addEnvie)
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import axios from "axios"
|
|||||||
|
|
||||||
import config from "../config"
|
import config from "../config"
|
||||||
|
|
||||||
export class JeuxJav {
|
export class JeuJav {
|
||||||
id = 0
|
jeuId = 0
|
||||||
|
|
||||||
titre = ""
|
titre = ""
|
||||||
|
|
||||||
@ -34,26 +34,26 @@ export class JeuxJav {
|
|||||||
bggPhoto = ""
|
bggPhoto = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JeuxJavList {
|
export interface JeuJavList {
|
||||||
data?: JeuxJav[]
|
data?: JeuJav[]
|
||||||
error?: Error
|
error?: Error
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JeuxJavData {
|
export interface JeuJavData {
|
||||||
data?: JeuxJav
|
data?: JeuJav
|
||||||
error?: Error
|
error?: Error
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getJeuxJavList = async (): Promise<JeuxJavList> => {
|
export const getJeuJavList = async (): Promise<JeuJavList> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`${config.API_URL}/JeuxJav`)
|
const { data } = await axios.get(`${config.API_URL}/JeuJav`)
|
||||||
return { data }
|
return { data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { error: error as Error }
|
return { error: error as Error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getJeuxJavData = async (id: string): Promise<JeuxJavData> => {
|
export const getJeuJavData = async (id: string): Promise<JeuJavData> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`${config.API_URL}/users/${id}`)
|
const { data } = await axios.get(`${config.API_URL}/users/${id}`)
|
||||||
return { data }
|
return { data }
|
@ -1,7 +1,7 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: number
|
membreId: number
|
||||||
name: string
|
name: string
|
||||||
phone: string
|
phone: string
|
||||||
email: string
|
email: string
|
||||||
@ -27,7 +27,7 @@ export const getUserList = async (): Promise<UserList> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getUserData = async (id: string): Promise<UserData> => {
|
export const getUserData = async (id: number): Promise<UserData> => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)
|
const { data } = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)
|
||||||
return { data }
|
return { data }
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
|
|
||||||
import mockStore from "../../utils/mockStore"
|
import mockStore from "../../utils/mockStore"
|
||||||
import JeuxJavList, {
|
import JeuJavList, {
|
||||||
initialState,
|
initialState,
|
||||||
getRequesting,
|
getRequesting,
|
||||||
getSuccess,
|
getSuccess,
|
||||||
getFailure,
|
getFailure,
|
||||||
fetchJeuxJavList,
|
fetchJeuJavList,
|
||||||
} from "../jeuxJavList"
|
} from "../jeuJavList"
|
||||||
|
|
||||||
jest.mock("axios")
|
jest.mock("axios")
|
||||||
|
|
||||||
const mockData = [
|
const mockData = {
|
||||||
{
|
"5": {
|
||||||
id: 5,
|
jeuId: 5,
|
||||||
titre: "6 qui prend!",
|
titre: "6 qui prend!",
|
||||||
auteur: "Wolfgang Kramer",
|
auteur: "Wolfgang Kramer",
|
||||||
editeur: "(uncredited) , Design Edge , B",
|
editeur: "(uncredited) , Design Edge , B",
|
||||||
@ -31,33 +31,34 @@ const mockData = [
|
|||||||
horodatage: "0000-00-00",
|
horodatage: "0000-00-00",
|
||||||
ean: "3421272101313",
|
ean: "3421272101313",
|
||||||
},
|
},
|
||||||
]
|
}
|
||||||
const mockError = "Oops! Something went wrong."
|
const mockError = "Oops! Something went wrong."
|
||||||
|
|
||||||
describe("JeuxJavList reducer", () => {
|
describe("JeuJavList reducer", () => {
|
||||||
it("should handle initial state", () => {
|
it("should handle initial state", () => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
expect(JeuxJavList(undefined, {})).toEqual(initialState)
|
expect(JeuJavList(undefined, {})).toEqual(initialState)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle requesting correctly", () => {
|
it("should handle requesting correctly", () => {
|
||||||
expect(JeuxJavList(undefined, { type: getRequesting.type })).toEqual({
|
expect(JeuJavList(undefined, { type: getRequesting.type })).toEqual({
|
||||||
readyStatus: "request",
|
readyStatus: "request",
|
||||||
items: [],
|
ids: [],
|
||||||
error: null,
|
entities: {},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle success correctly", () => {
|
it("should handle success correctly", () => {
|
||||||
expect(JeuxJavList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
expect(JeuJavList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
||||||
...initialState,
|
...initialState,
|
||||||
readyStatus: "success",
|
readyStatus: "success",
|
||||||
items: mockData,
|
ids: [5],
|
||||||
|
entities: mockData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle failure correctly", () => {
|
it("should handle failure correctly", () => {
|
||||||
expect(JeuxJavList(undefined, { type: getFailure.type, payload: mockError })).toEqual({
|
expect(JeuJavList(undefined, { type: getFailure.type, payload: mockError })).toEqual({
|
||||||
...initialState,
|
...initialState,
|
||||||
readyStatus: "failure",
|
readyStatus: "failure",
|
||||||
error: mockError,
|
error: mockError,
|
||||||
@ -65,8 +66,8 @@ describe("JeuxJavList reducer", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("JeuxJavList action", () => {
|
describe("JeuJavList action", () => {
|
||||||
it("fetches JeuxJav list successful", async () => {
|
it("fetches JeuJav list successful", async () => {
|
||||||
const { dispatch, getActions } = mockStore()
|
const { dispatch, getActions } = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: getRequesting.type },
|
{ type: getRequesting.type },
|
||||||
@ -76,11 +77,11 @@ describe("JeuxJavList action", () => {
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
axios.get.mockResolvedValue({ data: mockData })
|
axios.get.mockResolvedValue({ data: mockData })
|
||||||
|
|
||||||
await dispatch(fetchJeuxJavList())
|
await dispatch(fetchJeuJavList())
|
||||||
expect(getActions()).toEqual(expectedActions)
|
expect(getActions()).toEqual(expectedActions)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fetches JeuxJav list failed", async () => {
|
it("fetches JeuJav list failed", async () => {
|
||||||
const { dispatch, getActions } = mockStore()
|
const { dispatch, getActions } = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: getRequesting.type },
|
{ type: getRequesting.type },
|
||||||
@ -90,7 +91,7 @@ describe("JeuxJavList action", () => {
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
axios.get.mockRejectedValue({ message: mockError })
|
axios.get.mockRejectedValue({ message: mockError })
|
||||||
|
|
||||||
await dispatch(fetchJeuxJavList())
|
await dispatch(fetchJeuJavList())
|
||||||
expect(getActions()).toEqual(expectedActions)
|
expect(getActions()).toEqual(expectedActions)
|
||||||
})
|
})
|
||||||
})
|
})
|
@ -1,29 +1,35 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
|
|
||||||
import mockStore from "../../utils/mockStore"
|
import mockStore from "../../utils/mockStore"
|
||||||
import userData, { getRequesting, getSuccess, getFailure, fetchUserData } from "../userData"
|
import userData, {
|
||||||
|
getRequesting,
|
||||||
|
getSuccess,
|
||||||
|
getFailure,
|
||||||
|
fetchUserData,
|
||||||
|
initialState,
|
||||||
|
} from "../userData"
|
||||||
|
|
||||||
jest.mock("axios")
|
jest.mock("axios")
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
id: 1,
|
membreId: 1,
|
||||||
name: "PeL",
|
name: "PeL",
|
||||||
phone: "+886 0970...",
|
phone: "+886 0970...",
|
||||||
email: "forceoranj@gmail.com",
|
email: "forceoranj@gmail.com",
|
||||||
website: "https://www.parisestludique.fr",
|
website: "https://www.parisestludique.fr",
|
||||||
}
|
}
|
||||||
const { id } = mockData
|
const { membreId } = mockData
|
||||||
const mockError = "Oops! Something went wrong."
|
const mockError = "Oops! Something went wrong."
|
||||||
|
|
||||||
describe("userData reducer", () => {
|
describe("userData reducer", () => {
|
||||||
it("should handle initial state correctly", () => {
|
it("should handle initial state correctly", () => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
expect(userData(undefined, {})).toEqual({})
|
expect(userData(undefined, {})).toEqual(initialState)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle requesting correctly", () => {
|
it("should handle requesting correctly", () => {
|
||||||
expect(userData(undefined, { type: getRequesting.type, payload: id })).toEqual({
|
expect(userData(undefined, { type: getRequesting.type, payload: membreId })).toEqual({
|
||||||
[id]: { readyStatus: "request" },
|
readyStatus: "request",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -31,53 +37,47 @@ describe("userData reducer", () => {
|
|||||||
expect(
|
expect(
|
||||||
userData(undefined, {
|
userData(undefined, {
|
||||||
type: getSuccess.type,
|
type: getSuccess.type,
|
||||||
payload: { id, item: mockData },
|
payload: mockData,
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({ readyStatus: "success", entity: mockData })
|
||||||
[id]: { readyStatus: "success", item: mockData },
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle failure correctly", () => {
|
it("should handle failure correctly", () => {
|
||||||
expect(
|
expect(
|
||||||
userData(undefined, {
|
userData(undefined, {
|
||||||
type: getFailure.type,
|
type: getFailure.type,
|
||||||
payload: { id, error: mockError },
|
payload: mockError,
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({ readyStatus: "failure", error: mockError })
|
||||||
[id]: { readyStatus: "failure", error: mockError },
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("userData action", () => {
|
describe("userData action", () => {
|
||||||
const strId = id.toString()
|
|
||||||
|
|
||||||
it("fetches user data successful", async () => {
|
it("fetches user data successful", async () => {
|
||||||
const { dispatch, getActions } = mockStore()
|
const { dispatch, getActions } = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: getRequesting.type, payload: strId },
|
{ type: getRequesting.type },
|
||||||
{ type: getSuccess.type, payload: { id: strId, item: mockData } },
|
{ type: getSuccess.type, payload: mockData },
|
||||||
]
|
]
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
axios.get.mockResolvedValue({ data: mockData })
|
axios.get.mockResolvedValue({ data: mockData })
|
||||||
|
|
||||||
await dispatch(fetchUserData(strId))
|
await dispatch(fetchUserData(membreId))
|
||||||
expect(getActions()).toEqual(expectedActions)
|
expect(getActions()).toEqual(expectedActions)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fetches user data failed", async () => {
|
it("fetches user data failed", async () => {
|
||||||
const { dispatch, getActions } = mockStore()
|
const { dispatch, getActions } = mockStore()
|
||||||
const expectedActions = [
|
const expectedActions = [
|
||||||
{ type: getRequesting.type, payload: strId },
|
{ type: getRequesting.type },
|
||||||
{ type: getFailure.type, payload: { id: strId, error: mockError } },
|
{ type: getFailure.type, payload: mockError },
|
||||||
]
|
]
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
axios.get.mockRejectedValue({ message: mockError })
|
axios.get.mockRejectedValue({ message: mockError })
|
||||||
|
|
||||||
await dispatch(fetchUserData(strId))
|
await dispatch(fetchUserData(membreId))
|
||||||
expect(getActions()).toEqual(expectedActions)
|
expect(getActions()).toEqual(expectedActions)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -11,15 +11,15 @@ import userList, {
|
|||||||
|
|
||||||
jest.mock("axios")
|
jest.mock("axios")
|
||||||
|
|
||||||
const mockData = [
|
const mockData = {
|
||||||
{
|
"1": {
|
||||||
id: 1,
|
membreId: 1,
|
||||||
name: "PeL",
|
name: "PeL",
|
||||||
phone: "+886 0970...",
|
phone: "+886 0970...",
|
||||||
email: "forceoranj@gmail.com",
|
email: "forceoranj@gmail.com",
|
||||||
website: "https://www.parisestludique.fr",
|
website: "https://www.parisestludique.fr",
|
||||||
},
|
},
|
||||||
]
|
}
|
||||||
const mockError = "Oops! Something went wrong."
|
const mockError = "Oops! Something went wrong."
|
||||||
|
|
||||||
describe("userList reducer", () => {
|
describe("userList reducer", () => {
|
||||||
@ -31,8 +31,8 @@ describe("userList reducer", () => {
|
|||||||
it("should handle requesting correctly", () => {
|
it("should handle requesting correctly", () => {
|
||||||
expect(userList(undefined, { type: getRequesting.type })).toEqual({
|
expect(userList(undefined, { type: getRequesting.type })).toEqual({
|
||||||
readyStatus: "request",
|
readyStatus: "request",
|
||||||
items: [],
|
ids: [],
|
||||||
error: null,
|
entities: {},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -40,7 +40,8 @@ describe("userList reducer", () => {
|
|||||||
expect(userList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
expect(userList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
||||||
...initialState,
|
...initialState,
|
||||||
readyStatus: "success",
|
readyStatus: "success",
|
||||||
items: mockData,
|
ids: [1],
|
||||||
|
entities: mockData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,31 +1,26 @@
|
|||||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
import { Envie, EnvieWithoutId, addEnvie } from "../services/envies"
|
import { Envie, EnvieWithoutId, addEnvie } from "../services/envies"
|
||||||
import { AppThunk } from "."
|
import { AppThunk } from "."
|
||||||
|
|
||||||
interface EnvieRequest {
|
const envieAdapter = createEntityAdapter<Envie>({
|
||||||
readyStatus: string
|
selectId: (envie) => envie.envieId,
|
||||||
items: Envie | null
|
})
|
||||||
error: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState: EnvieRequest = {
|
const envieAdd = createSlice({
|
||||||
readyStatus: "invalid",
|
|
||||||
items: null,
|
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const envieList = createSlice({
|
|
||||||
name: "addEnvie",
|
name: "addEnvie",
|
||||||
initialState,
|
initialState: envieAdapter.getInitialState({
|
||||||
|
readyStatus: "idle",
|
||||||
|
} as StateRequest),
|
||||||
reducers: {
|
reducers: {
|
||||||
getRequesting: (state: EnvieRequest) => {
|
getRequesting: (state) => {
|
||||||
state.readyStatus = "request"
|
state.readyStatus = "request"
|
||||||
},
|
},
|
||||||
getSuccess: (state, { payload }: PayloadAction<Envie>) => {
|
getSuccess: (state, { payload }: PayloadAction<Envie>) => {
|
||||||
state.readyStatus = "success"
|
state.readyStatus = "success"
|
||||||
state.items = payload
|
envieAdapter.addOne(state, payload)
|
||||||
},
|
},
|
||||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||||
state.readyStatus = "failure"
|
state.readyStatus = "failure"
|
||||||
@ -34,8 +29,8 @@ const envieList = createSlice({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default envieList.reducer
|
export default envieAdd.reducer
|
||||||
export const { getRequesting, getSuccess, getFailure } = envieList.actions
|
export const { getRequesting, getSuccess, getFailure } = envieAdd.actions
|
||||||
|
|
||||||
export const postEnvie =
|
export const postEnvie =
|
||||||
(envieWithoutId: EnvieWithoutId): AppThunk =>
|
(envieWithoutId: EnvieWithoutId): AppThunk =>
|
||||||
@ -46,7 +41,7 @@ export const postEnvie =
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dispatch(getFailure(error.message))
|
dispatch(getFailure(error.message))
|
||||||
toast.error(`Erreur lors de l'ajout: ${error.message}`, {
|
toast.error(`Erreur lors de l'ajout d'une envie: ${error.message}`, {
|
||||||
position: "top-center",
|
position: "top-center",
|
||||||
autoClose: 6000,
|
autoClose: 6000,
|
||||||
hideProgressBar: true,
|
hideProgressBar: true,
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
import { Envie, getEnvieList } from "../services/envies"
|
import { Envie, getEnvieList } from "../services/envies"
|
||||||
import { AppThunk, AppState } from "."
|
import { AppThunk, AppState } from "."
|
||||||
|
|
||||||
interface EnvieListRequest {
|
const envieAdapter = createEntityAdapter<Envie>({
|
||||||
readyStatus: string
|
selectId: (envie) => envie.envieId,
|
||||||
items: Envie[]
|
})
|
||||||
error: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState: EnvieListRequest = {
|
|
||||||
readyStatus: "invalid",
|
|
||||||
items: [],
|
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const envieList = createSlice({
|
const envieList = createSlice({
|
||||||
name: "getEnvieList",
|
name: "getEnvieList",
|
||||||
initialState,
|
initialState: envieAdapter.getInitialState({
|
||||||
|
readyStatus: "idle",
|
||||||
|
} as StateRequest),
|
||||||
reducers: {
|
reducers: {
|
||||||
getRequesting: (state: EnvieListRequest) => {
|
getRequesting: (state) => {
|
||||||
state.readyStatus = "request"
|
state.readyStatus = "request"
|
||||||
},
|
},
|
||||||
getSuccess: (state, { payload }: PayloadAction<Envie[]>) => {
|
getSuccess: (state, { payload }: PayloadAction<Envie[]>) => {
|
||||||
state.readyStatus = "success"
|
state.readyStatus = "success"
|
||||||
state.items = payload
|
envieAdapter.setAll(state, payload)
|
||||||
},
|
},
|
||||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||||
state.readyStatus = "failure"
|
state.readyStatus = "failure"
|
||||||
@ -43,6 +39,15 @@ export const fetchEnvieList = (): AppThunk => async (dispatch) => {
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dispatch(getFailure(error.message))
|
dispatch(getFailure(error.message))
|
||||||
|
toast.error(`Erreur lors du chargement des envies: ${error.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 6000,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
dispatch(getSuccess(data as Envie[]))
|
dispatch(getSuccess(data as Envie[]))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { createMemoryHistory, createBrowserHistory } from "history"
|
import { createMemoryHistory, createBrowserHistory } from "history"
|
||||||
import { Action, configureStore } from "@reduxjs/toolkit"
|
import { Action, configureStore, EntityState } from "@reduxjs/toolkit"
|
||||||
import { ThunkAction } from "redux-thunk"
|
import { ThunkAction } from "redux-thunk"
|
||||||
import { routerMiddleware } from "connected-react-router"
|
import { routerMiddleware } from "connected-react-router"
|
||||||
|
|
||||||
import createRootReducer from "./rootReducer"
|
import createRootReducer from "./rootReducer"
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
|
|
||||||
interface Arg {
|
interface Arg {
|
||||||
initialState?: typeof window.__INITIAL_STATE__
|
initialState?: typeof window.__INITIAL_STATE__
|
||||||
@ -38,4 +39,6 @@ export type AppDispatch = typeof store.dispatch
|
|||||||
|
|
||||||
export type AppThunk = ThunkAction<void, AppState, unknown, Action<string>>
|
export type AppThunk = ThunkAction<void, AppState, unknown, Action<string>>
|
||||||
|
|
||||||
|
export type EntitiesRequest<T> = EntityState<T> & StateRequest
|
||||||
|
|
||||||
export default createStore
|
export default createStore
|
||||||
|
64
src/store/jeuJavList.ts
Normal file
64
src/store/jeuJavList.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
|
import { JeuJav, getJeuJavList } from "../services/jeuJav"
|
||||||
|
import { AppThunk, AppState } from "."
|
||||||
|
|
||||||
|
const jeuJavAdapter = createEntityAdapter<JeuJav>({
|
||||||
|
selectId: (jeuJav) => jeuJav.jeuId,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const initialState = jeuJavAdapter.getInitialState({
|
||||||
|
readyStatus: "idle",
|
||||||
|
} as StateRequest)
|
||||||
|
|
||||||
|
const jeuJavList = createSlice({
|
||||||
|
name: "jeuJavList",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
getRequesting: (state) => {
|
||||||
|
state.readyStatus = "request"
|
||||||
|
},
|
||||||
|
getSuccess: (state, { payload }: PayloadAction<JeuJav[]>) => {
|
||||||
|
state.readyStatus = "success"
|
||||||
|
jeuJavAdapter.setAll(state, payload)
|
||||||
|
},
|
||||||
|
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||||
|
state.readyStatus = "failure"
|
||||||
|
state.error = payload
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default jeuJavList.reducer
|
||||||
|
export const { getRequesting, getSuccess, getFailure } = jeuJavList.actions
|
||||||
|
|
||||||
|
export const fetchJeuJavList = (): AppThunk => async (dispatch) => {
|
||||||
|
dispatch(getRequesting())
|
||||||
|
|
||||||
|
const { error, data } = await getJeuJavList()
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
dispatch(getFailure(error.message))
|
||||||
|
toast.error(`Erreur lors du chargement des jeux JAV: ${error.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 6000,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dispatch(getSuccess(data as JeuJav[]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldFetchJeuJavList = (state: AppState) => state.jeuJavList.readyStatus !== "success"
|
||||||
|
|
||||||
|
export const fetchJeuJavListIfNeed = (): AppThunk => (dispatch, getState) => {
|
||||||
|
if (shouldFetchJeuJavList(getState())) return dispatch(fetchJeuJavList())
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
|
||||||
|
|
||||||
import { JeuxJav, getJeuxJavList } from "../services/jeuxJav"
|
|
||||||
import { AppThunk, AppState } from "."
|
|
||||||
|
|
||||||
interface JeuxJavList {
|
|
||||||
readyStatus: string // TODO Change it to: "invalid" | "request" | "success" | "failure"
|
|
||||||
items: JeuxJav[]
|
|
||||||
error: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState: JeuxJavList = {
|
|
||||||
readyStatus: "invalid",
|
|
||||||
items: [],
|
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const jeuxJavList = createSlice({
|
|
||||||
name: "jeuxJavList",
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
getRequesting: (state: JeuxJavList) => {
|
|
||||||
state.readyStatus = "request"
|
|
||||||
},
|
|
||||||
getSuccess: (state, { payload }: PayloadAction<JeuxJav[]>) => {
|
|
||||||
state.readyStatus = "success"
|
|
||||||
state.items = payload
|
|
||||||
},
|
|
||||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
|
||||||
state.readyStatus = "failure"
|
|
||||||
state.error = payload
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default jeuxJavList.reducer
|
|
||||||
export const { getRequesting, getSuccess, getFailure } = jeuxJavList.actions
|
|
||||||
|
|
||||||
export const fetchJeuxJavList = (): AppThunk => async (dispatch) => {
|
|
||||||
dispatch(getRequesting())
|
|
||||||
|
|
||||||
const { error, data } = await getJeuxJavList()
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
dispatch(getFailure(error.message))
|
|
||||||
} else {
|
|
||||||
dispatch(getSuccess(data as JeuxJav[]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldFetchJeuxJavList = (state: AppState) => state.jeuxJavList.readyStatus !== "success"
|
|
||||||
|
|
||||||
export const fetchJeuxJavListIfNeed = (): AppThunk => (dispatch, getState) => {
|
|
||||||
if (shouldFetchJeuxJavList(getState())) return dispatch(fetchJeuxJavList())
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import { connectRouter } from "connected-react-router"
|
|||||||
|
|
||||||
import userList from "./userList"
|
import userList from "./userList"
|
||||||
import userData from "./userData"
|
import userData from "./userData"
|
||||||
import jeuxJavList from "./jeuxJavList"
|
import jeuJavList from "./jeuJavList"
|
||||||
import envieList from "./envieList"
|
import envieList from "./envieList"
|
||||||
|
|
||||||
// Use inferred return type for making correctly Redux types
|
// Use inferred return type for making correctly Redux types
|
||||||
@ -11,7 +11,7 @@ import envieList from "./envieList"
|
|||||||
export default (history: History) => ({
|
export default (history: History) => ({
|
||||||
userList,
|
userList,
|
||||||
userData,
|
userData,
|
||||||
jeuxJavList,
|
jeuJavList,
|
||||||
envieList,
|
envieList,
|
||||||
router: connectRouter(history) as any,
|
router: connectRouter(history) as any,
|
||||||
// Register more reducers...
|
// Register more reducers...
|
||||||
|
@ -1,39 +1,31 @@
|
|||||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
import { User, getUserData } from "../services/jsonPlaceholder"
|
import { User, getUserData } from "../services/jsonPlaceholder"
|
||||||
import { AppThunk, AppState } from "."
|
import { AppThunk, AppState } from "."
|
||||||
|
|
||||||
interface UserDate {
|
type StateUser = { entity?: User } & StateRequest
|
||||||
[id: string]: {
|
|
||||||
readyStatus: string
|
|
||||||
item?: User
|
|
||||||
error?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Success {
|
export const initialState: StateUser = {
|
||||||
id: string
|
readyStatus: "idle",
|
||||||
item: User
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Failure {
|
|
||||||
id: string
|
|
||||||
error: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const userData = createSlice({
|
const userData = createSlice({
|
||||||
name: "userData",
|
name: "userData",
|
||||||
initialState: {} as UserDate,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
getRequesting: (state, { payload }: PayloadAction<string>) => {
|
getRequesting: (_) => ({
|
||||||
state[payload] = { readyStatus: "request" }
|
readyStatus: "request",
|
||||||
},
|
}),
|
||||||
getSuccess: (state, { payload }: PayloadAction<Success>) => {
|
getSuccess: (_, { payload }: PayloadAction<User>) => ({
|
||||||
state[payload.id] = { readyStatus: "success", item: payload.item }
|
readyStatus: "success",
|
||||||
},
|
entity: payload,
|
||||||
getFailure: (state, { payload }: PayloadAction<Failure>) => {
|
}),
|
||||||
state[payload.id] = { readyStatus: "failure", error: payload.error }
|
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
||||||
},
|
readyStatus: "failure",
|
||||||
|
error: payload,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -41,24 +33,34 @@ export default userData.reducer
|
|||||||
export const { getRequesting, getSuccess, getFailure } = userData.actions
|
export const { getRequesting, getSuccess, getFailure } = userData.actions
|
||||||
|
|
||||||
export const fetchUserData =
|
export const fetchUserData =
|
||||||
(id: string): AppThunk =>
|
(id: number): AppThunk =>
|
||||||
async (dispatch) => {
|
async (dispatch) => {
|
||||||
dispatch(getRequesting(id))
|
dispatch(getRequesting())
|
||||||
|
|
||||||
const { error, data } = await getUserData(id)
|
const { error, data } = await getUserData(id)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dispatch(getFailure({ id, error: error.message }))
|
dispatch(getFailure(error.message))
|
||||||
|
toast.error(`Erreur lors du chargement de l'utilisateur ${id}: ${error.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 6000,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
dispatch(getSuccess({ id, item: data as User }))
|
dispatch(getSuccess(data as User))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldFetchUserData = (state: AppState, id: string) =>
|
const shouldFetchUserData = (state: AppState, id: number) =>
|
||||||
state.userData[id]?.readyStatus !== "success"
|
state.userData.readyStatus !== "success" ||
|
||||||
|
(state.userData.entity && state.userData.entity.membreId !== id)
|
||||||
|
|
||||||
export const fetchUserDataIfNeed =
|
export const fetchUserDataIfNeed =
|
||||||
(id: string): AppThunk =>
|
(id: number): AppThunk =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
if (shouldFetchUserData(getState(), id)) return dispatch(fetchUserData(id))
|
if (shouldFetchUserData(getState(), id)) return dispatch(fetchUserData(id))
|
||||||
|
|
||||||
|
@ -1,30 +1,28 @@
|
|||||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
|
import { StateRequest } from "./utils"
|
||||||
import { User, getUserList } from "../services/jsonPlaceholder"
|
import { User, getUserList } from "../services/jsonPlaceholder"
|
||||||
import { AppThunk, AppState } from "."
|
import { AppThunk, AppState } from "."
|
||||||
|
|
||||||
interface UserList {
|
const userAdapter = createEntityAdapter<User>({
|
||||||
readyStatus: string
|
selectId: (user) => user.membreId,
|
||||||
items: User[]
|
})
|
||||||
error: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState: UserList = {
|
export const initialState = userAdapter.getInitialState({
|
||||||
readyStatus: "invalid",
|
readyStatus: "idle",
|
||||||
items: [],
|
} as StateRequest)
|
||||||
error: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const userList = createSlice({
|
const userList = createSlice({
|
||||||
name: "userList",
|
name: "userList",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
getRequesting: (state: UserList) => {
|
getRequesting: (state) => {
|
||||||
state.readyStatus = "request"
|
state.readyStatus = "request"
|
||||||
},
|
},
|
||||||
getSuccess: (state, { payload }: PayloadAction<User[]>) => {
|
getSuccess: (state, { payload }: PayloadAction<User[]>) => {
|
||||||
state.readyStatus = "success"
|
state.readyStatus = "success"
|
||||||
state.items = payload
|
userAdapter.setAll(state, payload)
|
||||||
},
|
},
|
||||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||||
state.readyStatus = "failure"
|
state.readyStatus = "failure"
|
||||||
@ -43,6 +41,15 @@ export const fetchUserList = (): AppThunk => async (dispatch) => {
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dispatch(getFailure(error.message))
|
dispatch(getFailure(error.message))
|
||||||
|
toast.error(`Erreur lors du chargement des utilisateurs: ${error.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 6000,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
dispatch(getSuccess(data as User[]))
|
dispatch(getSuccess(data as User[]))
|
||||||
}
|
}
|
||||||
|
4
src/store/utils.ts
Normal file
4
src/store/utils.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface StateRequest {
|
||||||
|
readyStatus: "idle" | "request" | "success" | "failure"
|
||||||
|
error?: string
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user