feat(app-shop): local/on-request sales via DM (drop shipping claims)
- Remove the 'Shipped worldwide' and 'At cost, no markup theatre' chips; the remaining chips are Lightning, Cashu, Local handover. - Copy now says sold locally, on request: message me -> we sort the details -> pay in sats at handover. No shipping, at all. - 'How it'll work' -> 'How buying works' (it's a DM, not a cart): Message me / We sort it out / Pay in sats. - New 'Request an item' CTA in the hero that deep-links into a private DM with the shop owner (/messages/<PLATFORM_OWNER_PUBKEYS[0] npub>), so a buyer can start the conversation with zero checkout machinery. - Honesty footer states local handover only / no shipping of any kind. - AGENTS.md documents the model (no cart/checkout/shipping without a decision).
This commit is contained in:
parent
8dcc97fd81
commit
059d9da5c4
2 changed files with 61 additions and 16 deletions
|
|
@ -345,7 +345,12 @@ session with **no other memory of this project**. So:
|
|||
aren't hard-coded**, so Marketplace/Mints (and any future section) are always
|
||||
reachable on mobile — don't re-hardcode only a subset.
|
||||
- **App Shop** (`/app-shop`, `src/pages/AppShopPage.tsx`): SkateSpots merch, to be
|
||||
paid in **sats only (Lightning or Cashu)** — no cards/accounts. Registered in
|
||||
paid in **sats only (Lightning or Cashu)** — no cards/accounts — sold
|
||||
**locally, on request**: there is **no cart, no checkout, no shipping**. Buying
|
||||
is a conversation: "Request an item" deep-links to a **DM with the owner**
|
||||
(`/messages/<owner npub>`, owner = `PLATFORM_OWNER_PUBKEYS[0]`), the details
|
||||
are agreed in chat, and sats change hands at handover. Don't add shipping
|
||||
copy/rates or a cart without a decision to change that model. Registered in
|
||||
`NAV_CATALOG` as id `app-shop` (label "App Shop", `ShoppingBag`), which is what
|
||||
makes it appear in **both** the desktop "More" menu and the mobile burger menu
|
||||
(the mobile menu appends catalog items not in `MOBILE_MENU_LISTED_HREFS`). It is
|
||||
|
|
|
|||
|
|
@ -1,21 +1,27 @@
|
|||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import {
|
||||
ShoppingBag,
|
||||
Zap,
|
||||
Coins,
|
||||
Truck,
|
||||
Shirt,
|
||||
Sticker,
|
||||
Package,
|
||||
Hammer,
|
||||
Sparkles,
|
||||
BadgeCheck,
|
||||
MapPin,
|
||||
MessageCircle,
|
||||
ArrowRight,
|
||||
} from 'lucide-react';
|
||||
import { PageHeader } from '@/components/PageHeader';
|
||||
import { NewsletterSubscribeButton } from '@/components/NewsletterSubscribeButton';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { PLATFORM_OWNER_PUBKEYS } from '@/hooks/useListingRestrictions';
|
||||
|
||||
/** Where "request an item" goes: an encrypted DM straight to the shop owner. */
|
||||
const SHOP_OWNER_NPUB = nip19.npubEncode(PLATFORM_OWNER_PUBKEYS[0]);
|
||||
const REQUEST_HREF = `/messages/${SHOP_OWNER_NPUB}`;
|
||||
|
||||
/**
|
||||
* App Shop — SkateSpots merch.
|
||||
|
|
@ -38,8 +44,7 @@ const PLANNED = [
|
|||
const PAY_CHIPS = [
|
||||
{ icon: Zap, label: 'Lightning' },
|
||||
{ icon: Coins, label: 'Cashu' },
|
||||
{ icon: Truck, label: 'Shipped worldwide' },
|
||||
{ icon: BadgeCheck, label: 'At cost, no markup theatre' },
|
||||
{ icon: MapPin, label: 'Local handover' },
|
||||
];
|
||||
|
||||
export default function AppShopPage() {
|
||||
|
|
@ -81,12 +86,30 @@ export default function AppShopPage() {
|
|||
</h2>
|
||||
|
||||
<p className="mt-4 max-w-xl text-sm leading-relaxed text-gray-300 sm:text-base">
|
||||
Merch for the people who keep this thing rolling. Paid in{' '}
|
||||
<strong className="font-bold text-[#f7931a]">sats</strong> — Lightning or Cashu,
|
||||
whichever you already keep. No cards, no accounts, no middleman skimming the drop.
|
||||
Merch for the people who keep this thing rolling — sold{' '}
|
||||
<strong className="font-bold text-white">locally, on request</strong>. You message
|
||||
me, we sort the details, and you pay in{' '}
|
||||
<strong className="font-bold text-[#f7931a]">sats</strong> — Lightning or Cashu —
|
||||
when you collect it. No cards, no accounts, no checkout robot, no middleman
|
||||
skimming the drop.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex flex-wrap gap-2">
|
||||
<div className="mt-6 flex flex-wrap items-center gap-3">
|
||||
<Button
|
||||
asChild
|
||||
className="gap-2 bg-[#ccff00] font-black uppercase text-black hover:bg-[#ccff00]/90"
|
||||
>
|
||||
<Link to={REQUEST_HREF}>
|
||||
<MessageCircle className="h-4 w-4" />
|
||||
Request an item
|
||||
</Link>
|
||||
</Button>
|
||||
<span className="text-[11px] text-gray-500">
|
||||
Opens a private, encrypted DM with me.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex flex-wrap gap-2">
|
||||
{PAY_CHIPS.map(({ icon: Icon, label }) => (
|
||||
<span
|
||||
key={label}
|
||||
|
|
@ -158,16 +181,31 @@ export default function AppShopPage() {
|
|||
<section className="mt-10">
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
<h3 className="text-sm font-black uppercase tracking-widest text-white">
|
||||
How it'll work
|
||||
How buying works
|
||||
</h3>
|
||||
<div className="h-px flex-1 bg-white/10" />
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-gray-500">
|
||||
it's a DM, not a cart
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ol className="grid gap-3 sm:grid-cols-3">
|
||||
{[
|
||||
{ n: '1', t: 'Pick your thing', d: 'One page, no logged-in pricing games, no fake urgency.' },
|
||||
{ n: '2', t: 'Pay in sats', d: 'Lightning or Cashu. Straight to us — no processor in the middle.' },
|
||||
{ n: '3', t: 'It ships', d: 'Posted to your door with an honest tracking number.' },
|
||||
{
|
||||
n: '1',
|
||||
t: 'Message me',
|
||||
d: 'Send a DM with what you want — item, size, colour, whatever you are after.',
|
||||
},
|
||||
{
|
||||
n: '2',
|
||||
t: 'We sort it out',
|
||||
d: 'Price in sats, and where and when to meet. It is a conversation, not a checkout.',
|
||||
},
|
||||
{
|
||||
n: '3',
|
||||
t: 'Pay in sats',
|
||||
d: 'Lightning or Cashu at handover, then it is yours. No shipping, no waiting rooms.',
|
||||
},
|
||||
].map((s) => (
|
||||
<li
|
||||
key={s.n}
|
||||
|
|
@ -211,9 +249,11 @@ export default function AppShopPage() {
|
|||
<section className="mt-8 rounded-2xl border border-white/8 bg-white/[0.02] p-4">
|
||||
<p className="text-xs leading-relaxed text-gray-500">
|
||||
<strong className="text-gray-300">The honest bit:</strong> we hold no stock and
|
||||
nothing here is for sale. There are no pre-orders, no countdown timers and no
|
||||
fake scarcity — when there is something real to buy, this page will say so and
|
||||
take your sats. Until then it is just an empty shop with good lighting.
|
||||
nothing here is for sale. There are no pre-orders, no countdown timers and no fake
|
||||
scarcity — and it is <strong className="text-gray-300">local handover only</strong>,
|
||||
arranged by DM, so there is no shipping of any kind. When there is something real,
|
||||
this page will say so and we will sort the details between us. Until then it is
|
||||
just an empty shop with good lighting.
|
||||
</p>
|
||||
<div className="mt-4 flex flex-wrap gap-2">
|
||||
<Button asChild variant="outline" className="gap-2 border-white/15">
|
||||
|
|
|
|||
Loading…
Reference in a new issue