feat: placeholder images, Nostr inbox, order lookup, SEO, bigger logo

- 5 SVG placeholder product images (minimal dark style with watermark initials)
- Seed data updated to reference .svg placeholders
- Nostr DM inbox in admin (Messages tab) with shop npub display
- GET /api/admin/nostr-info endpoint for shop pubkey
- My Orders page: customers look up orders by NIP-07 Nostr identity
- GET /api/orders/by-pubkey/:pubkey endpoint with hex validation
- SeoMeta component for OG/Twitter meta tags
- SEO meta on HomeView and ProductView
- Base OG meta tags in index.html
- "My Orders" link in shop header nav
- Splash logo doubled in size on desktop (680px max)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-17 01:28:36 +00:00
parent 0c7c803aee
commit 52fe7a013f
19 changed files with 342 additions and 6 deletions

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import { watchEffect } from 'vue'
const props = defineProps<{
title?: string
description?: string
image?: string
}>()
const siteName = 'Antonym'
const defaultDescription = 'Fashion for the sovereign individual. Bitcoin only. No accounts. No tracking.'
const defaultImage = '/logos/logo.svg'
watchEffect(() => {
const fullTitle = props.title ? `${props.title}${siteName}` : siteName
document.title = fullTitle
setMeta('description', props.description || defaultDescription)
setMeta('og:title', fullTitle)
setMeta('og:description', props.description || defaultDescription)
setMeta('og:image', props.image || defaultImage)
setMeta('og:type', 'website')
setMeta('og:site_name', siteName)
setMeta('twitter:card', 'summary_large_image')
setMeta('twitter:title', fullTitle)
setMeta('twitter:description', props.description || defaultDescription)
setMeta('twitter:image', props.image || defaultImage)
})
function setMeta(name: string, content: string) {
const attr = name.startsWith('og:') || name.startsWith('twitter:') ? 'property' : 'name'
let el = document.querySelector(`meta[${attr}="${name}"]`)
if (!el) {
el = document.createElement('meta')
el.setAttribute(attr, name)
document.head.appendChild(el)
}
el.setAttribute('content', content)
}
</script>
<template>
<slot />
</template>

View File

@@ -23,6 +23,7 @@ async function handleLogout() {
<nav class="sidebar-nav">
<router-link :to="{ name: 'admin-orders' }" class="nav-item">Orders</router-link>
<router-link :to="{ name: 'admin-products' }" class="nav-item">Products</router-link>
<router-link :to="{ name: 'admin-messages' }" class="nav-item">Messages</router-link>
</nav>
<div class="sidebar-footer">

View File

@@ -14,6 +14,7 @@ const { itemCount } = useCart()
<nav class="nav-links">
<router-link to="/" class="nav-link">Shop</router-link>
<router-link to="/my-orders" class="nav-link">My Orders</router-link>
</nav>
<div class="header-actions">

View File

@@ -107,7 +107,7 @@ onMounted(() => {
/* --- Logo phase --- */
.splash-logo {
width: min(60vw, 340px);
width: min(80vw, 680px);
height: auto;
}