Commit graph

522 commits

Author SHA1 Message Date
SkateSpots
3b06e90d15 feat(trust): let the viewer choose the trust provider (NIP-85 kind 10040)
Settings -> Trust is now functional, not read-only. useSetTrustProvider
publishes a kind-10040 designation for a custom provider (npub or 64-hex +
wss relay, the five 30382:<type> rows) and can clear it to fall back to the
community provider. The card validates input, prompts the signer, and
invalidates the provider + assertion queries so scores refresh. Docs
updated.
2026-10-09 17:55:00 +07:00
SkateSpots
fd9316331b feat(trust): Settings -> Trust and name AGPL on the License page
- Settings -> Trust section (?section=trust, TrustSettingsCard): a
  show/hide-marks toggle (useTrustDisplay; Flagged is never hidden) and the
  active provider + relay (useTrustProvider), linked to /trust.
- TrustBadge respects the display preference.
- Name AGPL-3.0 on the License page by updating its existing copy
  (licenseLabel + body) in en/es.
- Settings + card i18n keys; /settings?section=trust added to the smoke
  list. Docs updated.
2026-10-09 17:45:56 +07:00
SkateSpots
d1fae2688c feat(trust): attribute the badge and add a /trust explainer
Legibility pass: a trust verdict has to be explainable and sourced.

- TrustBadge tooltips now attribute the claim (from your network / the
  community provider / the provider you chose) and link to /trust.
- New /trust page: the three states, why absence is not distrust, who
  computes it, presence-is-not-endorsement, and point-of-view. Public,
  linked from the badge; added to the smoke-test route list.
- New trustUi.* keys and four walletUi.trust attribution keys (en/es).
2026-10-09 17:30:00 +07:00
SkateSpots
2d1144bb7e feat(trust): batch provider reads, neutral direct follows, flagged notice
Second pass of TRUST_SCORE.md.

- useTrustAssertions now batches: every TrustBadge registers its subject into
  a shared per-viewer union and ONE query fetches the union (no per-card
  query), with a debounced refetch when a list grows. Same hook API.
- A direct follow (or self) is no longer shown as Verified - only a
  friend-of-friend or a provider assertion counts; Flagged is never
  suppressed.
- useUserReports reads public NIP-56 reports for an account, and
  TrustFlaggedNotice shows a flagged counterparty with the reporters grouped
  by the viewer's network plus Report / Dismiss. Wired into the listing
  seller card and the coach detail page.
- New walletUi.trust.* keys (en/es).
2026-10-09 17:17:11 +07:00
SkateSpots
fead9ca781 feat(trust): spec-correct NIP-85 consumer + Verified/Unknown/Flagged badge
First slice of TRUST_SCORE.md.

- lib/nip85.ts: parse kind-10040 provider designations, resolve a provider
  with a house fallback, parse kind-30382 assertions (rank/followers/
  reporters/muters/hops; absent hops means no path, absence is not
  distrust), and author-check the provider.
- hooks/useTrustAssertions.ts: batch read from the resolved provider,
  signature-verified via onlyVerified + the author check.
- lib/trustLadder.ts: three buckets and thresholds (flagged = trusted
  reporters >= 2; verified rank >= 2).
- hooks/useTrustBucket.ts: prefers a NIP-85 assertion (verify + flag),
  falls back to the local WoT ladder (verify only).
- TrustBadge.tsx: now renders Verified / Unknown / Flagged with glyph,
  colour and tooltip; Unknown hidden by default, Flagged always shown.
  Reaches the map/tour/event cards, marketplace card, listing detail and
  coach detail through the shared component.

Open follow-ups (in TRUST_SCORE.md): batch the bucket at list level, a
'who reported' breakdown, a provider-choice setting, alerts, confidence
scoring.
2026-10-09 17:07:34 +07:00
SkateSpots
486ffdb000 feat(map): add autocomplete suggestions to the search bar
Typing in the map search pill now opens a suggestions dropdown built
from the items that already match the active filters:
- Spots: title matches; selecting one flies to it and opens its sheet.
- Places: distinct city/region/country tokens split out of each item's
  location; selecting one sets the query to that token (e.g. spain ->
  Spain).

