django-hero-gen 1.3.3 → 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/HomePage.js +148 -62
- 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
|
+
};`;
|
package/src/k1/pages/HomePage.js
CHANGED
|
@@ -1,94 +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 >
|
|
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
|
-
|
|
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>
|
|
44
63
|
</Container>
|
|
45
64
|
</Navbar>
|
|
46
65
|
|
|
47
66
|
<Container>
|
|
48
|
-
<
|
|
67
|
+
<div className="d-flex justify-content-between align-items-center mb-4">
|
|
68
|
+
<h2 className="fw-bold text-dark">Каталог товаров</h2>
|
|
69
|
+
</div>
|
|
49
70
|
<hr />
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
size="sm"
|
|
61
|
-
onClick={() => navigate(\`/message/\${b.id}\`)}
|
|
62
|
-
>
|
|
63
|
-
Сообщения
|
|
64
|
-
</Button>
|
|
65
|
-
<Button
|
|
66
|
-
variant="warning"
|
|
67
|
-
size="sm"
|
|
68
|
-
onClick={() => navigate(\`/editBlog/\${b.id}\`)}
|
|
69
|
-
>
|
|
70
|
-
Редакт.
|
|
71
|
-
</Button>
|
|
72
|
-
<Button
|
|
73
|
-
variant="info"
|
|
74
|
-
size="sm"
|
|
75
|
-
onClick={() => {
|
|
76
|
-
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",
|
|
77
81
|
}}
|
|
82
|
+
onClick={() => navigate(\`/product/\${b.id}\`)}
|
|
78
83
|
>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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>
|
|
89
169
|
</div>
|
|
90
|
-
)
|
|
170
|
+
)}
|
|
91
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>
|
|
92
178
|
</div>
|
|
93
179
|
);
|
|
94
180
|
};`;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
module.exports = () => `export const MOCK_PRODUCTS = [
|
|
2
|
+
{
|
|
3
|
+
id: 1,
|
|
4
|
+
title: "Беспроводные наушники Pro",
|
|
5
|
+
description:
|
|
6
|
+
"Профессиональное звучание с активным шумоподавлением и защитой от влаги IPX7. До 30 часов работы от одного заряда.",
|
|
7
|
+
price: 15900,
|
|
8
|
+
oldPrice: 19000,
|
|
9
|
+
category: "Электроника",
|
|
10
|
+
image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800",
|
|
11
|
+
isNew: true,
|
|
12
|
+
rating: 4.8,
|
|
13
|
+
specs: ["Bluetooth 5.2", "USB-C", "30ч работы"],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 2,
|
|
17
|
+
title: "Механическая клавиатура RGB",
|
|
18
|
+
description:
|
|
19
|
+
"Механические переключатели (Red Switches) с ресурсом 50 млн нажатий и полностью настраиваемой подсветкой.",
|
|
20
|
+
price: 8500,
|
|
21
|
+
category: "Аксессуары",
|
|
22
|
+
image: "https://images.unsplash.com/photo-1511467687858-23d96c32e4ae?w=800",
|
|
23
|
+
isNew: false,
|
|
24
|
+
rating: 4.5,
|
|
25
|
+
specs: ["Mechanical Red", "RGB", "Алюминиевый корпус"],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 3,
|
|
29
|
+
title: "Смарт-часы X-Series",
|
|
30
|
+
description:
|
|
31
|
+
"Ваш персональный тренер на запястье. Измерение пульса, кислорода в крови и 100+ спортивных режимов.",
|
|
32
|
+
price: 12000,
|
|
33
|
+
oldPrice: 14500,
|
|
34
|
+
category: "Гаджеты",
|
|
35
|
+
image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=800",
|
|
36
|
+
isNew: true,
|
|
37
|
+
rating: 4.9,
|
|
38
|
+
specs: ["OLED дисплей", "GPS", "Водонепроницаемость 5ATM"],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 4,
|
|
42
|
+
title: "Кофемашина Barista Home",
|
|
43
|
+
description:
|
|
44
|
+
"Идеальный эспрессо одним нажатием. Встроенный капучинатор и регулировка степени помола зерен.",
|
|
45
|
+
price: 45000,
|
|
46
|
+
oldPrice: 52000,
|
|
47
|
+
category: "Бытовая техника",
|
|
48
|
+
image: "https://images.unsplash.com/photo-1580915411954-282cb1b0d780?w=800",
|
|
49
|
+
isNew: false,
|
|
50
|
+
rating: 4.7,
|
|
51
|
+
specs: ["Давление 15 бар", "Зерновой/Молотый", "Объем 1.8л"],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 5,
|
|
55
|
+
title: "Беззеркальная камера 4K",
|
|
56
|
+
description:
|
|
57
|
+
"Профессиональная съемка видео в 4K и фото высокого разрешения. Быстрый автофокус по глазам.",
|
|
58
|
+
price: 89900,
|
|
59
|
+
category: "Фототехника",
|
|
60
|
+
image: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?w=800",
|
|
61
|
+
isNew: true,
|
|
62
|
+
rating: 5.0,
|
|
63
|
+
specs: ["Матрица 24Мп", "Wi-Fi / NFC", "4K 60fps"],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 6,
|
|
67
|
+
title: "Игровой монитор 144Гц",
|
|
68
|
+
description:
|
|
69
|
+
"Плавный геймплей без разрывов. IPS матрица с откликом 1мс и расширенным цветовым охватом.",
|
|
70
|
+
price: 24500,
|
|
71
|
+
oldPrice: 28900,
|
|
72
|
+
category: "Электроника",
|
|
73
|
+
image: "https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=800",
|
|
74
|
+
isNew: false,
|
|
75
|
+
rating: 4.6,
|
|
76
|
+
specs: ["27 дюймов", "IPS", "DisplayPort / HDMI"],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 7,
|
|
80
|
+
title: "Рюкзак для ноутбука Urban",
|
|
81
|
+
description:
|
|
82
|
+
"Стильный и влагозащищенный рюкзак с отдельным отсеком для ноутбука до 16 дюймов и USB-портом.",
|
|
83
|
+
price: 4200,
|
|
84
|
+
category: "Аксессуары",
|
|
85
|
+
image: "https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=800",
|
|
86
|
+
isNew: true,
|
|
87
|
+
rating: 4.4,
|
|
88
|
+
specs: ["Объем 20л", "Влагозащита", "Отсек для 16'"],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 8,
|
|
92
|
+
title: "Умная колонка SoundBase",
|
|
93
|
+
description:
|
|
94
|
+
"Мощный звук 360 градусов и встроенный голосовой помощник. Управление умным домом голосом.",
|
|
95
|
+
price: 7900,
|
|
96
|
+
oldPrice: 9500,
|
|
97
|
+
category: "Гаджеты",
|
|
98
|
+
image: "https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=800",
|
|
99
|
+
isNew: false,
|
|
100
|
+
rating: 4.7,
|
|
101
|
+
specs: ["Мощность 30Вт", "Bluetooth / Wi-Fi", "Голосовой ассистент"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 9,
|
|
105
|
+
title: "Планшет Pro Tab 12",
|
|
106
|
+
description:
|
|
107
|
+
"Высокая производительность для творчества и работы. Поддержка стилуса и клавиатуры.",
|
|
108
|
+
price: 64000,
|
|
109
|
+
category: "Электроника",
|
|
110
|
+
image: "https://images.unsplash.com/photo-1544244015-0df4b3ffc6b0?w=800",
|
|
111
|
+
isNew: true,
|
|
112
|
+
rating: 4.8,
|
|
113
|
+
specs: ["Экран 120Гц", "8Гб RAM", "256Гб SSD"],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 10,
|
|
117
|
+
title: "Электросамокат CityRide",
|
|
118
|
+
description:
|
|
119
|
+
"Легкий и складной самокат для города. Запас хода до 35 км на одном заряде.",
|
|
120
|
+
price: 32000,
|
|
121
|
+
oldPrice: 38000,
|
|
122
|
+
category: "Транспорт",
|
|
123
|
+
image: "https://images.unsplash.com/photo-1597075095401-db2205e46be7?w=800",
|
|
124
|
+
isNew: false,
|
|
125
|
+
rating: 4.5,
|
|
126
|
+
specs: ["Скорость 25км/ч", "Запас хода 35км", "Вес 12кг"],
|
|
127
|
+
},
|
|
128
|
+
];`;
|
package/src/kd/admin/admin.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
module.exports = () => `
|
|
2
|
-
from django.contrib import admin
|
|
1
|
+
module.exports = () => `from django.contrib import admin
|
|
3
2
|
from .models import Orders
|
|
4
3
|
|
|
5
4
|
@admin.register(Orders)
|
|
6
5
|
class OrdersAdmin(admin.ModelAdmin):
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
# Поля, которые отображаются в таблице
|
|
7
|
+
list_display = ('id', 'user', 'course', 'date', 'status', 'payment', 'created_at')
|
|
8
|
+
|
|
9
|
+
# Поля, которые можно нажать, чтобы перейти к редактированию
|
|
10
|
+
list_display_links = ('id', 'user')
|
|
11
|
+
|
|
12
|
+
# Боковой фильтр (очень удобен при большом количестве данных)
|
|
13
|
+
list_filter = ('status', 'course', 'payment', 'date')
|
|
14
|
+
|
|
15
|
+
# Поля для поиска (по логину юзера, ФИО или названию курса)
|
|
16
|
+
search_fields = ('user__username', 'user__full_name', 'course', 'review')
|
|
17
|
+
|
|
18
|
+
# Редактирование статуса прямо из списка (не заходя внутрь записи)
|
|
19
|
+
list_editable = ('status',)
|
|
20
|
+
|
|
21
|
+
|
|
10
22
|
`;
|