Skip to content

Commit 119f789

Browse files
Add store config (#136)
* Add store configuration in tab * Fix items state
1 parent 2ab79b2 commit 119f789

File tree

11 files changed

+494
-307
lines changed

11 files changed

+494
-307
lines changed

package-lock.json

Lines changed: 370 additions & 303 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
"@tinymce/tinymce-react": "^5.1.1",
3838
"@vercel/analytics": "^1.3.1",
3939
"@vercel/speed-insights": "^1.0.12",
40-
"antd": "^5.18.1",
41-
"axios": "^1.7.2",
40+
"antd": "^5.22.2",
41+
"axios": "^1.7.8",
4242
"clsx": "^2.1.1",
4343
"dayjs": "^1.11.11",
4444
"emailjs-com": "^3.2.0",
@@ -105,9 +105,9 @@
105105
"eslint-plugin-unused-imports": "^3.2.0",
106106
"postcss": "^8.4.38",
107107
"prettier": "^3.3.2",
108-
"tailwindcss": "^3.4.4",
108+
"tailwindcss": "^3.4.11",
109109
"typescript": "^4.9.5",
110-
"vite": "^5.3.0",
110+
"vite": "^5.4.11",
111111
"vitest": "^1.6.0"
112112
},
113113
"volta": {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Form, FormInstance, FormProps } from 'antd';
2+
import { useCallback, useEffect, useState } from 'react';
3+
import { IStore, StoreService } from '../../../services/admin/store';
4+
5+
interface IDashboardStoreHooks {
6+
form: FormInstance<IStore>
7+
items: IStore[]
8+
onAddArticle: (values: IStore) => void
9+
deleteArticle: (articleId: string) => void
10+
}
11+
12+
export const useDashboardStore = (): IDashboardStoreHooks => {
13+
const [form] = Form.useForm();
14+
const [items, setItems] = useState<IStore[]>([]);
15+
16+
const onAddArticle: FormProps<IStore>['onFinish'] = useCallback(({ name, price }): void => {
17+
StoreService.addArticle({ name, price, }).then((res) => setItems((prevState) => [...prevState, res]));
18+
form.resetFields();
19+
}, []);
20+
21+
const deleteArticle = useCallback((articleId: string): void => {
22+
StoreService.removeArticle(articleId).then(() =>
23+
setItems((prevState) => prevState.filter((item) => item.id !== articleId))
24+
);
25+
}, []);
26+
27+
useEffect(() => {
28+
StoreService.getAllArticles().then(setItems);
29+
}, []);
30+
31+
return {
32+
form,
33+
items,
34+
onAddArticle,
35+
deleteArticle,
36+
};
37+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import Navigation from '../../../pages/admin/Navigation';
3+
import { Button, Divider, Flex, Form, Input, InputNumber, Typography } from 'antd';
4+
import { formattedPrice } from '../../../lib/form';
5+
import { CloseCircleOutlined } from '@ant-design/icons';
6+
import { useDashboardStore } from './DashboardStore.hooks';
7+
8+
const DashboardStore: React.FC = () => {
9+
const { form, items, onAddArticle, deleteArticle } = useDashboardStore();
10+
return (
11+
<Navigation>
12+
<p className="text-xl mb-2">Liste des articles : </p>
13+
<Form layout="inline" name="addArticle" form={form} onFinish={onAddArticle}>
14+
<Form.Item name="name" rules={[{ required: true, message: 'Veuillez rentrer un nom d\'article' }]}>
15+
<Input placeholder="Nom de l'article"/>
16+
</Form.Item>
17+
<Form.Item name="price" rules={[{ required: true, message: 'Veuillez rentrer un prix' }]}>
18+
<InputNumber min={0} step={0.5} placeholder="Prix"/>
19+
</Form.Item>
20+
<Form.Item>
21+
<Button type="primary" htmlType="submit" className="button">Ajouter</Button>
22+
</Form.Item>
23+
</Form>
24+
<Divider/>
25+
<Flex gap={8} className="mt-4">
26+
{items.length > 0 ? items.map((article) => (
27+
<div key={article.id} className="flex bg-white rounded-md p-2 aspect-[1/1] w-28">
28+
<Flex vertical justify="space-between" gap={8} className="w-full">
29+
<Flex align="center" justify="space-between" className="w-full">
30+
<Typography.Text strong>
31+
{article.name}
32+
</Typography.Text>
33+
<Button
34+
shape="circle"
35+
variant="filled"
36+
color="danger"
37+
size="small"
38+
icon={<CloseCircleOutlined/>}
39+
onClick={() => deleteArticle(article.id)}
40+
/>
41+
</Flex>
42+
<Typography.Text>
43+
{formattedPrice(article.price)}
44+
</Typography.Text>
45+
</Flex>
46+
</div>
47+
)) : <div>Aucun article créé</div>}
48+
</Flex>
49+
</Navigation>
50+
);
51+
};
52+
53+
export default DashboardStore;

src/components/Routes/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const DashboardPartner = loadable(() => import('../Admin/DashboardPartner'));
2020
const DashboardTrombinoscope = loadable(() => import('../Admin/DashboardTrombinoscope'));
2121
const DashboardVote = loadable(() => import('../Admin/DashboardVote'));
2222
const DashboardVoteStatistics = loadable(() => import('../Admin/DashboardVoteStatistics'));
23+
const DashboardStore = loadable(() => import('../Admin/DashboardStore'));
2324
const Footer = loadable(() => import('../../components/Footer'));
2425
import Navbar from '../../components/Navbar';
2526

@@ -35,6 +36,7 @@ const Routes: React.FC = () => {
3536
<Route path={RouterUrl.adminTrombinoscope} element={<DashboardTrombinoscope />} />
3637
<Route path={RouterUrl.adminVote} element={<DashboardVote />} />
3738
<Route path={RouterUrl.adminVoteStatistics} element={<DashboardVoteStatistics />} />
39+
<Route path={RouterUrl.adminStore} element={<DashboardStore />} />
3840

3941
<Route path={RouterUrl.home} element={<Dashboard />} />
4042
</RouterRoutes>

src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum RouterUrl {
2121
adminTrombinoscope = '/trombinoscope',
2222
adminVote = '/votes',
2323
adminVoteStatistics = '/votes/:voteId',
24+
adminStore = '/store',
2425

2526
notFound = '*',
2627
}

src/lib/form.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,8 @@ export const fillFormDataWithKeys = (form: FormData, object: string | object) =>
55
}
66
});
77
};
8+
9+
export const formattedPrice = (price: number): string => new Intl.NumberFormat('fr-FR', {
10+
style: 'currency',
11+
currency: 'EUR'
12+
}).format(price).padEnd(4, '0');

src/pages/admin/Navigation/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MenuOutlined,
77
PlayCircleOutlined,
88
QuestionCircleOutlined,
9+
ShopOutlined,
910
TeamOutlined,
1011
VideoCameraOutlined,
1112
} from '@ant-design/icons';
@@ -53,6 +54,11 @@ const adminRoutes = [
5354
link: RouterUrl.adminVote,
5455
icon: <QuestionCircleOutlined />,
5556
},
57+
{
58+
name: 'Magasin',
59+
link: RouterUrl.adminStore,
60+
icon: <ShopOutlined />,
61+
},
5662
];
5763

5864
interface NavigationProps {

src/services/admin/store/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './store.interface';
2+
export * from './store.service';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface IStore {
2+
id: string
3+
name: string
4+
price: number
5+
image?: string
6+
}

0 commit comments

Comments
 (0)