buildSearchSuggestions lives in lib (pure, testable) and
MapSearchSuggestions.tsx is presentational; MapPage owns the list and
keyboard nav (ArrowUp/Down, Enter, Escape). Adds mapPage.searchSuggestPlaces
(en/es only; other locales fall back to English).
2026-10-09 16:31:31 +07:00
SkateSpots
281a4c6ba5 feat(i18n): add search/filter to the language picker
The language list now has a search input in both the header dropdown
(pinned above the scrolling list) and the inline list used in the mobile
nav sheet. Filtering matches the English name, native name, or locale
code, and shows a localized empty state. Adds nav.searchLanguages and
nav.noLanguages to en/es (other locales fall back to English).
2026-10-08 18:16:13 +07:00
SkateSpots
bd9eaf9b5f fix(ui): make long dropdown menus scrollable
The shared DropdownMenuContent used overflow-hidden with no max-height,
so any menu taller than the viewport (e.g. the language picker with 22
locales) was clipped and could not scroll to its last items. Apply
Radix's available-height CSS var as max-height and switch to
overflow-y-auto (canonical shadcn pattern).
2026-10-08 18:10:55 +07:00
SkateSpots
d7a836e558 i18n(uk): complete Ukrainian (100%) + add to picker 2026-10-08 17:21:03 +07:00
SkateSpots
2e1167f8bd i18n(id): complete Indonesian (100%) + add to picker 2026-10-08 17:19:24 +07:00
SkateSpots
86427a0fb0 i18n(cs): complete Czech (100%) + add to picker 2026-10-08 17:18:34 +07:00
SkateSpots
2d656e5269 i18n(ro): complete Romanian (100%) + add to picker 2026-10-08 17:18:03 +07:00
SkateSpots
4acfa59e56 i18n(sv): complete Swedish (100%) + add to picker 2026-10-08 17:17:54 +07:00
SkateSpots
9998e7b8f6 i18n(el): complete Greek (100%) + add to picker 2026-10-08 17:17:43 +07:00
SkateSpots
09859bb7da i18n(vi): complete Vietnamese (100%) + add to picker 2026-10-08 17:17:35 +07:00
SkateSpots
69d04469f5 i18n(tr): complete Turkish (100%) + add to picker 2026-10-08 17:17:34 +07:00
SkateSpots
8e98a43949 i18n(zh-TW): complete Traditional Chinese (100%) + add to picker 2026-10-08 17:15:51 +07:00
SkateSpots
1c670d314b i18n(zh-CN): complete Simplified Chinese (100%) + add to picker 2026-10-08 16:00:33 +07:00
SkateSpots
1dd23c1549 i18n(ko): separate South and North Korean options, one flag each 2026-10-08 12:45:09 +07:00
SkateSpots
4e8999b84c i18n(ko): show both Korean flags in the language picker 2026-10-08 12:24:31 +07:00
SkateSpots
361f03c988 i18n(ru,ko): complete Russian + Korean translations; use North Korean flag for Korean 2026-10-07 23:56:20 +07:00
SkateSpots
1460b06277 i18n: lazy-load locales + register 8 new (pt fr de it nl pl ru ko) with fonts 2026-10-07 21:23:39 +07:00
SkateSpots
a3b7f311ce i18n: translate image alt/aria + generated export documents (last English literals) 2026-10-07 18:30:52 +07:00
SkateSpots
f7c9f9ef4c i18n: sweep hardcoded English (deckCost, becomeRoles, weather, skaterLabels, notify, misc) into 4 locales 2026-10-07 18:09:57 +07:00
SkateSpots
af0a138d1a fix(nav): inline language switcher in the mobile sheet (portaled dropdown was unclickable inside a dialog) 2026-10-07 17:54:32 +07:00
SkateSpots
ab05973ff0 i18n(ja): add Japanese as fourth locale (plumbing + font + batch 1 shell) 2026-10-07 17:21:18 +07:00
SkateSpots
94a6628faa i18n(es): translate page/component toasts (follow, delete, reports, sync, review queue) 2026-10-07 17:14:18 +07:00
SkateSpots
dd2062dc74 i18n(es): translate status presets, SetStatusDialog, nutzap + wallet toasts 2026-10-07 17:04:04 +07:00
SkateSpots
97c90c506a i18n(es): translate zap/NWC/delete/stash toasts + reuse feesUi keys for fee metadata 2026-10-07 16:55:33 +07:00
SkateSpots
b01bd22e96 i18n(es): translate SupportSkateSpots + hook toasts (follow, report, offline, publish) 2026-10-07 16:50:00 +07:00
SkateSpots
7e578fb7d9 i18n(es): translate shared component literals (camera, quality meter, entity picker, offline, comments) 2026-10-07 16:44:23 +07:00
SkateSpots
f58b19e310 chore(map): drop the "Supported by N" backer count from the map footer strip 2026-10-07 16:12:04 +07:00
SkateSpots
efd7e9608d feat(create-spot): pin map, progressive disclosure, mandatory photo+description, inline errors, draft autosave 2026-10-07 10:29:01 +07:00
SkateSpots
24322b3b1c feat(map): tap drops a pin + confirm card instead of jumping into create-spot 2026-10-07 10:26:36 +07:00
SkateSpots
b8c306125c i18n(th): batch 2 — map (mapUi + mapPage) + AGENTS note 2026-10-06 13:35:20 +07:00
SkateSpots
39572998a0 feat(clink): real bech32+TLV decode/validation (lib/clink.ts), spec-correct raw QR/copy, show decoded pricing; validate scanner 2026-10-06 11:16:20 +07:00
SkateSpots
9ca5f2a604 fix(audit UI/UX): 44px slabs, missing grid base cols, aria-labels, dark weather panel, contrast, sheet width, stretched-link SpotCard 2026-10-05 21:47:29 +07:00
SkateSpots
84f80d8330 fix(audit P2 money/auth): invoice amount checks (Pro/fee/pointer), verify embedded zap req, harden NIP-05 proof + Pro grant, sanitize cordn link 2026-10-05 21:32:58 +07:00
SkateSpots
f2bfbf4274 fix(newsletter): Monday reminder banner on mobile (bottom, above nav) not clipped by the map toolbar 2026-10-05 20:48:02 +07:00
SkateSpots
8a05aa3b69 chore(footer): remove the 'Supported by N' chip 2026-10-05 20:15:55 +07:00
SkateSpots
a9dcb5719c feat(supporters): per-row dates + 'updated', dedicated /donate page, extract shared support cards 2026-10-05 19:54:49 +07:00
SkateSpots
24e1369f6c feat(supporters): unified ranked wall, verifiable zaps, tier legend, impact link, pay-for detail, anonymous flag, map chip, OG meta 2026-10-05 18:55:16 +07:00
SkateSpots
1bdaa57aa3 feat(supporters): on-chain verify chips, real avatars/names, fiat total, footer count 2026-10-05 17:56:10 +07:00
SkateSpots
5baec5fb80 Deck calculator: fix the mobile layout (no horizontal scrolling)
The sheet tables carry 5-8 columns and were unusable on a phone: values were
truncated ("900" -> "90") and columns fell off the edge, and the cost-vs-quantity
table scrolled sideways.

