first commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shop
2026-05-16 20:52:15 +03:00
parent 3419d90e61
commit 323e0a2926
67 changed files with 1723 additions and 3077 deletions
+15
View File
@@ -0,0 +1,15 @@
<%- include('partials/layout-start') %>
<div class="account">
<h1>Личный кабинет</h1>
<div class="card account-card">
<p><strong><%= user.name %></strong></p>
<p class="muted"><%= user.email %></p>
<p class="muted">С нами с <%= new Date(user.created_at).toLocaleDateString('ru-RU') %></p>
<div class="account-actions">
<a href="/orders" class="btn btn--primary">Мои заказы (<%= orderCount %>)</a>
</div>
</div>
</div>
<%- include('partials/layout-end') %>
+54
View File
@@ -0,0 +1,54 @@
<%- include('partials/layout-start') %>
<h1>Корзина</h1>
<% if (error) { %><p class="alert alert--error"><%= error %></p><% } %>
<% if (!items.length) { %>
<p class="empty">Корзина пуста. <a href="/">Перейти в каталог</a></p>
<% } else { %>
<form action="/cart/update" method="post" class="cart-table-wrap">
<table class="cart-table">
<thead>
<tr>
<th>Товар</th>
<th>Цена</th>
<th>Кол-во</th>
<th>Сумма</th>
<th></th>
</tr>
</thead>
<tbody>
<% items.forEach(item => { %>
<tr>
<td class="cart-table__product">
<% if (item.image_url) { %>
<img src="<%= item.image_url %>" alt="" class="cart-table__thumb">
<% } %>
<a href="/product/<%= item.slug %>"><%= item.name %></a>
</td>
<td><%= formatPrice(item.price_cents) %></td>
<td>
<input type="number" name="items[<%= item.id %>]" value="<%= item.quantity %>" min="0" max="<%= item.stock %>" class="input input--qty">
</td>
<td><%= formatPrice(item.line_total) %></td>
<td>
<button type="submit" formaction="/cart/remove/<%= item.id %>" formmethod="post" class="btn btn--ghost btn--sm" title="Удалить">×</button>
</td>
</tr>
<% }) %>
</tbody>
</table>
<div class="cart-actions">
<button type="submit" class="btn btn--ghost">Обновить</button>
<p class="cart-total">Итого: <strong><%= formatPrice(total) %></strong></p>
<% if (user) { %>
<a href="/checkout" class="btn btn--primary btn--lg">Оформить заказ</a>
<% } else { %>
<p class="hint"><a href="/login?next=/checkout">Войдите</a>, чтобы оформить заказ.</p>
<% } %>
</div>
</form>
<% } %>
<%- include('partials/layout-end') %>
+43
View File
@@ -0,0 +1,43 @@
<%- include('partials/layout-start') %>
<h1>Оформление заказа</h1>
<% if (error) { %><p class="alert alert--error"><%= error %></p><% } %>
<div class="checkout-layout">
<form action="/checkout" method="post" class="form card">
<h2>Данные доставки</h2>
<label class="label">
Имя
<input type="text" name="name" class="input" required value="<%= user ? user.name : '' %>">
</label>
<label class="label">
Email
<input type="email" name="email" class="input" required value="<%= user ? user.email : '' %>">
</label>
<label class="label">
Телефон
<input type="tel" name="phone" class="input" placeholder="+7 …">
</label>
<label class="label">
Адрес доставки
<textarea name="address" class="input" rows="3" required placeholder="Город, улица, дом, квартира"></textarea>
</label>
<button type="submit" class="btn btn--primary btn--lg btn--block">Подтвердить заказ</button>
</form>
<aside class="checkout-summary card">
<h2>Ваш заказ</h2>
<ul class="checkout-list">
<% items.forEach(item => { %>
<li>
<span><%= item.name %> × <%= item.quantity %></span>
<span><%= formatPrice(item.line_total) %></span>
</li>
<% }) %>
</ul>
<p class="checkout-total">Итого: <strong><%= formatPrice(total) %></strong></p>
</aside>
</div>
<%- include('partials/layout-end') %>
+9
View File
@@ -0,0 +1,9 @@
<%- include('partials/layout-start') %>
<div class="error-page">
<h1><%= code %></h1>
<p><%= message %></p>
<a href="/" class="btn btn--primary">На главную</a>
</div>
<%- include('partials/layout-end') %>
+47
View File
@@ -0,0 +1,47 @@
<%- include('partials/layout-start') %>
<section class="hero">
<h1>Каталог товаров</h1>
<p>Доставка по России. Оплата при получении.</p>
</section>
<% if (categories.length) { %>
<nav class="categories" aria-label="Категории">
<a href="/" class="chip <%= !activeCategory ? 'chip--active' : '' %>">Все</a>
<% categories.forEach(c => { %>
<a href="/?category=<%= c.slug %>" class="chip <%= activeCategory === c.slug ? 'chip--active' : '' %>"><%= c.name %></a>
<% }) %>
</nav>
<% } %>
<% if (!products.length) { %>
<p class="empty">Товары не найдены. Попробуйте другой запрос.</p>
<% } else { %>
<div class="grid">
<% products.forEach(p => { %>
<article class="card">
<a href="/product/<%= p.slug %>" class="card__image-wrap">
<% if (p.image_url) { %>
<img src="<%= p.image_url %>" alt="<%= p.name %>" class="card__image" loading="lazy">
<% } else { %>
<div class="card__placeholder">Нет фото</div>
<% } %>
</a>
<div class="card__body">
<% if (p.category_name) { %>
<span class="card__category"><%= p.category_name %></span>
<% } %>
<h2 class="card__title"><a href="/product/<%= p.slug %>"><%= p.name %></a></h2>
<p class="card__price"><%= formatPrice(p.price_cents) %></p>
<form action="/cart/add" method="post" class="card__form">
<input type="hidden" name="product_id" value="<%= p.id %>">
<input type="hidden" name="redirect" value="/cart">
<button type="submit" class="btn btn--primary btn--block">В корзину</button>
</form>
</div>
</article>
<% }) %>
</div>
<% } %>
<%- include('partials/layout-end') %>
+21
View File
@@ -0,0 +1,21 @@
<%- include('partials/layout-start') %>
<div class="auth">
<form action="/login" method="post" class="form card">
<h1>Вход</h1>
<% if (error) { %><p class="alert alert--error"><%= error %></p><% } %>
<input type="hidden" name="next" value="<%= next %>">
<label class="label">
Email
<input type="email" name="email" class="input" required value="<%= values.email || '' %>">
</label>
<label class="label">
Пароль
<input type="password" name="password" class="input" required>
</label>
<button type="submit" class="btn btn--primary btn--block">Войти</button>
<p class="form-footer">Нет аккаунта? <a href="/register">Регистрация</a></p>
</form>
</div>
<%- include('partials/layout-end') %>
+30
View File
@@ -0,0 +1,30 @@
<%- include('partials/layout-start') %>
<h1>Заказ #<%= order.id %></h1>
<% if (success) { %>
<p class="alert alert--success">Заказ успешно оформлен! Мы свяжемся с вами по email.</p>
<% } %>
<div class="card order-card">
<% const statusLabels = { pending: 'Ожидает обработки', paid: 'Оплачен', shipped: 'Отправлен', cancelled: 'Отменён' }; %>
<p><strong>Статус:</strong> <span class="status status--<%= order.status %>"><%= statusLabels[order.status] || order.status %></span></p>
<p><strong>Дата:</strong> <%= new Date(order.created_at).toLocaleString('ru-RU') %></p>
<p><strong>Доставка:</strong> <%= order.address %></p>
<p><strong>Контакт:</strong> <%= order.customer_name %>, <%= order.customer_email %><% if (order.customer_phone) { %>, <%= order.customer_phone %><% } %></p>
<h2>Состав заказа</h2>
<ul class="checkout-list">
<% items.forEach(item => { %>
<li>
<span><a href="/product/<%= item.slug %>"><%= item.name %></a> × <%= item.quantity %></span>
<span><%= formatPrice(item.price_cents * item.quantity) %></span>
</li>
<% }) %>
</ul>
<p class="checkout-total">Итого: <strong><%= formatPrice(order.total_cents) %></strong></p>
</div>
<p><a href="/orders" class="link-back">← Все заказы</a></p>
<%- include('partials/layout-end') %>
+33
View File
@@ -0,0 +1,33 @@
<%- include('partials/layout-start') %>
<h1>Мои заказы</h1>
<% if (!orders.length) { %>
<p class="empty">Заказов пока нет. <a href="/">Перейти в каталог</a></p>
<% } else { %>
<table class="cart-table">
<thead>
<tr>
<th>№</th>
<th>Дата</th>
<th>Статус</th>
<th>Сумма</th>
<th></th>
</tr>
</thead>
<tbody>
<% const statusLabels = { pending: 'Ожидает', paid: 'Оплачен', shipped: 'Отправлен', cancelled: 'Отменён' }; %>
<% orders.forEach(o => { %>
<tr>
<td>#<%= o.id %></td>
<td><%= new Date(o.created_at).toLocaleString('ru-RU') %></td>
<td><span class="status status--<%= o.status %>"><%= statusLabels[o.status] || o.status %></span></td>
<td><%= formatPrice(o.total_cents) %></td>
<td><a href="/orders/<%= o.id %>">Подробнее</a></td>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
<%- include('partials/layout-end') %>
+8
View File
@@ -0,0 +1,8 @@
</main>
<footer class="footer">
<div class="container">
<p>&copy; <%= new Date().getFullYear() %> Shop — локальный интернет-магазин на Node.js + SQLite</p>
</div>
</footer>
</body>
</html>
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %> — Shop</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header class="header">
<div class="container header__inner">
<a href="/" class="logo">Shop</a>
<form class="search" action="/" method="get">
<input type="search" name="q" placeholder="Поиск товаров…" value="<%= typeof searchQuery !== 'undefined' ? searchQuery : '' %>" aria-label="Поиск">
<button type="submit" class="btn btn--ghost">Найти</button>
</form>
<nav class="nav">
<a href="/cart" class="nav__link nav__cart">
Корзина
<% if (cartCount > 0) { %><span class="badge"><%= cartCount %></span><% } %>
</a>
<% if (user) { %>
<a href="/account" class="nav__link"><%= user.name %></a>
<form action="/logout" method="post" class="inline-form">
<button type="submit" class="btn btn--ghost btn--sm">Выйти</button>
</form>
<% } else { %>
<a href="/login" class="nav__link">Вход</a>
<a href="/register" class="btn btn--primary btn--sm">Регистрация</a>
<% } %>
</nav>
</div>
</header>
<main class="main container">
+37
View File
@@ -0,0 +1,37 @@
<%- include('partials/layout-start') %>
<article class="product-detail">
<div class="product-detail__media">
<% if (product.image_url) { %>
<img src="<%= product.image_url %>" alt="<%= product.name %>" class="product-detail__image">
<% } else { %>
<div class="card__placeholder product-detail__image">Нет фото</div>
<% } %>
</div>
<div class="product-detail__info">
<% if (product.category_name) { %>
<a href="/?category=<%= product.category_slug %>" class="card__category"><%= product.category_name %></a>
<% } %>
<h1><%= product.name %></h1>
<p class="product-detail__price"><%= formatPrice(product.price_cents) %></p>
<p class="product-detail__desc"><%= product.description %></p>
<p class="product-detail__stock">В наличии: <strong><%= product.stock %></strong> шт.</p>
<% if (product.stock > 0) { %>
<form action="/cart/add" method="post" class="product-detail__form">
<input type="hidden" name="product_id" value="<%= product.id %>">
<label class="label">
Количество
<input type="number" name="quantity" value="1" min="1" max="<%= product.stock %>" class="input input--qty">
</label>
<input type="hidden" name="redirect" value="/cart">
<button type="submit" class="btn btn--primary btn--lg">Добавить в корзину</button>
</form>
<% } else { %>
<p class="alert alert--warn">Нет в наличии</p>
<% } %>
<a href="/" class="link-back">← Назад в каталог</a>
</div>
</article>
<%- include('partials/layout-end') %>
+28
View File
@@ -0,0 +1,28 @@
<%- include('partials/layout-start') %>
<div class="auth">
<form action="/register" method="post" class="form card">
<h1>Регистрация</h1>
<% if (error) { %><p class="alert alert--error"><%= error %></p><% } %>
<label class="label">
Имя
<input type="text" name="name" class="input" required value="<%= values.name || '' %>">
</label>
<label class="label">
Email
<input type="email" name="email" class="input" required value="<%= values.email || '' %>">
</label>
<label class="label">
Пароль
<input type="password" name="password" class="input" required minlength="6">
</label>
<label class="label">
Повторите пароль
<input type="password" name="password2" class="input" required>
</label>
<button type="submit" class="btn btn--primary btn--block">Создать аккаунт</button>
<p class="form-footer">Уже есть аккаунт? <a href="/login">Войти</a></p>
</form>
</div>
<%- include('partials/layout-end') %>