DIY: use internal links, mark the rest "coming soon"
Reworked the DIY hub per feedback: point at our own app sections where we have relevant content, and render the topics we don't cover yet as unclickable cards with a "Coming soon" badge — no external links. - Live internal cards: Map your DIY spot (/create-spot), Buy parts & gear (/marketplace), plus the "Do it with people" actions (Builder, crews, spot, listing, coaching). - Coming-soon cards: deck building, SK8CAD, cost/pricing, ledge/box, quarterpipe, flatbar, concrete basics, coping, regrip, bearings/trucks, 3D-printed molds, mold tools. - Dropped external URLs/sanitizeUrl from the page; updated the footer copy.
This commit is contained in:
parent
84fde55ddd
commit
30d46ddfed
5 changed files with 91 additions and 81 deletions
20
AGENTS.md
20
AGENTS.md
|
|
@ -806,18 +806,18 @@ session with **no other memory of this project**. So:
|
||||||
burger menu (`MOBILE_MENU_LISTED_HREFS`) now **appends catalog items that
|
burger menu (`MOBILE_MENU_LISTED_HREFS`) now **appends catalog items that
|
||||||
aren't hard-coded**, so Marketplace/Mints (and any future section) are always
|
aren't hard-coded**, so Marketplace/Mints (and any future section) are always
|
||||||
reachable on mobile — don't re-hardcode only a subset.
|
reachable on mobile — don't re-hardcode only a subset.
|
||||||
- **DIY resources (`/diy`, 2026-10-05):** a curated hub for making your own
|
- **DIY resources (`/diy`, 2026-10-05):** a hub for making your own
|
||||||
boards/ramps/concrete. Content is a seed list in **`src/lib/diyGuides.ts`**
|
boards/ramps/concrete. Content is a seed list in **`src/lib/diyGuides.ts`**
|
||||||
(`DIY_CATEGORIES` boards/ramps/concrete/repairs/tools + `DIY_CARDS` +
|
(`DIY_CATEGORIES` boards/ramps/concrete/repairs/tools + `DIY_CARDS` +
|
||||||
`DIY_ACTIONS`) — vetted external links (Open Source Skateboards / SK8CAD /
|
`DIY_ACTIONS`); `src/pages/DiyPage.tsx` renders it. **Policy: link to our own
|
||||||
Instructables / YouTube) and in-app action cards; rendered by
|
app sections (`to`) where we have content; otherwise show the topic as an
|
||||||
`src/pages/DiyPage.tsx`. Registered as a nav-catalog item (id `diy`, Hammer)
|
unclickable `comingSoon` card** — no external links. Registered as a nav-catalog
|
||||||
so it shows in the desktop "More" menu **and** the mobile burger, and linked
|
item (id `diy`, Hammer) so it shows in the desktop "More" menu **and** the
|
||||||
from the **site footer** + the **map links strip**. Cross-links OUT to
|
mobile burger, and is linked from the **site footer** + the **map links strip**.
|
||||||
`/lab?craft=building` (Builder), `/crews`, `/create-spot`, `/create-listing`,
|
Cross-links to `/lab?craft=building` (Builder), `/crews`, `/create-spot`,
|
||||||
`/lab?craft=coaching`. ⚠️ **Link out, never rehost** — credit the source;
|
`/create-listing`, `/lab?craft=coaching`, `/marketplace`. Next: write our own
|
||||||
`sanitizeUrl` on every external URL. In-app tools (cost calculator) are the
|
step-by-step guides (NIP-23 `30023` with `t=diy`) to flip the coming-soon cards
|
||||||
next step; community guides would be NIP-23 `30023` with `t=diy`.
|
live, plus the in-app deck cost calculator.
|
||||||
- **App Shop** (`/app-shop`, `src/pages/AppShopPage.tsx`): SkateSpots merch, to be
|
- **App Shop** (`/app-shop`, `src/pages/AppShopPage.tsx`): SkateSpots merch, to be
|
||||||
paid in **sats only (Lightning or Cashu)** — no cards/accounts — sold
|
paid in **sats only (Lightning or Cashu)** — no cards/accounts — sold
|
||||||
**locally, on request**: there is **no cart, no checkout, no shipping**. Buying
|
**locally, on request**: there is **no cart, no checkout, no shipping**. Buying
|
||||||
|
|
|
||||||
|
|
@ -795,7 +795,8 @@ diyUi: {
|
||||||
tagline: 'Guides, open tools and plans to build your own decks, ramps and concrete spots — then find the people, places and market to go with them.',
|
tagline: 'Guides, open tools and plans to build your own decks, ramps and concrete spots — then find the people, places and market to go with them.',
|
||||||
doItWithPeople: 'Do it with people',
|
doItWithPeople: 'Do it with people',
|
||||||
source: 'Source',
|
source: 'Source',
|
||||||
footer: 'Resources link to their original authors — we curate, we don’t rehost. Always work safely: pads, gloves and eye protection.',
|
comingSoon: 'Coming soon',
|
||||||
|
footer: 'Step-by-step guides are on the way. For now: find the people who build in The Lab, map your spot, and buy or sell in the Marketplace. Work safely — pads, gloves and eye protection.',
|
||||||
categories: {
|
categories: {
|
||||||
boards: 'Boards',
|
boards: 'Boards',
|
||||||
ramps: 'Ramps & wooden obstacles',
|
ramps: 'Ramps & wooden obstacles',
|
||||||
|
|
@ -812,6 +813,7 @@ diyUi: {
|
||||||
flatbar: { title: 'Build a flatbar', blurb: 'Cheap, portable and the best way to learn rails.' },
|
flatbar: { title: 'Build a flatbar', blurb: 'Cheap, portable and the best way to learn rails.' },
|
||||||
concreteBasics: { title: 'Concrete basics', blurb: 'Mixing, forming and finishing a ledge or bank that lasts.' },
|
concreteBasics: { title: 'Concrete basics', blurb: 'Mixing, forming and finishing a ledge or bank that lasts.' },
|
||||||
coping: { title: 'Coping & edges', blurb: 'How to set coping and finish a transition for a smooth grind.' },
|
coping: { title: 'Coping & edges', blurb: 'How to set coping and finish a transition for a smooth grind.' },
|
||||||
|
buyParts: { title: 'Buy parts & gear', blurb: 'Pick up decks, hardware and tools from sellers in the Marketplace.' },
|
||||||
mapSpot: { title: 'Map your DIY spot', blurb: 'Built something? Add it to the map so others can find and skate it.' },
|
mapSpot: { title: 'Map your DIY spot', blurb: 'Built something? Add it to the map so others can find and skate it.' },
|
||||||
regrip: { title: 'Regrip a board', blurb: 'Strip old grip and lay new grip cleanly.' },
|
regrip: { title: 'Regrip a board', blurb: 'Strip old grip and lay new grip cleanly.' },
|
||||||
tune: { title: 'Tune bearings & trucks', blurb: 'Clean bearings, swap bushings and set your truck geometry.' },
|
tune: { title: 'Tune bearings & trucks', blurb: 'Clean bearings, swap bushings and set your truck geometry.' },
|
||||||
|
|
|
||||||
|
|
@ -795,7 +795,8 @@ diyUi: {
|
||||||
tagline: 'Guías, herramientas abiertas y planos para hacer tus propias tablas, rampas y spots de hormigón — y luego encontrar a la gente, los sitios y el mercado.',
|
tagline: 'Guías, herramientas abiertas y planos para hacer tus propias tablas, rampas y spots de hormigón — y luego encontrar a la gente, los sitios y el mercado.',
|
||||||
doItWithPeople: 'Hazlo con gente',
|
doItWithPeople: 'Hazlo con gente',
|
||||||
source: 'Fuente',
|
source: 'Fuente',
|
||||||
footer: 'Los recursos enlazan a sus autores originales: curamos, no rehospedamos. Trabaja siempre con seguridad: protecciones, guantes y gafas.',
|
comingSoon: 'Próximamente',
|
||||||
|
footer: 'Las guías paso a paso están en camino. Por ahora: encuentra a quien construye en The Lab, mapea tu spot y compra o vende en el Marketplace. Trabaja con seguridad: protecciones, guantes y gafas.',
|
||||||
categories: {
|
categories: {
|
||||||
boards: 'Tablas',
|
boards: 'Tablas',
|
||||||
ramps: 'Rampas y obstáculos de madera',
|
ramps: 'Rampas y obstáculos de madera',
|
||||||
|
|
@ -812,6 +813,7 @@ diyUi: {
|
||||||
flatbar: { title: 'Haz un flatbar', blurb: 'Barato, portátil y la mejor forma de aprender a deslizar.' },
|
flatbar: { title: 'Haz un flatbar', blurb: 'Barato, portátil y la mejor forma de aprender a deslizar.' },
|
||||||
concreteBasics: { title: 'Fundamentos del hormigón', blurb: 'Mezcla, encofrado y acabado de un bordillo o bank duradero.' },
|
concreteBasics: { title: 'Fundamentos del hormigón', blurb: 'Mezcla, encofrado y acabado de un bordillo o bank duradero.' },
|
||||||
coping: { title: 'Coping y bordes', blurb: 'Cómo colocar el coping y rematar una transición para un grind suave.' },
|
coping: { title: 'Coping y bordes', blurb: 'Cómo colocar el coping y rematar una transición para un grind suave.' },
|
||||||
|
buyParts: { title: 'Compra piezas y material', blurb: 'Consigue tablas, tornillería y herramientas de vendedores en el Marketplace.' },
|
||||||
mapSpot: { title: 'Mapea tu spot DIY', blurb: '¿Has construido algo? Añádelo al mapa para que otros lo encuentren.' },
|
mapSpot: { title: 'Mapea tu spot DIY', blurb: '¿Has construido algo? Añádelo al mapa para que otros lo encuentren.' },
|
||||||
regrip: { title: 'Cambia el grip', blurb: 'Quita el grip viejo y coloca el nuevo limpio.' },
|
regrip: { title: 'Cambia el grip', blurb: 'Quita el grip viejo y coloca el nuevo limpio.' },
|
||||||
tune: { title: 'Ajusta rodamientos y ejes', blurb: 'Limpia rodamientos, cambia gomas y ajusta la geometría de los ejes.' },
|
tune: { title: 'Ajusta rodamientos y ejes', blurb: 'Limpia rodamientos, cambia gomas y ajusta la geometría de los ejes.' },
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Curated DIY resources — how to make your own boards, ramps and concrete spots.
|
* Curated DIY resources — how to make your own boards, ramps and concrete spots.
|
||||||
*
|
*
|
||||||
* This is a **seed** list (vetted links + in-app actions), not user content, so
|
* Policy: point at **our own** app sections whenever we have something relevant
|
||||||
* it needs no moderation. Titles/blurbs are i18n keys (`diyUi.*`). To extend,
|
* (`to`), and otherwise show the topic as an unclickable **"coming soon"** card
|
||||||
* just add entries here.
|
* (`comingSoon: true`) instead of linking out. So this is a roadmap of what the
|
||||||
|
* DIY hub will cover, with live links where the app already has content.
|
||||||
*
|
*
|
||||||
* ⚠️ Link out, never rehost: credit the source. External URLs are rendered
|
* Content, not user data — no moderation. Titles/blurbs are i18n keys (`diyUi.*`).
|
||||||
* through `sanitizeUrl` on the page.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type DiyCategoryId = 'boards' | 'ramps' | 'concrete' | 'repairs' | 'tools';
|
export type DiyCategoryId = 'boards' | 'ramps' | 'concrete' | 'repairs' | 'tools';
|
||||||
|
|
@ -17,7 +17,7 @@ export interface DiyCategory {
|
||||||
emoji: string;
|
emoji: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A resource card — either an external guide (`url`) or an in-app action (`to`). */
|
/** A resource card — an in-app link (`to`) or a "coming soon" placeholder. */
|
||||||
export interface DiyCard {
|
export interface DiyCard {
|
||||||
id: string;
|
id: string;
|
||||||
/** Omit for the cross-link actions block. */
|
/** Omit for the cross-link actions block. */
|
||||||
|
|
@ -25,10 +25,10 @@ export interface DiyCard {
|
||||||
titleKey: string;
|
titleKey: string;
|
||||||
blurbKey: string;
|
blurbKey: string;
|
||||||
emoji: string;
|
emoji: string;
|
||||||
/** Shown as a credit for external links. */
|
/** In-app route. Omit for a coming-soon card. */
|
||||||
source?: string;
|
|
||||||
url?: string;
|
|
||||||
to?: string;
|
to?: string;
|
||||||
|
/** No destination yet — rendered unclickable with a "Coming soon" badge. */
|
||||||
|
comingSoon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DIY_CATEGORIES: DiyCategory[] = [
|
export const DIY_CATEGORIES: DiyCategory[] = [
|
||||||
|
|
@ -42,13 +42,12 @@ export const DIY_CATEGORIES: DiyCategory[] = [
|
||||||
export const DIY_CARDS: DiyCard[] = [
|
export const DIY_CARDS: DiyCard[] = [
|
||||||
// ── Boards ────────────────────────────────────────────────────────────────
|
// ── Boards ────────────────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
id: 'oss-build',
|
id: 'ossBuild',
|
||||||
category: 'boards',
|
category: 'boards',
|
||||||
emoji: '🛹',
|
emoji: '🛹',
|
||||||
titleKey: 'diyUi.cards.ossBuild.title',
|
titleKey: 'diyUi.cards.ossBuild.title',
|
||||||
blurbKey: 'diyUi.cards.ossBuild.blurb',
|
blurbKey: 'diyUi.cards.ossBuild.blurb',
|
||||||
source: 'Open Source Skateboards',
|
comingSoon: true,
|
||||||
url: 'https://opensourceskateboards.com',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sk8cad',
|
id: 'sk8cad',
|
||||||
|
|
@ -56,28 +55,25 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '🧊',
|
emoji: '🧊',
|
||||||
titleKey: 'diyUi.cards.sk8cad.title',
|
titleKey: 'diyUi.cards.sk8cad.title',
|
||||||
blurbKey: 'diyUi.cards.sk8cad.blurb',
|
blurbKey: 'diyUi.cards.sk8cad.blurb',
|
||||||
source: 'Open Source Skateboards',
|
comingSoon: true,
|
||||||
url: 'https://sk8cad.com',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'deck-cost',
|
id: 'deckCost',
|
||||||
category: 'boards',
|
category: 'boards',
|
||||||
emoji: '💰',
|
emoji: '💰',
|
||||||
titleKey: 'diyUi.cards.deckCost.title',
|
titleKey: 'diyUi.cards.deckCost.title',
|
||||||
blurbKey: 'diyUi.cards.deckCost.blurb',
|
blurbKey: 'diyUi.cards.deckCost.blurb',
|
||||||
source: 'Open Source Skateboards',
|
comingSoon: true,
|
||||||
url: 'https://opensourceskateboards.com',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Ramps & wooden obstacles ─────────────────────────────────────────────
|
// ── Ramps & wooden obstacles ─────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
id: 'ledge-box',
|
id: 'ledgeBox',
|
||||||
category: 'ramps',
|
category: 'ramps',
|
||||||
emoji: '📦',
|
emoji: '📦',
|
||||||
titleKey: 'diyUi.cards.ledgeBox.title',
|
titleKey: 'diyUi.cards.ledgeBox.title',
|
||||||
blurbKey: 'diyUi.cards.ledgeBox.blurb',
|
blurbKey: 'diyUi.cards.ledgeBox.blurb',
|
||||||
source: 'Instructables',
|
comingSoon: true,
|
||||||
url: 'https://www.instructables.com/howto/skate+ledge/',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'quarterpipe',
|
id: 'quarterpipe',
|
||||||
|
|
@ -85,8 +81,7 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '🌊',
|
emoji: '🌊',
|
||||||
titleKey: 'diyUi.cards.quarterpipe.title',
|
titleKey: 'diyUi.cards.quarterpipe.title',
|
||||||
blurbKey: 'diyUi.cards.quarterpipe.blurb',
|
blurbKey: 'diyUi.cards.quarterpipe.blurb',
|
||||||
source: 'Instructables',
|
comingSoon: true,
|
||||||
url: 'https://www.instructables.com/howto/skate+ramp/',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'flatbar',
|
id: 'flatbar',
|
||||||
|
|
@ -94,19 +89,25 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '➖',
|
emoji: '➖',
|
||||||
titleKey: 'diyUi.cards.flatbar.title',
|
titleKey: 'diyUi.cards.flatbar.title',
|
||||||
blurbKey: 'diyUi.cards.flatbar.blurb',
|
blurbKey: 'diyUi.cards.flatbar.blurb',
|
||||||
source: 'YouTube',
|
comingSoon: true,
|
||||||
url: 'https://www.youtube.com/results?search_query=how+to+build+a+skate+flatbar',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Concrete & DIY spots ─────────────────────────────────────────────────
|
// ── Concrete & DIY spots ─────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
id: 'concrete-basics',
|
id: 'mapSpot',
|
||||||
|
category: 'concrete',
|
||||||
|
emoji: '📍',
|
||||||
|
titleKey: 'diyUi.cards.mapSpot.title',
|
||||||
|
blurbKey: 'diyUi.cards.mapSpot.blurb',
|
||||||
|
to: '/create-spot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'concreteBasics',
|
||||||
category: 'concrete',
|
category: 'concrete',
|
||||||
emoji: '🧱',
|
emoji: '🧱',
|
||||||
titleKey: 'diyUi.cards.concreteBasics.title',
|
titleKey: 'diyUi.cards.concreteBasics.title',
|
||||||
blurbKey: 'diyUi.cards.concreteBasics.blurb',
|
blurbKey: 'diyUi.cards.concreteBasics.blurb',
|
||||||
source: 'Instructables',
|
comingSoon: true,
|
||||||
url: 'https://www.instructables.com/howto/concrete+skatepark/',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'coping',
|
id: 'coping',
|
||||||
|
|
@ -114,16 +115,7 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '🛞',
|
emoji: '🛞',
|
||||||
titleKey: 'diyUi.cards.coping.title',
|
titleKey: 'diyUi.cards.coping.title',
|
||||||
blurbKey: 'diyUi.cards.coping.blurb',
|
blurbKey: 'diyUi.cards.coping.blurb',
|
||||||
source: 'YouTube',
|
comingSoon: true,
|
||||||
url: 'https://www.youtube.com/results?search_query=diy+skatepark+coping',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'map-spot',
|
|
||||||
category: 'concrete',
|
|
||||||
emoji: '📍',
|
|
||||||
titleKey: 'diyUi.cards.mapSpot.title',
|
|
||||||
blurbKey: 'diyUi.cards.mapSpot.blurb',
|
|
||||||
to: '/create-spot',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Repairs & tuning ─────────────────────────────────────────────────────
|
// ── Repairs & tuning ─────────────────────────────────────────────────────
|
||||||
|
|
@ -133,8 +125,7 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '🩹',
|
emoji: '🩹',
|
||||||
titleKey: 'diyUi.cards.regrip.title',
|
titleKey: 'diyUi.cards.regrip.title',
|
||||||
blurbKey: 'diyUi.cards.regrip.blurb',
|
blurbKey: 'diyUi.cards.regrip.blurb',
|
||||||
source: 'YouTube',
|
comingSoon: true,
|
||||||
url: 'https://www.youtube.com/results?search_query=how+to+regrip+a+skateboard',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tune',
|
id: 'tune',
|
||||||
|
|
@ -142,33 +133,38 @@ export const DIY_CARDS: DiyCard[] = [
|
||||||
emoji: '🔩',
|
emoji: '🔩',
|
||||||
titleKey: 'diyUi.cards.tune.title',
|
titleKey: 'diyUi.cards.tune.title',
|
||||||
blurbKey: 'diyUi.cards.tune.blurb',
|
blurbKey: 'diyUi.cards.tune.blurb',
|
||||||
source: 'YouTube',
|
comingSoon: true,
|
||||||
url: 'https://www.youtube.com/results?search_query=clean+skateboard+bearings+bushings',
|
},
|
||||||
|
{
|
||||||
|
id: 'buyParts',
|
||||||
|
category: 'repairs',
|
||||||
|
emoji: '🛒',
|
||||||
|
titleKey: 'diyUi.cards.buyParts.title',
|
||||||
|
blurbKey: 'diyUi.cards.buyParts.blurb',
|
||||||
|
to: '/marketplace',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Tools & plans ────────────────────────────────────────────────────────
|
// ── Tools & plans ────────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
id: 'print-molds',
|
id: 'printMolds',
|
||||||
category: 'tools',
|
category: 'tools',
|
||||||
emoji: '🖨️',
|
emoji: '🖨️',
|
||||||
titleKey: 'diyUi.cards.printMolds.title',
|
titleKey: 'diyUi.cards.printMolds.title',
|
||||||
blurbKey: 'diyUi.cards.printMolds.blurb',
|
blurbKey: 'diyUi.cards.printMolds.blurb',
|
||||||
source: 'Printables',
|
comingSoon: true,
|
||||||
url: 'https://www.printables.com/search/models?q=skateboard+mold',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sk8cad-tools',
|
id: 'sk8cadTools',
|
||||||
category: 'tools',
|
category: 'tools',
|
||||||
emoji: '📐',
|
emoji: '📐',
|
||||||
titleKey: 'diyUi.cards.sk8cadTools.title',
|
titleKey: 'diyUi.cards.sk8cadTools.title',
|
||||||
blurbKey: 'diyUi.cards.sk8cadTools.blurb',
|
blurbKey: 'diyUi.cards.sk8cadTools.blurb',
|
||||||
source: 'Open Source Skateboards',
|
comingSoon: true,
|
||||||
url: 'https://sk8cad.com',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cross-links into the rest of the app — the point of the section: turn a guide
|
* Cross-links into the rest of the app — the point of the section: turn a topic
|
||||||
* into a person, a place, or a sale.
|
* into a person, a place, or a sale.
|
||||||
*/
|
*/
|
||||||
export const DIY_ACTIONS: DiyCard[] = [
|
export const DIY_ACTIONS: DiyCard[] = [
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,59 @@
|
||||||
import { useSeoMeta } from '@unhead/react';
|
import { useSeoMeta } from '@unhead/react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Hammer, ExternalLink, ChevronRight } from 'lucide-react';
|
import { Hammer, ChevronRight } from 'lucide-react';
|
||||||
import { useTranslation } from '@/contexts/language';
|
import { useTranslation } from '@/contexts/language';
|
||||||
import { sanitizeUrl } from '@/lib/sanitizeUrl';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { DIY_CATEGORIES, DIY_CARDS, DIY_ACTIONS, type DiyCard } from '@/lib/diyGuides';
|
import { DIY_CATEGORIES, DIY_CARDS, DIY_ACTIONS, type DiyCard } from '@/lib/diyGuides';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DIY — curated resources for making your own boards, ramps and concrete spots,
|
* DIY — resources for making your own boards, ramps and concrete spots, with
|
||||||
* with cross-links into the rest of the app (find a builder, map a spot, sell).
|
* cross-links into the rest of the app (find a builder, map a spot, sell).
|
||||||
* Content lives in `src/lib/diyGuides.ts`.
|
* Content lives in `src/lib/diyGuides.ts`. Cards with no destination yet render
|
||||||
|
* unclickable with a "Coming soon" badge.
|
||||||
*/
|
*/
|
||||||
function DiyCardView({ card }: { card: DiyCard }) {
|
function DiyCardView({ card }: { card: DiyCard }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const external = !!card.url;
|
|
||||||
|
|
||||||
const body = (
|
const body = (
|
||||||
<>
|
<>
|
||||||
<span className="text-2xl leading-none" aria-hidden>{card.emoji}</span>
|
<span className="text-2xl leading-none" aria-hidden>{card.emoji}</span>
|
||||||
<span className="min-w-0 flex-1">
|
<span className="min-w-0 flex-1">
|
||||||
<span className="block font-bold text-white">{t(card.titleKey)}</span>
|
<span className="flex items-center gap-2">
|
||||||
|
<span className="font-bold text-white">{t(card.titleKey)}</span>
|
||||||
|
{card.comingSoon && (
|
||||||
|
<span className="flex-shrink-0 rounded-full border border-white/15 bg-white/5 px-2 py-0.5 text-[9px] font-black uppercase tracking-wider text-gray-400">
|
||||||
|
{t('diyUi.comingSoon')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
<span className="mt-0.5 block text-xs leading-relaxed text-gray-400">{t(card.blurbKey)}</span>
|
<span className="mt-0.5 block text-xs leading-relaxed text-gray-400">{t(card.blurbKey)}</span>
|
||||||
{card.source && (
|
|
||||||
<span className="mt-1 block text-[10px] text-gray-500">{t('diyUi.source')}: {card.source}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
{external
|
{card.to && <ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-gray-500" />}
|
||||||
? <ExternalLink className="mt-0.5 h-4 w-4 flex-shrink-0 text-gray-500" />
|
|
||||||
: <ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-gray-500" />}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const cls = cn(
|
const base = 'flex items-start gap-3 rounded-2xl border border-white/10 p-4 transition-colors';
|
||||||
'group flex items-start gap-3 rounded-2xl border border-white/10 bg-white/[0.03] p-4 transition-colors',
|
|
||||||
'hover:border-[#ccff00]/40 hover:bg-white/[0.06]',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (external) {
|
if (!card.to) {
|
||||||
|
// Coming soon — deliberately not a link.
|
||||||
return (
|
return (
|
||||||
<a href={sanitizeUrl(card.url)!} target="_blank" rel="noopener noreferrer" className={cls}>
|
<div
|
||||||
|
aria-disabled="true"
|
||||||
|
className={cn(base, 'cursor-default border-dashed bg-white/[0.015]')}
|
||||||
|
>
|
||||||
{body}
|
{body}
|
||||||
</a>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <Link to={card.to!} className={cls}>{body}</Link>;
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={card.to}
|
||||||
|
className={cn(base, 'bg-white/[0.03] hover:border-[#ccff00]/40 hover:bg-white/[0.06]')}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DiyPage() {
|
export default function DiyPage() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue