views: add base layout, home page and product card partial
This commit is contained in:
1 parent
e395799550
commit
d22f4c9210
3 files changed
+186
No files matched your search
@@ -0,0 +1,57 @@
|
|||||||
|
<section class="hero">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="hero__inner">
|
||||||
|
<span class="hero__eyebrow">Independent goods, fairly priced</span>
|
||||||
|
<h1>A small shop with a big appetite for good things.</h1>
|
||||||
|
<p>Bazaar is a demo storefront running on a hand-rolled PHP microframework - real catalog, session cart, Stripe checkout and an admin back office. Browse the shelves.</p>
|
||||||
|
<div class="hero__actions">
|
||||||
|
<a class="btn btn--primary" href="/products">Browse the catalog</a>
|
||||||
|
<?php if (!empty($categories)): ?>
|
||||||
|
<a class="btn" href="/products?category=<?= e($categories[0]['slug']) ?>">Start with <?= e($categories[0]['name']) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="block">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="section-head">
|
||||||
|
<div>
|
||||||
|
<h2>Fresh on the shelves</h2>
|
||||||
|
<p class="muted" style="margin:0">The latest additions to the catalog.</p>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn--ghost" href="/products">View all →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($featured)): ?>
|
||||||
|
<div class="empty">
|
||||||
|
<p class="muted">No products yet. Seed the database or add one from the admin panel.</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="product-grid">
|
||||||
|
<?php foreach ($featured as $product): ?>
|
||||||
|
<?= $view->renderPartial('partials.productCard', ['product' => $product, 'currency' => $currency]) ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (!empty($categories)): ?>
|
||||||
|
<section class="block block--tight" style="background:var(--surface);border-top:1px solid var(--line);border-bottom:1px solid var(--line)">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="section-head">
|
||||||
|
<h2 style="margin:0">Shop by department</h2>
|
||||||
|
</div>
|
||||||
|
<div class="chips">
|
||||||
|
<?php foreach ($categories as $category): ?>
|
||||||
|
<a class="chip" href="/products?category=<?= e($category['slug']) ?>">
|
||||||
|
<?= e($category['name']) ?>
|
||||||
|
<span class="muted">· <?= (int) $category['productCount'] ?></span>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php endif; ?>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<?= e($locale) ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title><?= e($title ?? 'Bazaar - a small good shop') ?></title>
|
||||||
|
<link rel="stylesheet" href="/assets/app.css">
|
||||||
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='7' fill='%23b4531f'/><text x='16' y='23' font-size='19' text-anchor='middle' fill='white' font-family='serif'>B</text></svg>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<div class="wrap site-header__bar">
|
||||||
|
<a class="brand" href="/">
|
||||||
|
<span class="brand__mark">B</span>
|
||||||
|
<?= e($appName) ?>
|
||||||
|
</a>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="/products"><?= e($t('nav.shop')) ?></a>
|
||||||
|
<?php foreach (array_slice($navCategories, 0, 3) as $navCategory): ?>
|
||||||
|
<a class="hide-sm" href="/products?category=<?= e($navCategory['slug']) ?>"><?= e($navCategory['name']) ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</nav>
|
||||||
|
<span class="nav__spacer"></span>
|
||||||
|
<nav class="nav">
|
||||||
|
<span class="lang-switch">
|
||||||
|
<?php foreach ($supportedLocales as $code): ?>
|
||||||
|
<a class="lang-switch__item <?= $locale === $code ? 'lang-switch__item--active' : '' ?>" href="/locale/<?= e($code) ?>"><?= e(strtoupper($code)) ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</span>
|
||||||
|
<?php if ($currentUser !== null): ?>
|
||||||
|
<?php if ($isStaff): ?>
|
||||||
|
<a href="/admin"><?= e($t('nav.admin')) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="/account/wishlist"><?= e($t('nav.wishlist')) ?><?php if ($wishlistCount > 0): ?> (<?= (int) $wishlistCount ?>)<?php endif; ?></a>
|
||||||
|
<a href="/account/orders"><?= e($t('nav.orders')) ?></a>
|
||||||
|
<form method="post" action="/logout" style="display:inline">
|
||||||
|
<?= $csrfToken ? '<input type="hidden" name="csrfToken" value="' . e($csrfToken) . '">' : '' ?>
|
||||||
|
<button type="submit" class="btn btn--ghost btn--sm"><?= e($t('nav.signOut')) ?></button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="/login"><?= e($t('nav.signIn')) ?></a>
|
||||||
|
<a class="btn btn--sm" href="/register"><?= e($t('nav.createAccount')) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a class="cart-link" href="/cart" aria-label="<?= e($t('nav.cart')) ?>">
|
||||||
|
<svg class="icon" viewBox="0 0 24 24"><path d="M3 4h2l2.4 12.3a1 1 0 0 0 1 .8h9.2a1 1 0 0 0 1-.8L21 8H6"/><circle cx="9" cy="20" r="1.3"/><circle cx="18" cy="20" r="1.3"/></svg>
|
||||||
|
<?php if ($cartCount > 0): ?><span class="cart-badge"><?= (int) $cartCount ?></span><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<?php if (!empty($flashes)): ?>
|
||||||
|
<div class="flashes">
|
||||||
|
<?php foreach ($flashes as $type => $message): ?>
|
||||||
|
<div class="flash flash--<?= e($type === 'error' ? 'error' : 'success') ?>"><?= e($message) ?></div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<?= $content ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="site-footer">
|
||||||
|
<div class="wrap site-footer__bar">
|
||||||
|
<span>© <?= date('Y') ?> <?= e($appName) ?>. Built on a hand-rolled PHP microframework.</span>
|
||||||
|
<span>Payments secured by Stripe (test mode)</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
$stock = (int) ($product['availableStock'] ?? $product['stockQuantity']);
|
||||||
|
$initial = strtoupper(substr($product['name'], 0, 1));
|
||||||
|
$ratingAverage = (float) ($product['ratingAverage'] ?? 0);
|
||||||
|
$ratingCount = (int) ($product['ratingCount'] ?? 0);
|
||||||
|
$variantCount = (int) ($product['variantCount'] ?? 0);
|
||||||
|
$priceFromCents = (int) ($product['priceFromCents'] ?? $product['priceCents']);
|
||||||
|
$wishlisted = $wishlisted ?? [];
|
||||||
|
$isSaved = in_array((int) $product['id'], array_map('intval', $wishlisted), true);
|
||||||
|
?>
|
||||||
|
<article class="product-card">
|
||||||
|
<a href="/products/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
|
||||||
|
<div class="product-card__media">
|
||||||
|
<?php if (!empty($product['imageUrl'])): ?>
|
||||||
|
<img src="<?= e($product['imageUrl']) ?>" alt="<?= e($product['name']) ?>" loading="lazy">
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="product-card__placeholder"><?= e($initial) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="product-card__body">
|
||||||
|
<?php if (!empty($product['categoryName'])): ?>
|
||||||
|
<span class="product-card__cat"><?= e($product['categoryName']) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="/products/<?= e($product['slug']) ?>"><span class="product-card__name"><?= e($product['name']) ?></span></a>
|
||||||
|
|
||||||
|
<?php if ($ratingCount > 0): ?>
|
||||||
|
<div class="rating rating--sm" title="<?= number_format($ratingAverage, 1) ?> / 5">
|
||||||
|
<span class="rating__stars"><?= str_repeat('*', (int) round($ratingAverage)) ?><span class="rating__off"><?= str_repeat('*', 5 - (int) round($ratingAverage)) ?></span></span>
|
||||||
|
<span class="rating__count"><?= number_format($ratingAverage, 1) ?> (<?= $ratingCount ?>)</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="product-card__foot">
|
||||||
|
<span class="price">
|
||||||
|
<?php if ($variantCount > 0 && $priceFromCents !== (int) $product['priceCents']): ?><span class="price__from">from</span> <?php endif; ?>
|
||||||
|
<?= e(formatMoney($priceFromCents, $currency)) ?>
|
||||||
|
</span>
|
||||||
|
<?php if ($stock <= 0): ?>
|
||||||
|
<span class="badge badge--out">Sold out</span>
|
||||||
|
<?php elseif ($stock <= 5): ?>
|
||||||
|
<span class="badge badge--low">Only <?= $stock ?> left</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge badge--stock">In stock</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (($currentUser ?? null) !== null): ?>
|
||||||
|
<form method="post" action="/wishlist/toggle" class="product-card__wish">
|
||||||
|
<input type="hidden" name="csrfToken" value="<?= e($csrfToken) ?>">
|
||||||
|
<input type="hidden" name="productId" value="<?= (int) $product['id'] ?>">
|
||||||
|
<button type="submit" class="btn btn--ghost btn--sm<?= $isSaved ? ' is-saved' : '' ?>">
|
||||||
|
<?= $isSaved ? 'Saved' : 'Save' ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
Reference in new issue
Block a user