- On max-width:639px the four data tables render as stacked cards (.ss-sheet-table
  in index.css): thead hidden, each td a label/value row (label via data-label +
  td::before), cells flex-wrap so a long label plus input never overflows.
- The cost-vs-quantity block is now a vertical list instead of a table, so it
  never scrolls horizontally.

Verified at 390px: values are full and each row is a readable card.
2026-10-05 15:30:12 +07:00
SkateSpots
bed0fb8e34 Deck calculator polish: collapse quote fields, duplicate rows, tax-aware hand-off
- The Client quote fields (15+ inputs) are now a collapsed <details>, so the
  always-visible Margins card stays short; the chevron rotates on open.
- CalcSection buttons set aria-expanded.
- Every equipment/material/process/packaging row gains a duplicate (Copy) action.
- The sticky summary chip shows the order total when quantity > 1 and uses the
  setup-inclusive cost.
- "List a deck at this price" passes a buyer-inclusive unit price (tax added on
  top unless tax is marked inclusive).
2026-10-05 12:45:23 +07:00
SkateSpots
475497b205 Deck calculator: Advanced mode — per-op rate/scrap, batch setup, tax, cost curve
A Simple <-> Advanced switch (not "Pro" — that name is Sasquatch Pro). Advanced
reveals four things a working shop needs:

