Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f52ebb496 | |||
| 5408021e1b | |||
| e9d0a1d549 | |||
| 3293c8b1b9 | |||
| e527a79d0c | |||
| 616e8e76c9 | |||
| 67fdf675f4 | |||
| 6bbec3f25f | |||
| d540511a47 | |||
| 7b5012a6e8 | |||
| 78fa452d28 | |||
| f8270722cc |
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
File diff suppressed because one or more lines are too long
@@ -12,4 +12,4 @@
|
||||
<div id="root" class="bg-dark"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
101
Lab/src/App.jsx
101
Lab/src/App.jsx
@@ -1,8 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import Navbar from "./components/Navbar";
|
||||
import Footer from "./components/Footer";
|
||||
import BookForm from "./components/BookForm";
|
||||
import BookList from "./components/BookList";
|
||||
import Home from "./components/Home";
|
||||
import News from "./components/News";
|
||||
import Manga from "./components/Manga";
|
||||
import Author from "./components/Author";
|
||||
import Reading from "./components/Reading";
|
||||
import Account from "./components/Account";
|
||||
import data from "../data.json"; // Используем только для авторов и статусов
|
||||
|
||||
function App() {
|
||||
@@ -10,8 +15,8 @@ function App() {
|
||||
const [authors] = useState(data.authors);
|
||||
const [statuses] = useState(data.statuses);
|
||||
const [editingBook, setEditingBook] = useState(null);
|
||||
const [statusFilter, setStatusFilter] = useState(""); // пусто — все книги
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("");
|
||||
|
||||
// Загрузка книг с сервера
|
||||
useEffect(() => {
|
||||
@@ -43,6 +48,20 @@ function App() {
|
||||
});
|
||||
};
|
||||
|
||||
// Массовое удаление
|
||||
const deleteSelectedBooks = (ids) => {
|
||||
Promise.all(
|
||||
ids.map((id) =>
|
||||
fetch(`http://localhost:5174/books/${id}`, { method: "DELETE" })
|
||||
)
|
||||
).then(() => {
|
||||
fetch("http://localhost:5174/books")
|
||||
.then((res) => res.json())
|
||||
.then((data) => setBooks(data));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Обновить книгу
|
||||
const updateBook = (book) => {
|
||||
fetch(`http://localhost:5174/books/${book.id}`, {
|
||||
@@ -56,53 +75,49 @@ function App() {
|
||||
});
|
||||
};
|
||||
|
||||
// Фильтрация книг по статусу
|
||||
const filteredBooks = statusFilter
|
||||
? books.filter(
|
||||
book =>
|
||||
statuses.find(s => String(s.id) === String(book.statusId))?.name === statusFilter
|
||||
)
|
||||
: books;
|
||||
// Фильтрация книг по поиску и статусу
|
||||
const filteredBooks = books.filter(book =>
|
||||
book.title.toLowerCase().includes(searchQuery.toLowerCase()) &&
|
||||
(statusFilter === "" ||
|
||||
statuses.find(s => String(s.id) === String(book.statusId))?.name === statusFilter)
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main className="container py-4">
|
||||
<h1 className="mb-4">Книги</h1>
|
||||
{/* --- Фильтр по статусу --- */}
|
||||
<div className="mb-3" style={{ maxWidth: 350 }}>
|
||||
<select
|
||||
className="form-select"
|
||||
value={statusFilter}
|
||||
onChange={e => setStatusFilter(e.target.value)}
|
||||
>
|
||||
<option value="">Все статусы</option>
|
||||
{statuses.map(s => (
|
||||
<option key={s.id} value={s.name}>{s.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{/* --- Форма добавления/редактирования --- */}
|
||||
<BookForm
|
||||
authors={authors}
|
||||
statuses={statuses}
|
||||
onSubmit={editingBook ? updateBook : addBook}
|
||||
editingBook={editingBook}
|
||||
onCancel={() => setEditingBook(null)}
|
||||
/>
|
||||
{/* --- Список книг (фильтрованные) --- */}
|
||||
<BookList
|
||||
books={filteredBooks}
|
||||
authors={authors}
|
||||
statuses={statuses}
|
||||
onEdit={setEditingBook}
|
||||
onDelete={deleteBook}
|
||||
/>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<Home
|
||||
books={filteredBooks}
|
||||
authors={authors}
|
||||
statuses={statuses}
|
||||
editingBook={editingBook}
|
||||
onAdd={addBook}
|
||||
onEdit={updateBook}
|
||||
onDelete={deleteBook}
|
||||
onCancel={() => setEditingBook(null)}
|
||||
setEditingBook={setEditingBook}
|
||||
searchQuery={searchQuery}
|
||||
setSearchQuery={setSearchQuery}
|
||||
statusFilter={statusFilter}
|
||||
setStatusFilter={setStatusFilter}
|
||||
onDeleteSelected={deleteSelectedBooks}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/news" element={<News />} />
|
||||
<Route path="/manga/:id" element={<Manga />} />
|
||||
<Route path="/author/:id" element={<Author />} />
|
||||
<Route path="/reading/:id" element={<Reading />} />
|
||||
<Route path="/account" element={<Account />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
70
Lab/src/components/Account.jsx
Normal file
70
Lab/src/components/Account.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default function Account() {
|
||||
const [phone, setPhone] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
setSubmitted(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container my-5">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-md-6 bg-secondary p-4 rounded bg-custom-dark">
|
||||
<form id="loginForm" onSubmit={handleSubmit}>
|
||||
<h2 className="text-center mb-4">
|
||||
<i className="bi bi-person-circle me-2"></i>Вход в систему
|
||||
</h2>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="number" className="form-label">
|
||||
<i className="bi bi-telephone-fill me-2"></i>Номер телефона
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
className="form-control"
|
||||
id="number"
|
||||
name="number_acc"
|
||||
placeholder="+7 (___) ___-__-__"
|
||||
required
|
||||
value={phone}
|
||||
onChange={e => setPhone(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="password" className="form-label">
|
||||
<i className="bi bi-lock-fill me-2"></i>Пароль
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
name="password_acc"
|
||||
placeholder="Пароль"
|
||||
required
|
||||
minLength="4"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="d-grid">
|
||||
<button type="submit" className="btn btn-warning">
|
||||
<i className="bi bi-box-arrow-in-right me-2"></i>Войти
|
||||
</button>
|
||||
</div>
|
||||
{submitted && (
|
||||
<div className="mt-3 alert alert-info p-2">
|
||||
<i className="bi bi-info-circle me-2"></i>Проверка формы: пока без логики!
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
Lab/src/components/Author.jsx
Normal file
24
Lab/src/components/Author.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Author() {
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
<div className="row">
|
||||
<div className="col-md-4 text-center">
|
||||
<img
|
||||
className="img-fluid rounded"
|
||||
src="../img/ХаяоМиядзаки.png" // Используй нужную тебе картинку!
|
||||
alt="Суи Исида"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-8 text-color-light">
|
||||
<h3>Автор: Фусэ </h3>
|
||||
<p>
|
||||
Известен произведением «О моём перерождении в слизь».
|
||||
В нём автор объединяет элементы западных и восточных ролевых игр и использует идею жанра «исекай» о перерождении или призыве в другой мир.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +1,64 @@
|
||||
import React from "react";
|
||||
import StatusLabel from "./StatusLabel";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function BookCard({ book, authorName, statusName, onEdit, onDelete }) {
|
||||
function BookCard({
|
||||
book,
|
||||
authorName,
|
||||
statusName,
|
||||
onEdit,
|
||||
onDelete,
|
||||
selected,
|
||||
onSelect
|
||||
}) {
|
||||
return (
|
||||
<div className="card mb-3 w-100" style={{ maxWidth: "450px", margin: "0 auto" }}>
|
||||
<img
|
||||
src={book.cover || "https://placehold.co/200x300"}
|
||||
className="card-img-top shadow mt-2 mb-3"
|
||||
alt="Обложка книги"
|
||||
style={{ height: "300px", objectFit: "contain" }}
|
||||
<div
|
||||
className="card card-hover mb-3 w-100"
|
||||
style={{ maxWidth: "450px", margin: "0 auto", position: "relative" }}
|
||||
>
|
||||
{/* Чекбокс для выделения */}
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selected}
|
||||
onChange={e => onSelect(book.id, e.target.checked)}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 10,
|
||||
left: 10,
|
||||
zIndex: 10,
|
||||
width: "20px",
|
||||
height: "20px"
|
||||
}}
|
||||
title="Выделить для массового удаления"
|
||||
/>
|
||||
|
||||
<Link to={`/manga/${book.id}`}>
|
||||
<img
|
||||
src={book.cover || "https://placehold.co/200x300"}
|
||||
className="card-img-top shadow mt-2 mb-3"
|
||||
alt="Обложка книги"
|
||||
style={{ maxHeight: "300px", objectFit: "contain" }}
|
||||
/>
|
||||
</Link>
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">{book.title}</h5>
|
||||
<p className="card-text">{book.description}</p>
|
||||
<p className="card-text"><small>Автор: {authorName}</small></p>
|
||||
<p className="card-text"><StatusLabel statusName={statusName} /></p>
|
||||
<button className="btn btn-warning me-2" onClick={() => onEdit(book)}>
|
||||
<p className="card-text">
|
||||
<small>Автор: {authorName}</small>
|
||||
</p>
|
||||
<p className="card-text">
|
||||
<StatusLabel statusName={statusName} />
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-warning me-2"
|
||||
onClick={() => onEdit(book)}
|
||||
>
|
||||
Редактировать
|
||||
</button>
|
||||
<button className="btn btn-danger" onClick={() => onDelete(book.id)}>
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
onClick={() => onDelete(book.id)}
|
||||
>
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import React from "react";
|
||||
import BookCard from "./BookCard";
|
||||
|
||||
function BookList({ books, authors, statuses, onEdit, onDelete }) {
|
||||
const getAuthorName = (id) => authors.find(a => String(a.id) === String(id))?.name || "Неизвестен";
|
||||
const getStatusName = (id) => statuses.find(s => String(s.id) === String(id))?.name || "Нет статуса";
|
||||
function BookList({
|
||||
books,
|
||||
authors,
|
||||
statuses,
|
||||
onEdit,
|
||||
onDelete,
|
||||
selectedIds,
|
||||
onSelect
|
||||
}) {
|
||||
const getAuthorName = (id) =>
|
||||
authors.find((a) => String(a.id) === String(id))?.name || "Неизвестен";
|
||||
const getStatusName = (id) =>
|
||||
statuses.find((s) => String(s.id) === String(id))?.name || "Нет статуса";
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center ">
|
||||
{books.map(book => (
|
||||
{books.map((book) => (
|
||||
<div
|
||||
className="col-12 col-sm-10 col-md-6 col-lg-4 d-flex align-items-stretch mb-4"
|
||||
key={book.id}
|
||||
@@ -18,6 +28,8 @@ function BookList({ books, authors, statuses, onEdit, onDelete }) {
|
||||
statusName={getStatusName(book.statusId)}
|
||||
onEdit={onEdit}
|
||||
onDelete={onDelete}
|
||||
selected={selectedIds.includes(book.id)}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
94
Lab/src/components/Home.jsx
Normal file
94
Lab/src/components/Home.jsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React, { useState } from "react";
|
||||
import BookForm from "./BookForm";
|
||||
import BookList from "./BookList";
|
||||
|
||||
export default function Home({
|
||||
books,
|
||||
authors,
|
||||
statuses,
|
||||
editingBook,
|
||||
onAdd,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onCancel,
|
||||
setEditingBook,
|
||||
searchQuery,
|
||||
setSearchQuery,
|
||||
statusFilter,
|
||||
setStatusFilter,
|
||||
onDeleteSelected // новый пропс!
|
||||
}) {
|
||||
// Состояние для выделенных книг
|
||||
const [selectedIds, setSelectedIds] = useState([]);
|
||||
|
||||
// Обработчик выделения карточки
|
||||
const handleSelect = (id, checked) => {
|
||||
setSelectedIds(prev =>
|
||||
checked ? [...prev, id] : prev.filter(selectedId => selectedId !== id)
|
||||
);
|
||||
};
|
||||
|
||||
// Кнопка массового удаления
|
||||
const handleDeleteSelected = () => {
|
||||
if (
|
||||
selectedIds.length &&
|
||||
window.confirm(`Удалить ${selectedIds.length} выделенных книг?`)
|
||||
) {
|
||||
onDeleteSelected(selectedIds);
|
||||
setSelectedIds([]); // очистка выделения
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="mb-4">Книги</h1>
|
||||
<div className="mb-3" style={{ maxWidth: 350 }}>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-2"
|
||||
placeholder="Поиск по названию"
|
||||
value={searchQuery}
|
||||
onChange={e => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
<select
|
||||
className="form-select"
|
||||
value={statusFilter}
|
||||
onChange={e => setStatusFilter(e.target.value)}
|
||||
>
|
||||
<option value="">Все статусы</option>
|
||||
{statuses.map(s => (
|
||||
<option key={s.id} value={s.name}>{s.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Кнопка массового удаления */}
|
||||
<div className="mb-2">
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
disabled={selectedIds.length === 0}
|
||||
onClick={handleDeleteSelected}
|
||||
>
|
||||
Удалить выделенные ({selectedIds.length})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<BookForm
|
||||
authors={authors}
|
||||
statuses={statuses}
|
||||
onSubmit={editingBook ? onEdit : onAdd}
|
||||
editingBook={editingBook}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
<BookList
|
||||
books={books}
|
||||
authors={authors}
|
||||
statuses={statuses}
|
||||
onEdit={setEditingBook}
|
||||
onDelete={onDelete}
|
||||
selectedIds={selectedIds}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
37
Lab/src/components/Manga.jsx
Normal file
37
Lab/src/components/Manga.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function Manga() {
|
||||
return (
|
||||
<main className="container my-5 flex-grow-1">
|
||||
<div className="row justify-content-center">
|
||||
<div className="col-md-8 text-center">
|
||||
<figure className="figure">
|
||||
<img
|
||||
src="/img/заглушка.jpg"
|
||||
className="figure-img img-fluid rounded"
|
||||
alt="Токийский Гуль"
|
||||
style={{ maxHeight: 350, objectFit: "contain" }}
|
||||
/>
|
||||
<figcaption className="figure-caption text-light">
|
||||
О моём перерождении в слизь
|
||||
</figcaption>
|
||||
</figure>
|
||||
<p className="mt-4 text-color-light">
|
||||
Обычный служащий финансовой компании Сатору Миками погибает, защищая коллегу от грабителя с ножом.
|
||||
После смерти Сатору попадает в фэнтезийный мир, в котором он предстаёт в виде комка слизи средних размеров по имени Римуру, наделённой немалым разумом.
|
||||
Отныне Римуру будет жить в мире, полном разных рас, в надежде построить однажды страну, где к каждой расе будут относиться одинаково.
|
||||
</p>
|
||||
<div className="d-flex justify-content-center gap-3 mt-3">
|
||||
<Link to="/author/1" className="btn btn-primary">
|
||||
<i className="bi bi-person-lines-fill me-2"></i>Про автора
|
||||
</Link>
|
||||
<Link to="/reading/1" className="btn btn-success">
|
||||
<i className="bi bi-book me-2"></i>Читать
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export default function Navbar() {
|
||||
<Link className="navbar-brand" to="/">
|
||||
<img src="/img/manga.png" alt="ЛОГО" height="50" />
|
||||
</Link>
|
||||
{/* Бургер-кнопка для мобилок */}
|
||||
<button
|
||||
className="navbar-toggler"
|
||||
type="button"
|
||||
@@ -20,11 +21,18 @@ export default function Navbar() {
|
||||
</button>
|
||||
|
||||
<div className="collapse navbar-collapse ms-3" id="navbarNavDropdown">
|
||||
|
||||
|
||||
<Link to="/account" className="btn btn-outline-warning">
|
||||
Вход
|
||||
</Link>
|
||||
<ul className="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||
<li className="nav-item me-2">
|
||||
<Link to="/news" className="btn btn-outline-warning">
|
||||
Новости
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link to="/account" className="btn btn-outline-warning">
|
||||
Вход
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
|
||||
14
Lab/src/components/News.jsx
Normal file
14
Lab/src/components/News.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
export default function News() {
|
||||
return (
|
||||
<div className="container-news mt-4">
|
||||
<div className="card">
|
||||
<img src="/img/новость.jpg" className="card-img-top" alt="Новость" />
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Новость</h5>
|
||||
<p className="card-text">Lorem ipsum dolor sit amet...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
33
Lab/src/components/Reading.jsx
Normal file
33
Lab/src/components/Reading.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Reading() {
|
||||
|
||||
|
||||
// Пример массив страниц — можешь заменить на свой массив или получать из API:
|
||||
const pages = [
|
||||
"/img/SL1.png",
|
||||
"/img/SL2.png",
|
||||
// Можно добавить другие страницы
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="container mt-5 flex-grow-1">
|
||||
<div className="row">
|
||||
<div className="col-12 text-center">
|
||||
<h3>Читать мангу...</h3>
|
||||
<div className="d-flex justify-content-center">
|
||||
{pages.map((src, idx) => (
|
||||
<img
|
||||
className="img-reading mx-2"
|
||||
src={src}
|
||||
alt={`Манга страница ${idx + 1}`}
|
||||
key={src}
|
||||
style={{ maxWidth: 350, maxHeight: 500 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,12 @@
|
||||
.card.card-hover {
|
||||
transition: transform 0.2s cubic-bezier(.4,2,.3,1);
|
||||
}
|
||||
|
||||
.card.card-hover:hover {
|
||||
transform: scale(1.04);
|
||||
z-index: 2;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.bg-custom-dark {
|
||||
background-color: #0f0630 !important;
|
||||
@@ -49,4 +58,8 @@ footer {
|
||||
box-sizing: border-box;
|
||||
padding: 8px 12px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.text-color-light {
|
||||
color: aliceblue;
|
||||
}
|
||||
5672
package-lock.json
generated
5672
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "int-prog",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "npm-run-all --parallel backend vite",
|
||||
"vite": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "http-server -p 3000 ./html/",
|
||||
"backend": "json-server ./html/database/data.json -p 5174",
|
||||
"prod": "npm-run-all build serve --parallel backend serve",
|
||||
"lint": "eslint . --ext js --report-unused-disable-directives --max-warnings 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"inputmask": "^5.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.0.2",
|
||||
"eslint-plugin-html": "8.1.2",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.3",
|
||||
"http-server": "14.1.1",
|
||||
"json-server": "^1.0.0-beta.3",
|
||||
"npm-run-all": "4.1.5",
|
||||
"vite": "6.2.0"
|
||||
}
|
||||
}
|
||||
BIN
Отчет №6.docx
Normal file
BIN
Отчет №6.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user