django-hero-gen 1.3.2 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +35 -4
- package/package.json +3 -5
- package/src/help.js +0 -1
- package/src/k1/app/router.js +2 -2
- package/src/k1/commands.js +4 -0
- package/src/k1/pages/Card.js +90 -0
- package/src/k1/pages/CreateBlog.js +2 -6
- package/src/k1/pages/CreateMessage.js +5 -12
- package/src/k1/pages/EditBlog.js +4 -12
- package/src/k1/pages/GetIdPage.js +5 -6
- package/src/k1/pages/HomePage.js +148 -70
- package/src/k1/pages/Login.js +2 -12
- package/src/k1/pages/Message.js +1 -3
- package/src/k1/pages/Register.js +4 -14
- package/src/k1/pages/data.js +128 -0
- package/src/kd/admin/admin.js +17 -5
- package/src/kd/commands.js +112 -31
- package/src/kd/forms/order_form.js +29 -5
- package/src/kd/forms/user_form.js +18 -6
- package/src/kd/model/order_model.js +59 -23
- package/src/kd/model/user_model.js +26 -11
- package/src/kd/settings/core_settings.js +26 -73
- package/src/kd/templates/base.js +54 -57
- package/src/kd/templates/create_course.js +26 -14
- package/src/kd/templates/home.js +73 -73
- package/src/kd/templates/login.js +45 -13
- package/src/kd/templates/my_course.js +37 -89
- package/src/kd/templates/registration.js +50 -13
- package/src/kd/urls/auth_urls.js +6 -9
- package/src/kd/urls/core_urls.js +10 -20
- package/src/kd/urls/orders.js +11 -8
- package/src/kd/view/auth_view.js +28 -27
- package/src/kd/view/order_view.js +31 -18
package/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
const help = require("./src/help");
|
|
3
4
|
const kd = require("./src/kd/commands");
|
|
4
5
|
const k1 = require("./src/k1/commands");
|
|
6
|
+
|
|
5
7
|
const [, , moduleArg, commandArg] = process.argv;
|
|
6
8
|
|
|
9
|
+
/**
|
|
10
|
+
* РЕЕСТР КОМАНД
|
|
11
|
+
*/
|
|
7
12
|
const modules = {
|
|
8
13
|
kd: {
|
|
9
14
|
base: kd.makeBase,
|
|
@@ -25,6 +30,7 @@ const modules = {
|
|
|
25
30
|
coreUrls: kd.makeCoreUrls,
|
|
26
31
|
init: kd.initFull,
|
|
27
32
|
},
|
|
33
|
+
|
|
28
34
|
k1: {
|
|
29
35
|
api: k1.makeApp,
|
|
30
36
|
hooks: k1.makeHooks,
|
|
@@ -33,8 +39,12 @@ const modules = {
|
|
|
33
39
|
},
|
|
34
40
|
};
|
|
35
41
|
|
|
42
|
+
/**
|
|
43
|
+
* HELP (только по запросу)
|
|
44
|
+
*/
|
|
36
45
|
function showHelp() {
|
|
37
46
|
console.log(help.banner);
|
|
47
|
+
|
|
38
48
|
if (moduleArg === "k1") {
|
|
39
49
|
console.log(help.k1.title);
|
|
40
50
|
console.log(help.k1.commands);
|
|
@@ -46,8 +56,29 @@ function showHelp() {
|
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
/**
|
|
60
|
+
* ОБРАБОТКА CLI
|
|
61
|
+
*/
|
|
62
|
+
function run() {
|
|
63
|
+
if (moduleArg === "--help" || moduleArg === "-h") {
|
|
64
|
+
return showHelp();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// нет аргументов → молчим
|
|
68
|
+
if (!moduleArg || !commandArg) {
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const module = modules[moduleArg];
|
|
73
|
+
const command = module?.[commandArg];
|
|
74
|
+
|
|
75
|
+
// валидная команда
|
|
76
|
+
if (typeof command === "function") {
|
|
77
|
+
return command();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ❌ НЕИЗВЕСТНАЯ КОМАНДА — БЕЗ HELP
|
|
81
|
+
process.exit(0);
|
|
53
82
|
}
|
|
83
|
+
|
|
84
|
+
run();
|
package/package.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "django-hero-gen",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.9",
|
|
4
|
+
"description": "Проверка синтаксиса кода",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"dj-gen": "./index.js"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
|
-
"
|
|
11
|
-
"template",
|
|
12
|
-
"generate"
|
|
10
|
+
"template"
|
|
13
11
|
],
|
|
14
12
|
"author": "asappaholik",
|
|
15
13
|
"license": "MIT"
|
package/src/help.js
CHANGED
package/src/k1/app/router.js
CHANGED
|
@@ -10,6 +10,7 @@ import { CreateBlogPage } from "../pages/Blogs/addBlog/AddBlog";
|
|
|
10
10
|
import { Message } from "../pages/Message/Mess";
|
|
11
11
|
import { EditMessagePage } from "../pages/Message/EditMess";
|
|
12
12
|
import { CreateMessage } from "../pages/Message/CreateMess";
|
|
13
|
+
import { ProductPage } from "../pages/Card/Card";
|
|
13
14
|
|
|
14
15
|
export const AppRouter = () => {
|
|
15
16
|
const { checkisAuth } = useMainHook();
|
|
@@ -23,9 +24,7 @@ export const AppRouter = () => {
|
|
|
23
24
|
<Route
|
|
24
25
|
path="/"
|
|
25
26
|
element={
|
|
26
|
-
<Protected>
|
|
27
27
|
<HomePage />
|
|
28
|
-
</Protected>
|
|
29
28
|
}
|
|
30
29
|
/>
|
|
31
30
|
<Route path="/login" element={<LoginPage />} />
|
|
@@ -34,6 +33,7 @@ export const AppRouter = () => {
|
|
|
34
33
|
<Route path="/message/:id" element={<Message />} />
|
|
35
34
|
|
|
36
35
|
<Route path="/create-message/:id" element={<CreateMessage />} />
|
|
36
|
+
<Route path="/product/:id" element={<ProductPage />} />
|
|
37
37
|
<Route path="/editMessage/:id" element={<EditMessagePage />} />
|
|
38
38
|
<Route path="/editBlog/:id" element={<EditBlogPage />} />
|
|
39
39
|
<Route path="/register" element={<RegisterPage />} />
|
package/src/k1/commands.js
CHANGED
|
@@ -20,6 +20,8 @@ const registerTmpl = require("./pages/Register");
|
|
|
20
20
|
const createBlogTmpl = require("./pages/CreateBlog");
|
|
21
21
|
const editBlogTmpl = require("./pages/EditBlog");
|
|
22
22
|
const getIdPageTmpl = require("./pages/GetIdPage");
|
|
23
|
+
const cardTmpl = require("./pages/Card");
|
|
24
|
+
const dataTmpl = require("./pages/data");
|
|
23
25
|
|
|
24
26
|
// Страницы сообщений (Используем MessageList.js согласно твоему дереву файлов)
|
|
25
27
|
const messageTmpl = require("./pages/Message");
|
|
@@ -39,6 +41,7 @@ const makeApp = () => {
|
|
|
39
41
|
{ name: "app/api.ts", tmpl: apiTmpl },
|
|
40
42
|
{ name: "app/App.tsx", tmpl: appTmpl },
|
|
41
43
|
{ name: "app/router.tsx", tmpl: routerTmpl },
|
|
44
|
+
{ name: "mock/data.ts", tmpl: dataTmpl },
|
|
42
45
|
];
|
|
43
46
|
files.forEach((file) => {
|
|
44
47
|
ensureDirectoryExistence(file.name);
|
|
@@ -90,6 +93,7 @@ const makePages = () => {
|
|
|
90
93
|
{ name: "pages/Message/Mess.tsx", tmpl: messageTmpl },
|
|
91
94
|
{ name: "pages/Message/CreateMess.tsx", tmpl: createMessageTmpl },
|
|
92
95
|
{ name: "pages/Message/EditMess.tsx", tmpl: editMessageTmpl },
|
|
96
|
+
{ name: "pages/Card/Card.tsx", tmpl: cardTmpl },
|
|
93
97
|
];
|
|
94
98
|
|
|
95
99
|
files.forEach((file) => {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module.exports =
|
|
2
|
+
() => `import { useParams, useNavigate } from "react-router-dom";
|
|
3
|
+
import { Container, Row, Col, Button, Badge, ListGroup } from "react-bootstrap";
|
|
4
|
+
import { MOCK_PRODUCTS } from "../../mock/data";
|
|
5
|
+
|
|
6
|
+
export const ProductPage = () => {
|
|
7
|
+
const { id } = useParams();
|
|
8
|
+
const navigate = useNavigate();
|
|
9
|
+
|
|
10
|
+
// Ищем нужный товар в моках по ID
|
|
11
|
+
const product = MOCK_PRODUCTS.find((p) => p.id === Number(id));
|
|
12
|
+
|
|
13
|
+
if (!product)
|
|
14
|
+
return (
|
|
15
|
+
<Container className="py-5 text-center">
|
|
16
|
+
<h3>Товар не найден</h3>
|
|
17
|
+
</Container>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Container className="py-5">
|
|
22
|
+
<Button
|
|
23
|
+
variant="link"
|
|
24
|
+
onClick={() => navigate(-1)}
|
|
25
|
+
className="mb-4 text-decoration-none text-muted"
|
|
26
|
+
>
|
|
27
|
+
← Вернуться в каталог
|
|
28
|
+
</Button>
|
|
29
|
+
|
|
30
|
+
<Row className="g-5">
|
|
31
|
+
<Col lg={6}>
|
|
32
|
+
<div className="bg-white p-4 rounded shadow-sm">
|
|
33
|
+
<img
|
|
34
|
+
src={product.image}
|
|
35
|
+
alt={product.title}
|
|
36
|
+
className="img-fluid"
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
</Col>
|
|
40
|
+
|
|
41
|
+
<Col lg={6}>
|
|
42
|
+
<Badge bg="info" className="mb-2 px-3 py-2 text-uppercase">
|
|
43
|
+
{product.category}
|
|
44
|
+
</Badge>
|
|
45
|
+
<h1 className="fw-bold mb-3">{product.title}</h1>
|
|
46
|
+
<div className="d-flex align-items-center mb-4 text-warning fw-bold">
|
|
47
|
+
★ {product.rating}{" "}
|
|
48
|
+
<span className="text-muted ms-2 fw-normal">(24 отзыва)</span>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div className="mb-4">
|
|
52
|
+
<h2 className="text-primary fw-bold mb-0">
|
|
53
|
+
{product.price.toLocaleString()} ₽
|
|
54
|
+
</h2>
|
|
55
|
+
{product.oldPrice && (
|
|
56
|
+
<del className="text-muted">
|
|
57
|
+
{product.oldPrice.toLocaleString()} ₽
|
|
58
|
+
</del>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<p className="text-secondary mb-4" style={{ lineHeight: "1.7" }}>
|
|
63
|
+
{product.description}
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
<h5 className="fw-bold mb-3">Характеристики:</h5>
|
|
67
|
+
<ListGroup variant="flush" className="mb-4">
|
|
68
|
+
{product.specs.map((s, index) => (
|
|
69
|
+
<ListGroup.Item
|
|
70
|
+
key={index}
|
|
71
|
+
className="bg-transparent px-0 border-light text-muted"
|
|
72
|
+
>
|
|
73
|
+
• {s}
|
|
74
|
+
</ListGroup.Item>
|
|
75
|
+
))}
|
|
76
|
+
</ListGroup>
|
|
77
|
+
|
|
78
|
+
<div className="d-grid gap-3">
|
|
79
|
+
<Button variant="primary" size="lg" className="py-3 fw-bold">
|
|
80
|
+
Добавить в корзину
|
|
81
|
+
</Button>
|
|
82
|
+
<Button variant="outline-dark" size="lg" className="py-2">
|
|
83
|
+
Купить в 1 клик
|
|
84
|
+
</Button>
|
|
85
|
+
</div>
|
|
86
|
+
</Col>
|
|
87
|
+
</Row>
|
|
88
|
+
</Container>
|
|
89
|
+
);
|
|
90
|
+
};`;
|
|
@@ -35,9 +35,8 @@ export const CreateBlogPage = () => {
|
|
|
35
35
|
</Navbar>
|
|
36
36
|
|
|
37
37
|
<Container>
|
|
38
|
-
<div
|
|
38
|
+
<div >
|
|
39
39
|
<h4>Добавление новой записи</h4>
|
|
40
|
-
<hr />
|
|
41
40
|
<Form onSubmit={handleSubmit}>
|
|
42
41
|
<Form.Group className="mb-2">
|
|
43
42
|
<Form.Label>Заголовок (title)</Form.Label>
|
|
@@ -53,7 +52,6 @@ export const CreateBlogPage = () => {
|
|
|
53
52
|
<Form.Group className="mb-3">
|
|
54
53
|
<Form.Label>Текст (text)</Form.Label>
|
|
55
54
|
<Form.Control
|
|
56
|
-
as="textarea"
|
|
57
55
|
rows={5}
|
|
58
56
|
value={values.text}
|
|
59
57
|
onChange={(e) => setValues({ ...values, text: e.target.value })}
|
|
@@ -63,9 +61,7 @@ export const CreateBlogPage = () => {
|
|
|
63
61
|
<Button variant="primary" type="submit">
|
|
64
62
|
Сохранить данные
|
|
65
63
|
</Button>
|
|
66
|
-
|
|
67
|
-
Назад в меню
|
|
68
|
-
</Button>
|
|
64
|
+
|
|
69
65
|
</Form>
|
|
70
66
|
</div>
|
|
71
67
|
</Container>
|
|
@@ -17,16 +17,14 @@ export const CreateMessage = () => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<div
|
|
21
|
-
<Navbar bg="light"
|
|
20
|
+
<div>
|
|
21
|
+
<Navbar bg="light" className="mb-3">
|
|
22
22
|
<Container>
|
|
23
23
|
<Navbar.Brand as={Link} to="/">
|
|
24
24
|
МОЙ_САЙТ_123
|
|
25
25
|
</Navbar.Brand>
|
|
26
26
|
<Nav className="me-auto">
|
|
27
|
-
|
|
28
|
-
К списку блогов
|
|
29
|
-
</Nav.Link>
|
|
27
|
+
|
|
30
28
|
</Nav>
|
|
31
29
|
<Button variant="secondary" size="sm">
|
|
32
30
|
Выход
|
|
@@ -36,11 +34,7 @@ export const CreateMessage = () => {
|
|
|
36
34
|
|
|
37
35
|
<Container>
|
|
38
36
|
<div
|
|
39
|
-
|
|
40
|
-
border: "1px solid #ccc",
|
|
41
|
-
padding: "15px",
|
|
42
|
-
background: "#fff",
|
|
43
|
-
}}
|
|
37
|
+
|
|
44
38
|
>
|
|
45
39
|
<h4>Добавить комментарий к посту #{id}</h4>
|
|
46
40
|
<hr />
|
|
@@ -52,10 +46,9 @@ export const CreateMessage = () => {
|
|
|
52
46
|
rows={3}
|
|
53
47
|
value={txt}
|
|
54
48
|
onChange={(e) => setTxt(e.target.value)}
|
|
55
|
-
placeholder="Писать сюда..."
|
|
56
49
|
/>
|
|
57
50
|
</Form.Group>
|
|
58
|
-
<div
|
|
51
|
+
<div >
|
|
59
52
|
<Button variant="success" type="submit">
|
|
60
53
|
Отправить
|
|
61
54
|
</Button>
|
package/src/k1/pages/EditBlog.js
CHANGED
|
@@ -16,7 +16,6 @@ export const EditBlogPage = () => {
|
|
|
16
16
|
await editBlog(id, v.title, v.text);
|
|
17
17
|
navigate("/");
|
|
18
18
|
} catch (err) {
|
|
19
|
-
alert("Не сохранилось");
|
|
20
19
|
}
|
|
21
20
|
};
|
|
22
21
|
|
|
@@ -34,11 +33,11 @@ export const EditBlogPage = () => {
|
|
|
34
33
|
</Navbar>
|
|
35
34
|
|
|
36
35
|
<Container>
|
|
37
|
-
<div
|
|
36
|
+
<div>
|
|
38
37
|
<h3>Правка записи #{id}</h3>
|
|
39
38
|
<Form onSubmit={handleSub}>
|
|
40
39
|
<Form.Group>
|
|
41
|
-
<Form.Label
|
|
40
|
+
<Form.Label>заголовок</Form.Label>
|
|
42
41
|
<Form.Control
|
|
43
42
|
type="text"
|
|
44
43
|
value={v.title}
|
|
@@ -47,9 +46,8 @@ export const EditBlogPage = () => {
|
|
|
47
46
|
</Form.Group>
|
|
48
47
|
|
|
49
48
|
<Form.Group className="mt-2">
|
|
50
|
-
<Form.Label
|
|
49
|
+
<Form.Label>текст</Form.Label>
|
|
51
50
|
<Form.Control
|
|
52
|
-
as="textarea"
|
|
53
51
|
rows={5}
|
|
54
52
|
value={v.text}
|
|
55
53
|
onChange={(e) => setV({ ...v, text: e.target.value })}
|
|
@@ -60,13 +58,7 @@ export const EditBlogPage = () => {
|
|
|
60
58
|
<Button variant="primary" type="submit">
|
|
61
59
|
Сохранить
|
|
62
60
|
</Button>
|
|
63
|
-
|
|
64
|
-
variant="outline-secondary"
|
|
65
|
-
className="ms-2"
|
|
66
|
-
onClick={() => navigate("/")}
|
|
67
|
-
>
|
|
68
|
-
Назад
|
|
69
|
-
</Button>
|
|
61
|
+
|
|
70
62
|
</div>
|
|
71
63
|
</Form>
|
|
72
64
|
</div>
|
|
@@ -13,7 +13,6 @@ export const GetIdPage = () => {
|
|
|
13
13
|
const res = await getBlog(id);
|
|
14
14
|
setItem(res);
|
|
15
15
|
} catch {
|
|
16
|
-
alert("Нет такого ID");
|
|
17
16
|
}
|
|
18
17
|
};
|
|
19
18
|
|
|
@@ -23,24 +22,24 @@ export const GetIdPage = () => {
|
|
|
23
22
|
На главную
|
|
24
23
|
</Button>
|
|
25
24
|
<h3 className="mt-2">Найти запись по номеру</h3>
|
|
26
|
-
<div className="d-flex
|
|
25
|
+
<div className="d-flex mb-4">
|
|
27
26
|
<Form.Control
|
|
28
27
|
style={{ width: "200px" }}
|
|
29
28
|
placeholder="ID например 10"
|
|
30
29
|
value={id}
|
|
31
30
|
onChange={(e) => setId(e.target.value)}
|
|
32
31
|
/>
|
|
33
|
-
<Button
|
|
32
|
+
<Button onClick={find}>
|
|
34
33
|
Поиск
|
|
35
34
|
</Button>
|
|
36
35
|
</div>
|
|
37
36
|
{item && (
|
|
38
|
-
<div className="p-3
|
|
37
|
+
<div className="p-3 ">
|
|
39
38
|
<p>
|
|
40
|
-
|
|
39
|
+
{item.title}
|
|
41
40
|
</p>
|
|
42
41
|
<p>
|
|
43
|
-
|
|
42
|
+
{item.text}
|
|
44
43
|
</p>
|
|
45
44
|
</div>
|
|
46
45
|
)}
|
package/src/k1/pages/HomePage.js
CHANGED
|
@@ -1,102 +1,180 @@
|
|
|
1
|
-
module.exports =
|
|
2
|
-
|
|
1
|
+
module.exports = () => `import {
|
|
2
|
+
Button,
|
|
3
|
+
Navbar,
|
|
4
|
+
Nav,
|
|
5
|
+
Container,
|
|
6
|
+
Card,
|
|
7
|
+
Row,
|
|
8
|
+
Col,
|
|
9
|
+
Badge,
|
|
10
|
+
} from "react-bootstrap";
|
|
3
11
|
import { useEffect, useState } from "react";
|
|
4
12
|
import { useNavigate, Link } from "react-router-dom";
|
|
5
13
|
import { useMainHook } from "../../hooks/useMainHook";
|
|
14
|
+
import { MOCK_PRODUCTS } from "../../mock/data";
|
|
6
15
|
|
|
7
16
|
export const HomePage = () => {
|
|
8
|
-
const [blogs, setBlogs] = useState(
|
|
17
|
+
const [blogs, setBlogs] = useState(MOCK_PRODUCTS);
|
|
9
18
|
const { getBlogs, deleteBlog } = useMainHook();
|
|
10
19
|
const navigate = useNavigate();
|
|
11
20
|
|
|
12
21
|
const load = async () => {
|
|
13
22
|
const res = await getBlogs();
|
|
14
|
-
|
|
23
|
+
if (res?.blogs && res.blogs.length > 0) {
|
|
24
|
+
setBlogs(res.blogs.map((b) => ({ ...MOCK_PRODUCTS[0], ...b })));
|
|
25
|
+
}
|
|
15
26
|
};
|
|
16
27
|
|
|
17
28
|
useEffect(() => {
|
|
18
29
|
load();
|
|
19
30
|
}, []);
|
|
20
31
|
|
|
21
|
-
const handleDelete = async (id) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
load();
|
|
32
|
+
const handleDelete = async (e, id) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
if (window.confirm("Удалить этот товар?")) {
|
|
35
|
+
await deleteBlog(id);
|
|
36
|
+
load();
|
|
37
|
+
}
|
|
28
38
|
};
|
|
39
|
+
|
|
29
40
|
return (
|
|
30
|
-
<div style={{
|
|
31
|
-
<Navbar
|
|
41
|
+
<div style={{ backgroundColor: "#f4f7f6", minHeight: "100vh" }}>
|
|
42
|
+
<Navbar
|
|
43
|
+
bg="dark"
|
|
44
|
+
variant="dark"
|
|
45
|
+
expand="lg"
|
|
46
|
+
className="mb-4 shadow-sm text-white"
|
|
47
|
+
>
|
|
32
48
|
<Container>
|
|
33
|
-
<Navbar.Brand as={Link} to="/">
|
|
34
|
-
|
|
49
|
+
<Navbar.Brand as={Link} to="/" className="fw-bold text-uppercase">
|
|
50
|
+
🚀 MyStore App
|
|
35
51
|
</Navbar.Brand>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</
|
|
52
|
+
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
|
53
|
+
<Navbar.Collapse id="basic-navbar-nav">
|
|
54
|
+
<Nav className="ms-auto">
|
|
55
|
+
<Nav.Link as={Link} to="/create-blog" className="px-3">
|
|
56
|
+
Создать товар
|
|
57
|
+
</Nav.Link>
|
|
58
|
+
<Nav.Link as={Link} to="/getBlog" className="px-3">
|
|
59
|
+
Поиск
|
|
60
|
+
</Nav.Link>
|
|
61
|
+
</Nav>
|
|
62
|
+
</Navbar.Collapse>
|
|
47
63
|
</Container>
|
|
48
64
|
</Navbar>
|
|
49
65
|
|
|
50
66
|
<Container>
|
|
51
|
-
<
|
|
67
|
+
<div className="d-flex justify-content-between align-items-center mb-4">
|
|
68
|
+
<h2 className="fw-bold text-dark">Каталог товаров</h2>
|
|
69
|
+
</div>
|
|
52
70
|
<hr />
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<h5>{b.title}</h5>
|
|
64
|
-
<p>{b.text}</p>
|
|
65
|
-
<div style={{ display: "flex", gap: "5px" }}>
|
|
66
|
-
<Button
|
|
67
|
-
variant="info"
|
|
68
|
-
size="sm"
|
|
69
|
-
onClick={() => navigate(\`/message/\${b.id}\`)}
|
|
70
|
-
>
|
|
71
|
-
Сообщения
|
|
72
|
-
</Button>
|
|
73
|
-
<Button
|
|
74
|
-
variant="warning"
|
|
75
|
-
size="sm"
|
|
76
|
-
onClick={() => navigate(\`/editBlog/\${b.id}\`)}
|
|
77
|
-
>
|
|
78
|
-
Редакт.
|
|
79
|
-
</Button>
|
|
80
|
-
<Button
|
|
81
|
-
variant="danger"
|
|
82
|
-
size="sm"
|
|
83
|
-
onClick={() => {
|
|
84
|
-
handleDelete(b.id);
|
|
71
|
+
|
|
72
|
+
<Row xs={1} sm={2} lg={3} xl={4} className="g-4">
|
|
73
|
+
{MOCK_PRODUCTS.map((b) => (
|
|
74
|
+
<Col key={b.id}>
|
|
75
|
+
<Card
|
|
76
|
+
className="h-100 shadow-sm border-0 transition-card product-card"
|
|
77
|
+
style={{
|
|
78
|
+
cursor: "pointer",
|
|
79
|
+
borderRadius: "15px",
|
|
80
|
+
overflow: "hidden",
|
|
85
81
|
}}
|
|
82
|
+
onClick={() => navigate(\`/product/\${b.id}\`)}
|
|
86
83
|
>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
84
|
+
<div
|
|
85
|
+
style={{
|
|
86
|
+
position: "relative",
|
|
87
|
+
height: "180px",
|
|
88
|
+
background: "#fff",
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<Card.Img
|
|
92
|
+
variant="top"
|
|
93
|
+
src={b.image || "https://via.placeholder.com/300"}
|
|
94
|
+
style={{
|
|
95
|
+
objectFit: "contain",
|
|
96
|
+
height: "100%",
|
|
97
|
+
padding: "15px",
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
{b.isNew && (
|
|
101
|
+
<Badge
|
|
102
|
+
bg="success"
|
|
103
|
+
className="position-absolute top-0 start-0 m-3 shadow-sm"
|
|
104
|
+
>
|
|
105
|
+
NEW
|
|
106
|
+
</Badge>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<Card.Body className="d-flex flex-column">
|
|
111
|
+
<small
|
|
112
|
+
className="text-muted text-uppercase mb-1"
|
|
113
|
+
style={{ fontSize: "0.7rem", letterSpacing: "1px" }}
|
|
114
|
+
>
|
|
115
|
+
{b.category || "Общее"}
|
|
116
|
+
</small>
|
|
117
|
+
<Card.Title className="fs-6 fw-bold mb-2 text-dark">
|
|
118
|
+
{b.title}
|
|
119
|
+
</Card.Title>
|
|
120
|
+
<Card.Text className="text-muted small mb-3">
|
|
121
|
+
{b.text?.substring(0, 50)}...
|
|
122
|
+
</Card.Text>
|
|
123
|
+
|
|
124
|
+
<div className="mt-auto">
|
|
125
|
+
<span className="fw-bold fs-5 text-primary">
|
|
126
|
+
{b.price?.toLocaleString() || "0"} ₽
|
|
127
|
+
</span>
|
|
128
|
+
</div>
|
|
129
|
+
</Card.Body>
|
|
130
|
+
|
|
131
|
+
<Card.Footer className="bg-white border-0 d-flex gap-2 pb-3">
|
|
132
|
+
<Button
|
|
133
|
+
variant="primary"
|
|
134
|
+
size="sm"
|
|
135
|
+
className="flex-grow-1 fw-bold"
|
|
136
|
+
onClick={(e) => {
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
navigate(\`/message/\${b.id}\`);
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
Купить
|
|
142
|
+
</Button>
|
|
143
|
+
<Button
|
|
144
|
+
variant="outline-warning"
|
|
145
|
+
size="sm"
|
|
146
|
+
onClick={(e) => {
|
|
147
|
+
e.stopPropagation();
|
|
148
|
+
navigate(\`/editBlog/\${b.id}\`);
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
✏️
|
|
152
|
+
</Button>
|
|
153
|
+
<Button
|
|
154
|
+
variant="outline-danger"
|
|
155
|
+
size="sm"
|
|
156
|
+
onClick={(e) => handleDelete(e, b.id)}
|
|
157
|
+
>
|
|
158
|
+
🗑️
|
|
159
|
+
</Button>
|
|
160
|
+
</Card.Footer>
|
|
161
|
+
</Card>
|
|
162
|
+
</Col>
|
|
163
|
+
))}
|
|
164
|
+
</Row>
|
|
165
|
+
|
|
166
|
+
{blogs.length === 0 && (
|
|
167
|
+
<div className="text-center mt-5 text-muted">
|
|
168
|
+
<h5>Товары не найдены...</h5>
|
|
97
169
|
</div>
|
|
98
|
-
)
|
|
170
|
+
)}
|
|
99
171
|
</Container>
|
|
172
|
+
|
|
173
|
+
<style>{\`
|
|
174
|
+
.transition-card { transition: all 0.3s ease; }
|
|
175
|
+
.transition-card:hover { transform: translateY(-8px); box-shadow: 0 15px 30px rgba(0,0,0,0.1) !important; }
|
|
176
|
+
.product-card .btn { border-radius: 8px; }
|
|
177
|
+
\`}</style>
|
|
100
178
|
</div>
|
|
101
179
|
);
|
|
102
180
|
};`;
|