- Per-operation labour rate (0 = shop rate) and per-operation scrap, which
  compounds with the general scrap into an effective waste %.
- One-off batch setup (hours + cost), amortised over the order quantity, so the
  unit price depends on qty; cost/board now uses the setup-inclusive unit cost.
- A tax / VAT line (inclusive or added on top), shown on the client quote as
  Subtotal / Tax / Total or "includes X%".
- A cost-vs-quantity curve (1/5/10/25/50/100 decks).

All fields round-trip through CSV. The sheet is merged over DEFAULT_CALC on read
and clampPct is NaN-safe, so an old saved sheet or preset can't produce NaN;
currency switching also converts the new money fields.
2026-10-05 12:38:58 +07:00
SkateSpots
df92399abf Deck calculator: collapsible sections, templates, batch/trade pricing, warnings
A+B+C pass on /deck-cost.

A - Declutter: each sheet section is now an accordion (CalcSection) with its
live per-board subtotal in the header (Labor open by default, Margins always
visible), plus quick-start templates (Hobby starter / Small shop) that prefll a
realistic sheet.

B - Batch & trade: a quantity field (unit price vs order total), a wholesale
markup giving a wholesale/unit price, profit per deck, and a breakeven
(fixed / profit) with an optional target-monthly-profit goal. The client quote
shows "x N" and the order total; the deposit is taken on the total.

C - Guards: an amber warning block for markup < 1x, wholesale >= retail, rows
with 0 boards, fixed costs with 0 boards/month, no materials, equipment+overhead
over 60% of cost, and equipment spread over a single board; number inputs clamp
negatives to 0.

New state round-trips through CSV (margins rows). The default sheet produces no
warnings.
2026-10-05 12:25:27 +07:00
SkateSpots
3b8fadad2c Deck calculator: make labels match the maths (burden, loaded rate, markup)
The arithmetic was already consistent, but three labels described something
other than what the code computes:
- "Overhead factor" multiplied labour only while a separate section held the
  real overhead, inviting double-counting -> "Labour burden (x)" + a hint.
- "Labor cost" showed wage x burden, i.e. the loaded rate, not a per-board
  cost -> "Loaded rate".
- "Profit margin (x)" was a markup -> "Markup (x)", with the implied gross
  margin % shown so the name matches the number.

Also: distinct board-count columns (Lifetime boards / Boards per order /
Boards per run) instead of three identical "# boards"; "Scrap / waste (%)".
PDF and Markdown exports use the same names. CSV machine keys are unchanged so
existing exports still round-trip.
2026-10-05 11:21:38 +07:00
SkateSpots
5b9616a86d Deck calculator quotes: payment terms, methods, deposit and optional Incoterms
QuoteProfile gains payment methods (multi-select chips), a deposit percent
(PDF splits "Deposit due now" / "Balance on completion"), free-text payment
terms, separate payment details (bank/PayPal), and an optional Incoterm
(EXW/FCA/DAP/DDP). The client PDF gains a payment block; the PNG shows the
deposit. Quote state is now merged over DEFAULT_QUOTE on read so a quote saved
before a new field existed cannot crash the tool.
2026-10-05 11:16:37 +07:00
SkateSpots
b247be13b0 DIY: parametric 2D deck designer with 1:1 printable template
New /diy/deck-designer tool. src/lib/deckDesign.ts holds the pure geometry
(outline, waist, rounded ends, kicks, concave) and the exporters; the component
renders a live top view, side profile and concave section with presets, saved
locally.

Exports: true-size SVG, a tiled 1:1 PDF (A4/Letter, split across sheets with
corner registration crosses), and an R12 DXF (outline polyline + 3/16" truck
holes + centreline, $INSUNITS=4). Truck patterns: New school 53.975x41.275 mm
and Old school 63.5x41.275 mm.

Deliberately 2D: this is the outline a builder traces and cuts, not a SK8CAD
clone (SK8CAD is proprietary; only its JSCAD engine is MIT, and we copied
nothing).
2026-10-05 11:16:34 +07:00