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 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
- if (modules[moduleArg] && modules[moduleArg][commandArg]) {
50
- modules[moduleArg][commandArg]();
51
- } else {
52
- showHelp();
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.3",
4
- "description": "Генератор шаблонов для Django",
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
- "django",
11
- "template",
12
- "generate"
10
+ "template"
13
11
  ],
14
12
  "author": "asappaholik",
15
13
  "license": "MIT"
package/src/help.js CHANGED
@@ -50,7 +50,6 @@ module.exports = {
50
50
  dj-gen k1 hooks - Хуки (useAuth, useBlog, useMessage)
51
51
  dj-gen k1 utils - Хранилище и обработка запросов
52
52
  dj-gen k1 pages - Все страницы (Auth, Blogs, Search)
53
-
54
53
  `,
55
54
  },
56
55
 
@@ -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 />} />
@@ -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
+ };`;
@@ -1,94 +1,180 @@
1
- module.exports =
2
- () => `import { Button, Navbar, Nav, Container } from "react-bootstrap";
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
- setBlogs(res.blogs);
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
- await deleteBlog(id);
23
- const load = async () => {
24
- const res = await getBlogs();
25
- setBlogs(res.blogs);
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 bg="light" className="mb-3">
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
- МОЙ_САЙТ_123
49
+ <Navbar.Brand as={Link} to="/" className="fw-bold text-uppercase">
50
+ 🚀 MyStore App
35
51
  </Navbar.Brand>
36
- <Nav className="me-auto">
37
- <Nav.Link as={Link} to="/create-blog">
38
- Создать запись
39
- </Nav.Link>
40
- <Nav.Link as={Link} to="/getBlog">
41
- Поиск
42
- </Nav.Link>
43
- </Nav>
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
- <h3>Список всех записей:</h3>
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
- {blogs.map((b) => (
51
- <div
52
- key={b.id}
53
- style={{
54
- }}
55
- >
56
- <h5>{b.title}</h5>
57
- <p>{b.text}</p>
58
- <div style={{ display: "flex", gap: "2px" }}>
59
- <Button
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
- </Button>
81
- <Button
82
- variant="info"
83
- size="sm"
84
- onClick={() => navigate(\`/create-message/\${b.id}\`)}
85
- >
86
- +
87
- </Button>
88
- </div>
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
+ ];`;
@@ -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
- list_display = ('user', 'course', 'date', 'payment', 'status')
8
- list_filter = ('status', 'payment', 'course')
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
  `;