mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-04 04:31:09 +00:00
Добавление главной страницы о общей статистикой
This commit is contained in:
@@ -33,7 +33,7 @@ def get_info(app) -> Dict:
|
|||||||
@router.get("/", include_in_schema=False)
|
@router.get("/", include_in_schema=False)
|
||||||
async def root(request: Request, app=Depends(lambda: get_app())):
|
async def root(request: Request, app=Depends(lambda: get_app())):
|
||||||
"""Эндпоинт главной страницы"""
|
"""Эндпоинт главной страницы"""
|
||||||
return RedirectResponse("/books")
|
return templates.TemplateResponse(request, "index.html", get_info(app))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/books", include_in_schema=False)
|
@router.get("/books", include_in_schema=False)
|
||||||
@@ -85,7 +85,7 @@ async def api_info(app=Depends(lambda: get_app())):
|
|||||||
description="Возвращает статистическую информацию о системе",
|
description="Возвращает статистическую информацию о системе",
|
||||||
)
|
)
|
||||||
async def api_stats(session: Session = Depends(get_session)):
|
async def api_stats(session: Session = Depends(get_session)):
|
||||||
"""Эндпоинт стстистика системы"""
|
"""Эндпоинт стстистики системы"""
|
||||||
authors = select(func.count()).select_from(Author)
|
authors = select(func.count()).select_from(Author)
|
||||||
books = select(func.count()).select_from(Book)
|
books = select(func.count()).select_from(Book)
|
||||||
genres = select(func.count()).select_from(Genre)
|
genres = select(func.count()).select_from(Genre)
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<svg width="800px" height="800px" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg
|
||||||
|
width="800px"
|
||||||
|
height="800px"
|
||||||
|
viewBox="0 0 15 15"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M0.877014 7.49988C0.877014 3.84219 3.84216 0.877045 7.49985 0.877045C11.1575 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1575 14.1227 7.49985 14.1227C3.84216 14.1227 0.877014 11.1575 0.877014 7.49988ZM7.49985 1.82704C4.36683 1.82704 1.82701 4.36686 1.82701 7.49988C1.82701 8.97196 2.38774 10.3131 3.30727 11.3213C4.19074 9.94119 5.73818 9.02499 7.50023 9.02499C9.26206 9.02499 10.8093 9.94097 11.6929 11.3208C12.6121 10.3127 13.1727 8.97172 13.1727 7.49988C13.1727 4.36686 10.6328 1.82704 7.49985 1.82704ZM10.9818 11.9787C10.2839 10.7795 8.9857 9.97499 7.50023 9.97499C6.01458 9.97499 4.71624 10.7797 4.01845 11.9791C4.97952 12.7272 6.18765 13.1727 7.49985 13.1727C8.81227 13.1727 10.0206 12.727 10.9818 11.9787ZM5.14999 6.50487C5.14999 5.207 6.20212 4.15487 7.49999 4.15487C8.79786 4.15487 9.84999 5.207 9.84999 6.50487C9.84999 7.80274 8.79786 8.85487 7.49999 8.85487C6.20212 8.85487 5.14999 7.80274 5.14999 6.50487ZM7.49999 5.10487C6.72679 5.10487 6.09999 5.73167 6.09999 6.50487C6.09999 7.27807 6.72679 7.90487 7.49999 7.90487C8.27319 7.90487 8.89999 7.27807 8.89999 6.50487C8.89999 5.73167 8.27319 5.10487 7.49999 5.10487Z"
|
d="M0.877014 7.49988C0.877014 3.84219 3.84216 0.877045 7.49985 0.877045C11.1575 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1575 14.1227 7.49985 14.1227C3.84216 14.1227 0.877014 11.1575 0.877014 7.49988ZM7.49985 1.82704C4.36683 1.82704 1.82701 4.36686 1.82701 7.49988C1.82701 8.97196 2.38774 10.3131 3.30727 11.3213C4.19074 9.94119 5.73818 9.02499 7.50023 9.02499C9.26206 9.02499 10.8093 9.94097 11.6929 11.3208C12.6121 10.3127 13.1727 8.97172 13.1727 7.49988C13.1727 4.36686 10.6328 1.82704 7.49985 1.82704ZM10.9818 11.9787C10.2839 10.7795 8.9857 9.97499 7.50023 9.97499C6.01458 9.97499 4.71624 10.7797 4.01845 11.9791C4.97952 12.7272 6.18765 13.1727 7.49985 13.1727C8.81227 13.1727 10.0206 12.727 10.9818 11.9787ZM5.14999 6.50487C5.14999 5.207 6.20212 4.15487 7.49999 4.15487C8.79786 4.15487 9.84999 5.207 9.84999 6.50487C9.84999 7.80274 8.79786 8.85487 7.49999 8.85487C6.20212 8.85487 5.14999 7.80274 5.14999 6.50487ZM7.49999 5.10487C6.72679 5.10487 6.09999 5.73167 6.09999 6.50487C6.09999 7.27807 6.72679 7.90487 7.49999 7.90487C8.27319 7.90487 8.89999 7.27807 8.89999 6.50487C8.89999 5.73167 8.27319 5.10487 7.49999 5.10487Z"
|
||||||
fill="#000000"
|
fill="#000000"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,274 @@
|
|||||||
|
const svg = document.getElementById("bookSvg");
|
||||||
|
const NS = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
|
const svgWidth = 200;
|
||||||
|
const svgHeight = 250;
|
||||||
|
const lineCount = 5;
|
||||||
|
const lineDelay = 16;
|
||||||
|
const bookWidth = 120;
|
||||||
|
const bookHeight = 180;
|
||||||
|
const bookX = (svgWidth - bookWidth) / 2;
|
||||||
|
const bookY = (svgHeight - bookHeight) / 2;
|
||||||
|
const desiredLineSpacing = 8;
|
||||||
|
const baseLineWidth = 2;
|
||||||
|
const maxLineWidth = 10;
|
||||||
|
const maxLineHeight = bookHeight - 24;
|
||||||
|
const innerPaddingX = 10;
|
||||||
|
const appearStagger = 8;
|
||||||
|
|
||||||
|
let lineSpacing;
|
||||||
|
if (lineCount > 1) {
|
||||||
|
const maxSpan = Math.max(0, bookWidth - maxLineWidth - 2 * innerPaddingX);
|
||||||
|
const wishSpan = desiredLineSpacing * (lineCount - 1);
|
||||||
|
const realSpan = Math.min(wishSpan, maxSpan);
|
||||||
|
lineSpacing = realSpan / (lineCount - 1);
|
||||||
|
} else {
|
||||||
|
lineSpacing = 0;
|
||||||
|
}
|
||||||
|
const linesSpan = lineSpacing * (lineCount - 1);
|
||||||
|
|
||||||
|
const rightBase = bookX + bookWidth - innerPaddingX - maxLineWidth;
|
||||||
|
const lineStartX = rightBase - linesSpan;
|
||||||
|
|
||||||
|
const leftLimit = bookX + innerPaddingX;
|
||||||
|
|
||||||
|
let phase = 0;
|
||||||
|
let time = 0;
|
||||||
|
|
||||||
|
const baseAppearDuration = 40;
|
||||||
|
const appearDuration = baseAppearDuration + (lineCount - 1) * appearStagger;
|
||||||
|
|
||||||
|
const baseFlipDuration = 120;
|
||||||
|
const flipDuration = baseFlipDuration + (lineCount - 1) * lineDelay;
|
||||||
|
|
||||||
|
const baseDisappearDuration = 40;
|
||||||
|
const disappearDuration =
|
||||||
|
baseDisappearDuration + (lineCount - 1) * appearStagger;
|
||||||
|
|
||||||
|
const pauseDuration = 30;
|
||||||
|
|
||||||
|
const book = document.createElementNS(NS, "rect");
|
||||||
|
book.setAttribute("x", bookX);
|
||||||
|
book.setAttribute("y", bookY);
|
||||||
|
book.setAttribute("width", bookWidth);
|
||||||
|
book.setAttribute("height", bookHeight);
|
||||||
|
book.setAttribute("fill", "#374151");
|
||||||
|
book.setAttribute("rx", "4");
|
||||||
|
svg.appendChild(book);
|
||||||
|
|
||||||
|
const lines = [];
|
||||||
|
for (let i = 0; i < lineCount; i++) {
|
||||||
|
const line = document.createElementNS(NS, "rect");
|
||||||
|
line.setAttribute("fill", "#ffffff");
|
||||||
|
line.setAttribute("rx", "1");
|
||||||
|
svg.appendChild(line);
|
||||||
|
|
||||||
|
const baseX = lineStartX + i * lineSpacing;
|
||||||
|
const targetX = leftLimit + i * lineSpacing;
|
||||||
|
const moveDistance = baseX - targetX;
|
||||||
|
|
||||||
|
lines.push({
|
||||||
|
el: line,
|
||||||
|
baseX,
|
||||||
|
targetX,
|
||||||
|
moveDistance,
|
||||||
|
currentX: baseX,
|
||||||
|
width: baseLineWidth,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function easeInOutQuad(t) {
|
||||||
|
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function easeOutQuad(t) {
|
||||||
|
return 1 - (1 - t) * (1 - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function easeInQuad(t) {
|
||||||
|
return t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLine(line) {
|
||||||
|
const el = line.el;
|
||||||
|
const centerY = bookY + bookHeight / 2;
|
||||||
|
|
||||||
|
el.setAttribute("x", line.currentX);
|
||||||
|
el.setAttribute("y", centerY - line.height / 2);
|
||||||
|
el.setAttribute("width", line.width);
|
||||||
|
el.setAttribute("height", Math.max(0, line.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateBook() {
|
||||||
|
time++;
|
||||||
|
|
||||||
|
if (phase === 0) {
|
||||||
|
for (let i = 0; i < lineCount; i++) {
|
||||||
|
const delay = (lineCount - 1 - i) * appearStagger;
|
||||||
|
const localTime = Math.max(0, time - delay);
|
||||||
|
const progress = Math.min(1, localTime / baseAppearDuration);
|
||||||
|
const easedProgress = easeOutQuad(progress);
|
||||||
|
|
||||||
|
lines[i].height = maxLineHeight * easedProgress;
|
||||||
|
lines[i].currentX = lines[i].baseX;
|
||||||
|
lines[i].width = baseLineWidth;
|
||||||
|
updateLine(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= appearDuration) {
|
||||||
|
phase = 1;
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
|
} else if (phase === 1) {
|
||||||
|
for (let i = 0; i < lineCount; i++) {
|
||||||
|
const delay = i * lineDelay;
|
||||||
|
const localTime = Math.max(0, time - delay);
|
||||||
|
const progress = Math.min(1, localTime / baseFlipDuration);
|
||||||
|
|
||||||
|
const moveProgress = easeInOutQuad(progress);
|
||||||
|
lines[i].currentX = lines[i].baseX - lines[i].moveDistance * moveProgress;
|
||||||
|
|
||||||
|
const widthProgress =
|
||||||
|
progress < 0.5
|
||||||
|
? easeOutQuad(progress * 2)
|
||||||
|
: 1 - easeInQuad((progress - 0.5) * 2);
|
||||||
|
|
||||||
|
lines[i].width =
|
||||||
|
baseLineWidth + (maxLineWidth - baseLineWidth) * widthProgress;
|
||||||
|
|
||||||
|
updateLine(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= flipDuration) {
|
||||||
|
phase = 2;
|
||||||
|
time = 0;
|
||||||
|
}
|
||||||
|
} else if (phase === 2) {
|
||||||
|
for (let i = 0; i < lineCount; i++) {
|
||||||
|
const delay = (lineCount - 1 - i) * appearStagger;
|
||||||
|
const localTime = Math.max(0, time - delay);
|
||||||
|
const progress = Math.min(1, localTime / baseDisappearDuration);
|
||||||
|
const easedProgress = easeInQuad(progress);
|
||||||
|
|
||||||
|
lines[i].height = maxLineHeight * (1 - easedProgress);
|
||||||
|
updateLine(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time >= disappearDuration + pauseDuration) {
|
||||||
|
phase = 0;
|
||||||
|
time = 0;
|
||||||
|
for (let i = 0; i < lineCount; i++) {
|
||||||
|
lines[i].currentX = lines[i].baseX;
|
||||||
|
lines[i].width = baseLineWidth;
|
||||||
|
lines[i].height = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(animateBook);
|
||||||
|
}
|
||||||
|
|
||||||
|
animateBook();
|
||||||
|
|
||||||
|
function animateCounter(element, target, duration = 2000) {
|
||||||
|
const start = 0;
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
function update(currentTime) {
|
||||||
|
const elapsed = currentTime - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
|
||||||
|
const easedProgress = 1 - Math.pow(1 - progress, 3);
|
||||||
|
const current = Math.floor(start + (target - start) * easedProgress);
|
||||||
|
|
||||||
|
element.textContent = current.toLocaleString("ru-RU");
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
} else {
|
||||||
|
element.textContent = target.toLocaleString("ru-RU");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadStats() {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/stats");
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Ошибка загрузки статистики");
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = await response.json();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const booksEl = document.getElementById("stat-books");
|
||||||
|
const authorsEl = document.getElementById("stat-authors");
|
||||||
|
const genresEl = document.getElementById("stat-genres");
|
||||||
|
const usersEl = document.getElementById("stat-users");
|
||||||
|
|
||||||
|
if (booksEl) {
|
||||||
|
animateCounter(booksEl, stats.books, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (authorsEl) {
|
||||||
|
animateCounter(authorsEl, stats.authors, 1500);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (genresEl) {
|
||||||
|
animateCounter(genresEl, stats.genres, 1500);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (usersEl) {
|
||||||
|
animateCounter(usersEl, stats.users, 1500);
|
||||||
|
}
|
||||||
|
}, 450);
|
||||||
|
}, 500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Ошибка загрузки статистики:", error);
|
||||||
|
|
||||||
|
document.getElementById("stat-books").textContent = "—";
|
||||||
|
document.getElementById("stat-authors").textContent = "—";
|
||||||
|
document.getElementById("stat-genres").textContent = "—";
|
||||||
|
document.getElementById("stat-users").textContent = "—";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function observeStatCards() {
|
||||||
|
const cards = document.querySelectorAll(".stat-card");
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry, index) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setTimeout(() => {
|
||||||
|
entry.target.classList.add("animate-fade-in");
|
||||||
|
entry.target.style.opacity = "1";
|
||||||
|
entry.target.style.transform = "translateY(0)";
|
||||||
|
}, index * 100);
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ threshold: 0.1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
cards.forEach((card) => {
|
||||||
|
card.style.opacity = "0";
|
||||||
|
card.style.transform = "translateY(20px)";
|
||||||
|
card.style.transition = "opacity 0.5s ease, transform 0.5s ease";
|
||||||
|
observer.observe(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
loadStats();
|
||||||
|
observeStatCards();
|
||||||
|
});
|
||||||
@@ -19,7 +19,6 @@ nav ul li a {
|
|||||||
font-size: large;
|
font-size: large;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom checkbox styles */
|
|
||||||
.custom-checkbox {
|
.custom-checkbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -40,7 +39,7 @@ nav ul li a {
|
|||||||
height: 18px;
|
height: 18px;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 2px solid #d1d5db; /* gray-300 */
|
border: 2px solid #d1d5db;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -48,11 +47,11 @@ nav ul li a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.custom-checkbox:hover input ~ .checkmark {
|
.custom-checkbox:hover input ~ .checkmark {
|
||||||
border-color: #6b7280; /* gray-500 */
|
border-color: #6b7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-checkbox input:checked ~ .checkmark {
|
.custom-checkbox input:checked ~ .checkmark {
|
||||||
background-color: #6b7280; /* gray-500 */
|
background-color: #6b7280;
|
||||||
border-color: #6b7280;
|
border-color: #6b7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +113,29 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes shake {
|
@keyframes shake {
|
||||||
0%, 100% { transform: translateX(0); }
|
0%,
|
||||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
|
100% {
|
||||||
20%, 40%, 60%, 80% { transform: translateX(5px); }
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
10%,
|
||||||
|
30%,
|
||||||
|
50%,
|
||||||
|
70%,
|
||||||
|
90% {
|
||||||
|
transform: translateX(-5px);
|
||||||
|
}
|
||||||
|
20%,
|
||||||
|
40%,
|
||||||
|
60%,
|
||||||
|
80% {
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#req-length, #req-upper, #req-lower, #req-digit {
|
#req-length,
|
||||||
|
#req-upper,
|
||||||
|
#req-lower,
|
||||||
|
#req-digit {
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +161,8 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-tab, #register-tab {
|
#login-tab,
|
||||||
|
#register-tab {
|
||||||
font-family: "Dited", sans-serif;
|
font-family: "Dited", sans-serif;
|
||||||
letter-spacing: 1.5px;
|
letter-spacing: 1.5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -156,10 +173,73 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes dropdownFade {
|
@keyframes dropdownFade {
|
||||||
from { opacity: 0; transform: translateY(-5px); }
|
from {
|
||||||
to { opacity: 1; transform: translateY(0); }
|
opacity: 0;
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#user-arrow.rotate-180 {
|
#user-arrow.rotate-180 {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-soft {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse-soft {
|
||||||
|
animation: pulse-soft 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bookSvg {
|
||||||
|
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-up {
|
||||||
|
animation: fadeInUp 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover svg {
|
||||||
|
transform: scale(1.1);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card svg {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-text {
|
||||||
|
background: linear-gradient(135deg, #374151 0%, #6b7280 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
<!-- templates/auth.html -->
|
{% extends "base.html" %} {% block title %}LiB - Авторизация{% endblock %} {%
|
||||||
{% extends "base.html" %}
|
block content %}
|
||||||
|
|
||||||
{% block title %}LiB - Авторизация{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="flex flex-1 items-center justify-center p-4">
|
<div class="flex flex-1 items-center justify-center p-4">
|
||||||
<div class="w-full max-w-md">
|
<div class="w-full max-w-md">
|
||||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
<div class="flex border-b border-gray-200">
|
<div class="flex border-b border-gray-200">
|
||||||
<button type="button" id="login-tab" class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-700 bg-gray-50 border-b-2 border-gray-500">Вход</button>
|
<button
|
||||||
<button type="button" id="register-tab" class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-400 hover:text-gray-600">Регистрация</button>
|
type="button"
|
||||||
|
id="login-tab"
|
||||||
|
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-700 bg-gray-50 border-b-2 border-gray-500"
|
||||||
|
>
|
||||||
|
Вход
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="register-tab"
|
||||||
|
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-400 hover:text-gray-600"
|
||||||
|
>
|
||||||
|
Регистрация
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="login-form" class="p-6">
|
<form id="login-form" class="p-6">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="login-username" class="block text-sm font-medium text-gray-700 mb-2">Имя пользователя</label>
|
<label
|
||||||
|
for="login-username"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Имя пользователя</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="login-username"
|
id="login-username"
|
||||||
@@ -24,9 +36,12 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="login-password" class="block text-sm font-medium text-gray-700 mb-2">Пароль</label>
|
<label
|
||||||
|
for="login-password"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Пароль</label
|
||||||
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -41,37 +56,77 @@
|
|||||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||||
onclick="togglePassword(this)"
|
onclick="togglePassword(this)"
|
||||||
>
|
>
|
||||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
class="eye-open w-5 h-5"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
|
class="eye-closed w-5 h-5 hidden"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
<label class="custom-checkbox flex items-center text-sm text-gray-600">
|
<label
|
||||||
|
class="custom-checkbox flex items-center text-sm text-gray-600"
|
||||||
|
>
|
||||||
<input type="checkbox" id="remember-me" />
|
<input type="checkbox" id="remember-me" />
|
||||||
<span class="checkmark"></span>Запомнить меня
|
<span class="checkmark"></span>Запомнить меня
|
||||||
</label>
|
</label>
|
||||||
<a href="#" class="text-sm text-gray-500 hover:text-gray-700 transition">Забыли пароль?</a>
|
<a
|
||||||
|
href="#"
|
||||||
|
class="text-sm text-gray-500 hover:text-gray-700 transition"
|
||||||
|
>Забыли пароль?</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
<div id="login-error" class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"></div>
|
id="login-error"
|
||||||
|
class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"
|
||||||
|
></div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
id="login-submit"
|
id="login-submit"
|
||||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium"
|
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium"
|
||||||
>Войти</button>
|
>
|
||||||
|
Войти
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<form
|
||||||
<form id="register-form" class="p-6 hidden" onsubmit="return handleRegister(event)">
|
id="register-form"
|
||||||
|
class="p-6 hidden"
|
||||||
|
onsubmit="return handleRegister(event);"
|
||||||
|
>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="register-username" class="block text-sm font-medium text-gray-700 mb-2">Имя пользователя</label>
|
<label
|
||||||
|
for="register-username"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Имя пользователя</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="register-username"
|
id="register-username"
|
||||||
@@ -83,9 +138,12 @@
|
|||||||
maxlength="50"
|
maxlength="50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="register-email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
|
<label
|
||||||
|
for="register-email"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Email</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="register-email"
|
id="register-email"
|
||||||
@@ -95,9 +153,15 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="register-fullname" class="block text-sm font-medium text-gray-700 mb-2">Полное имя <span class="text-gray-400">(необязательно)</span></label>
|
<label
|
||||||
|
for="register-fullname"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Полное имя
|
||||||
|
<span class="text-gray-400"
|
||||||
|
>(необязательно)</span
|
||||||
|
></label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="register-fullname"
|
id="register-fullname"
|
||||||
@@ -107,9 +171,12 @@
|
|||||||
maxlength="100"
|
maxlength="100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="register-password" class="block text-sm font-medium text-gray-700 mb-2">Пароль</label>
|
<label
|
||||||
|
for="register-password"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Пароль</label
|
||||||
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -126,25 +193,61 @@
|
|||||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||||
onclick="togglePassword(this)"
|
onclick="togglePassword(this)"
|
||||||
>
|
>
|
||||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
class="eye-open w-5 h-5"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
|
class="eye-closed w-5 h-5 hidden"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<div class="h-1 w-full bg-gray-200 rounded-full overflow-hidden">
|
<div
|
||||||
<div id="password-strength-bar" class="h-full w-0 transition-all duration-300"></div>
|
class="h-1 w-full bg-gray-200 rounded-full overflow-hidden"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="password-strength-bar"
|
||||||
|
class="h-full w-0 transition-all duration-300"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<p id="password-strength-text" class="text-xs mt-1 text-gray-500"></p>
|
<p
|
||||||
|
id="password-strength-text"
|
||||||
|
class="text-xs mt-1 text-gray-500"
|
||||||
|
></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="register-password-confirm" class="block text-sm font-medium text-gray-700 mb-2">Подтвердите пароль</label>
|
<label
|
||||||
|
for="register-password-confirm"
|
||||||
|
class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Подтвердите пароль</label
|
||||||
|
>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -159,22 +262,55 @@
|
|||||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||||
onclick="togglePassword(this)"
|
onclick="togglePassword(this)"
|
||||||
>
|
>
|
||||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
class="eye-open w-5 h-5"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
|
class="eye-closed w-5 h-5 hidden"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||||
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p id="password-match-error" class="text-xs mt-1 text-red-500 hidden">Пароли не совпадают</p>
|
<p
|
||||||
|
id="password-match-error"
|
||||||
|
class="text-xs mt-1 text-red-500 hidden"
|
||||||
|
>
|
||||||
|
Пароли не совпадают
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
<div id="register-error" class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"></div>
|
id="register-error"
|
||||||
|
class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"
|
||||||
<div id="register-success" class="hidden mb-4 p-3 bg-green-100 border border-green-300 text-green-700 rounded-lg text-sm"></div>
|
></div>
|
||||||
|
<div
|
||||||
|
id="register-success"
|
||||||
|
class="hidden mb-4 p-3 bg-green-100 border border-green-300 text-green-700 rounded-lg text-sm"
|
||||||
|
></div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
id="register-submit"
|
id="register-submit"
|
||||||
@@ -186,8 +322,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %} {% block scripts %}
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
<script type="text/javascript" src="/static/auth.js"></script>
|
<script type="text/javascript" src="/static/auth.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -5,7 +5,6 @@ content %}
|
|||||||
class="w-1/4 bg-white p-4 rounded-lg shadow-md mr-4 h-fit resize-x overflow-auto min-w-64 max-w-96"
|
class="w-1/4 bg-white p-4 rounded-lg shadow-md mr-4 h-fit resize-x overflow-auto min-w-64 max-w-96"
|
||||||
>
|
>
|
||||||
<h2 class="text-xl font-semibold mb-4">Поиск</h2>
|
<h2 class="text-xl font-semibold mb-4">Поиск</h2>
|
||||||
|
|
||||||
<div class="relative mb-4">
|
<div class="relative mb-4">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -29,9 +28,7 @@ content %}
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold mb-4">Фильтры</h2>
|
<h2 class="text-xl font-semibold mb-4">Фильтры</h2>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h3 class="font-medium mb-2">Авторы</h3>
|
<h3 class="font-medium mb-2">Авторы</h3>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -52,12 +49,10 @@ content %}
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<h3 class="font-medium mb-2">Жанры</h3>
|
<h3 class="font-medium mb-2">Жанры</h3>
|
||||||
<ul id="genres-list" class="max-h-60 overflow-y-auto"></ul>
|
<ul id="genres-list" class="max-h-60 overflow-y-auto"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="apply-filters-btn"
|
id="apply-filters-btn"
|
||||||
class="w-full bg-gray-500 text-white py-2 px-4 rounded-lg hover:bg-gray-600 transition duration-200 mb-2"
|
class="w-full bg-gray-500 text-white py-2 px-4 rounded-lg hover:bg-gray-600 transition duration-200 mb-2"
|
||||||
@@ -70,19 +65,13 @@ content %}
|
|||||||
>
|
>
|
||||||
Сбросить фильтры
|
Сбросить фильтры
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Счётчик результатов -->
|
|
||||||
<div
|
<div
|
||||||
id="results-counter"
|
id="results-counter"
|
||||||
class="mt-4 text-center text-sm text-gray-500"
|
class="mt-4 text-center text-sm text-gray-500"
|
||||||
></div>
|
></div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="flex-1">
|
<main class="flex-1">
|
||||||
<div id="books-container">
|
<div id="books-container"></div>
|
||||||
<!-- Книги будут загружены через JS -->
|
|
||||||
</div>
|
|
||||||
<!-- Пагинация добавляется динамически после books-container -->
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %} {% block scripts %}
|
{% endblock %} {% block scripts %}
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
{% extends "base.html" %} {% block title %}LiB - Библиотека{% endblock %} {%
|
||||||
|
block content %}
|
||||||
|
<div class="flex flex-1 items-center justify-center p-4">
|
||||||
|
<div class="w-full max-w-4xl">
|
||||||
|
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
<div class="text-center py-8 border-b border-gray-200">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mb-2">
|
||||||
|
Добро пожаловать в LiB
|
||||||
|
</h2>
|
||||||
|
<p class="text-gray-500">Ваша персональная библиотека книг</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-8">
|
||||||
|
<div
|
||||||
|
class="flex flex-col lg:flex-row items-center justify-center gap-12"
|
||||||
|
>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<svg
|
||||||
|
id="bookSvg"
|
||||||
|
width="400"
|
||||||
|
height="500"
|
||||||
|
viewBox="0 0 200 250"
|
||||||
|
class="drop-shadow-lg"
|
||||||
|
></svg>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 gap-6">
|
||||||
|
<div
|
||||||
|
class="stat-card bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 text-center transform transition-all duration-300 hover:scale-105 hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="mb-3">
|
||||||
|
<svg
|
||||||
|
class="w-10 h-10 mx-auto text-gray-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="stat-books"
|
||||||
|
class="text-4xl font-bold text-gray-800 mb-1 stat-number"
|
||||||
|
data-target="0"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-500 font-medium">
|
||||||
|
Книг
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="stat-card bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 text-center transform transition-all duration-300 hover:scale-105 hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="mb-3">
|
||||||
|
<svg
|
||||||
|
class="w-10 h-10 mx-auto text-gray-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="stat-authors"
|
||||||
|
class="text-4xl font-bold text-gray-800 mb-1 stat-number"
|
||||||
|
data-target="0"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-500 font-medium">
|
||||||
|
Авторов
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="stat-card bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 text-center transform transition-all duration-300 hover:scale-105 hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="mb-3">
|
||||||
|
<svg
|
||||||
|
class="w-10 h-10 mx-auto text-gray-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="stat-genres"
|
||||||
|
class="text-4xl font-bold text-gray-800 mb-1 stat-number"
|
||||||
|
data-target="0"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-500 font-medium">
|
||||||
|
Жанров
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="stat-card bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-6 text-center transform transition-all duration-300 hover:scale-105 hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="mb-3">
|
||||||
|
<svg
|
||||||
|
class="w-10 h-10 mx-auto text-gray-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="stat-users"
|
||||||
|
class="text-4xl font-bold text-gray-800 mb-1 stat-number"
|
||||||
|
data-target="0"
|
||||||
|
>
|
||||||
|
0
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-500 font-medium">
|
||||||
|
Пользователей
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-8 pb-8">
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<a
|
||||||
|
href="/books"
|
||||||
|
class="inline-flex items-center justify-center px-6 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition duration-200 font-medium shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Смотреть книги
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/authors"
|
||||||
|
class="inline-flex items-center justify-center px-6 py-3 bg-white text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition duration-200 font-medium shadow-sm hover:shadow-md transform hover:-translate-y-0.5"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Все авторы
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 text-center text-gray-400 text-sm">
|
||||||
|
<p>LiB — Библиотека. Создано с ❤️</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %} {% block scripts %}
|
||||||
|
<script type="text/javascript" src="/static/index.js"></script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user