@withpotter/components 0.13.0-next.82 → 0.13.0-next.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/dashboard/home/widgets/activity-widget.js +50 -43
  2. package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
  3. package/dist/dashboard/home/widgets/home-widgets.js +3 -2
  4. package/dist/dashboard/home/widgets/home-widgets.js.map +1 -1
  5. package/dist/dashboard/home/widgets/host-widgets.d.ts +6 -0
  6. package/dist/dashboard/home/widgets/host-widgets.js +16 -0
  7. package/dist/dashboard/home/widgets/host-widgets.js.map +1 -0
  8. package/dist/dashboard/home/widgets/member-label.d.ts +11 -0
  9. package/dist/dashboard/home/widgets/member-label.js +10 -0
  10. package/dist/dashboard/home/widgets/member-label.js.map +1 -0
  11. package/dist/dashboard/home/widgets/middle-ellipsis.d.ts +5 -0
  12. package/dist/dashboard/home/widgets/middle-ellipsis.js +11 -0
  13. package/dist/dashboard/home/widgets/middle-ellipsis.js.map +1 -0
  14. package/dist/dashboard/home/widgets/money-widgets.js +9 -4
  15. package/dist/dashboard/home/widgets/money-widgets.js.map +1 -1
  16. package/dist/dashboard/home/widgets/registry.d.ts +6 -3
  17. package/dist/dashboard/home/widgets/registry.js +12 -2
  18. package/dist/dashboard/home/widgets/registry.js.map +1 -1
  19. package/dist/dashboard/home/widgets/revenue-series.d.ts +12 -0
  20. package/dist/dashboard/home/widgets/revenue-series.js +26 -0
  21. package/dist/dashboard/home/widgets/revenue-series.js.map +1 -0
  22. package/dist/dashboard/home/widgets/team-widget.js +2 -1
  23. package/dist/dashboard/home/widgets/team-widget.js.map +1 -1
  24. package/dist/dashboard/home/widgets/types.d.ts +13 -1
  25. package/dist/dashboard/home/widgets/types.js.map +1 -1
  26. package/dist/dashboard/home/widgets/use-home-layout.js +9 -4
  27. package/dist/dashboard/home/widgets/use-home-layout.js.map +1 -1
  28. package/dist/dashboard/home/widgets/website-widget.js +9 -1
  29. package/dist/dashboard/home/widgets/website-widget.js.map +1 -1
  30. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/dashboard/home/widgets/website-widget.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport {\n ArrowRight,\n ArrowUpRight,\n Eye,\n Globe,\n Lock,\n ShoppingBag,\n TrendingDown,\n TrendingUp,\n} from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { analyticsApi } from '@withpotter/lib/api/analytics';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { Panel, PanelAction } from '../panel';\n\n/**\n * The storefront, once there is one.\n *\n * WHY THIS WIDGET EXISTS. Deleting the \"Finish setting up\" panel only works if\n * every chore it held has somewhere better to be. Three of the four did:\n * \"add your first product\" belongs to Today, \"take payments\" is already in\n * Blocked because it stops money outright, and both are boxes whose empty state\n * the chore can occupy. \"Design your site\" and \"get a domain\" had nowhere — no\n * widget on the board was ever going to show their result — so the panel could\n * only have been relocated, not removed. This is the box those two fill.\n */\n/**\n * Views over the trailing week.\n *\n * IT FETCHES ITS OWN, a deliberate exception to the board's rule against\n * per-widget requests. That rule exists because six widgets reading money would\n * mean six copies of one request, which is why `MoneyBoardProvider` exists.\n * Nothing else on the board wants page views, so a provider for one consumer\n * would be ceremony — and `getHomeSummary`, which the money widgets share, does\n * not carry them.\n *\n * SEVEN DAYS, not the money board's window, and the two do not have to agree.\n * The money widgets share one range precisely so revenue-over-30 never appears\n * beside orders-over-7; this card is not part of that sentence.\n */\ninterface Traffic {\n /** Visits over the trailing seven days. */\n views: number;\n /** The same window, one week earlier, for the change. */\n prior: number;\n /** Distinct people, as opposed to page loads. */\n visitors: number;\n /** One point per day, oldest first. Drives the sparkline. */\n series: number[];\n /** Where most of them came from, and what share that was. */\n topSource?: { source: string; percentage: number };\n /** The page they looked at most. */\n topPage?: string;\n /** Sessions that reached a cart, and sessions that bought. */\n carts: number;\n orders: number;\n}\n\nfunction useWeeklyTraffic(enabled: boolean) {\n /*\n * Two windows, so the number can say which way it is going. A visit count\n * with no comparison is a fact you cannot act on: 40 is good or bad only\n * against last week.\n *\n * Through the query cache rather than an effect. `/analytics/dashboard` is a\n * twenty-odd-field payload and this asked for two of them on every mount of\n * the home, with nothing holding the answer — dev's double invoke made it\n * four requests for a card most merchants scroll past. Keyed by the day,\n * which is the resolution a week-long comparison has, so returning to the\n * home inside the same day costs nothing.\n */\n const iso = (d: Date) => d.toISOString().slice(0, 10);\n const today = iso(new Date());\n const windows = useMemo(() => {\n const end = new Date(`${today}T00:00:00.000Z`);\n const days = (n: number) => new Date(end.getTime() - n * 86_400_000);\n return {\n now: { startDate: iso(days(6)), endDate: today },\n before: { startDate: iso(days(13)), endDate: iso(days(7)) },\n };\n }, [today]);\n\n // Silent on failure, both of them: this card's job is the storefront, and a\n // failed traffic read must not turn a working Website card into an error.\n const { data: now } = useApiQuery(\n ['home-traffic', today],\n (slug) => analyticsApi.getDashboard(slug, windows.now),\n { enabled, retry: false },\n );\n const { data: before } = useApiQuery(\n ['home-traffic-previous', today],\n (slug) => analyticsApi.getDashboard(slug, windows.before),\n { enabled, retry: false },\n );\n\n return useMemo<Traffic | null>(() => {\n if (!now) return null;\n // All of this arrives in the SAME response as the view count, so the card\n // gets four more facts for no extra request. It would have been a waste to\n // fetch `overview` and throw the rest of the payload away.\n const sources = now.trafficSources ?? [];\n const pages = now.topPages ?? [];\n return {\n views: now.overview?.totalPageViews ?? 0,\n prior: before?.overview?.totalPageViews ?? 0,\n visitors: now.overview?.uniqueVisitors ?? 0,\n series: (now.pageViewsTimeSeries ?? []).map((p) => p.value ?? 0),\n topSource: sources.length\n ? { source: sources[0].source, percentage: sources[0].percentage }\n : undefined,\n topPage: pages.length ? pages[0].pageUrl : undefined,\n carts: now.funnel?.addToCart ?? 0,\n orders: now.funnel?.checkoutComplete ?? 0,\n };\n }, [now, before]);\n}\n\n/**\n * Week on week, and it stays silent when it cannot be honest.\n *\n * A first week has no prior to compare against, and rendering \"+100%\" against\n * zero is a number that means nothing dressed as a number that means something.\n * The analytics rule is that a figure ships only if it can change what someone\n * does next; a delta off no baseline cannot.\n */\nfunction TrafficDelta({ now, prior }: { now: number; prior: number }) {\n if (prior <= 0 || now === prior) return null;\n const pct = Math.round(((now - prior) / prior) * 100);\n const up = pct > 0;\n return (\n <span\n className={`ml-auto inline-flex items-center gap-0.5 text-sm font-medium tabular-nums ${\n up ? 'text-[var(--success)]' : 'text-[var(--text-tertiary)]'\n }`}\n >\n {up ? (\n <TrendingUp className=\"h-[11px] w-[11px]\" />\n ) : (\n <TrendingDown className=\"h-[11px] w-[11px]\" />\n )}\n {Math.abs(pct)}%\n </span>\n );\n}\n\n/**\n * Below this, a conversion rate is arithmetic on noise. One sale in nine visits\n * is 11.1%, which reads as a triumph and means nothing.\n */\nconst MIN_FOR_RATE = 30;\n\n/** One step of the funnel: the count, then what it counts. */\nfunction Step({ value, label }: { value: number; label: string }) {\n return (\n <span className=\"shrink-0\">\n <span className=\"font-medium tabular-nums text-[var(--text-secondary)]\">\n {value.toLocaleString()}\n </span>{' '}\n {label}\n </span>\n );\n}\n\n/**\n * A headline count and what it counts, for the pre-conversion state.\n *\n * THE GLYPH LEADS, and it is the one its own screen already uses: `Eye` is\n * what the Traffic tab marks views with, `ShoppingBag` is what the Activity\n * widget draws beside `order_placed`. Two counts sitting side by side are\n * told apart at a glance by shape long before anyone reads either label —\n * which is the whole reason to have them — and picking a third icon for a\n * concept the app has already settled would cost that.\n *\n * `self-center` because the row is baseline-aligned: the number and its label\n * share a baseline, which is the detail that makes them read as one phrase,\n * and a 16px glyph hung off that same line sits low against a 24px figure.\n */\nfunction Count({\n icon: Icon,\n value,\n label,\n}: {\n icon: LucideIcon;\n value: number;\n label: string;\n}) {\n return (\n <p className=\"flex items-baseline gap-2\">\n <Icon\n aria-hidden\n className=\"size-4 shrink-0 self-center text-[var(--text-muted)]\"\n />\n {/* 17px, not 20. Two of these sit side by side now, and at `text-2xl`\n they were the loudest thing in a card whose subject is the site\n above them — a week's visit count competing with the storefront it\n describes. One step down the scale (John, 2026-08-25) still reads as\n a figure against a 13px label. */}\n <span className=\"text-xl font-semibold tabular-nums tracking-[-0.02em] text-[var(--text-primary)]\">\n {value.toLocaleString()}\n </span>\n <span className=\"text-sm text-[var(--text-tertiary)]\">{label}</span>\n </p>\n );\n}\n\nexport function WebsiteWidget({\n storefrontUrl,\n hasCustomDomain,\n siteImage,\n businessName,\n}: {\n storefrontUrl?: string | null;\n hasCustomDomain: boolean;\n /** The business's own image. Absent is normal and draws a mark instead. */\n siteImage?: string | null;\n businessName?: string;\n}) {\n const { tenant } = useAuth();\n const isOpen = tenant?.status === 'active';\n const host = storefrontUrl?.replace(/^https?:\\/\\//, '').replace(/\\/$/, '');\n // Only asked for once the site is published: there is no traffic to a\n // storefront that does not exist, and asking would spend a request to be\n // told zero.\n const traffic = useWeeklyTraffic(!!storefrontUrl);\n\n return (\n <Panel\n title=\"Website\"\n className=\"h-full\"\n bodyClassName=\"flex flex-col min-h-0\"\n >\n {/*\n IT IS A LINK PREVIEW — the card that appears when somebody pastes your\n address into WhatsApp or Slack.\n\n Two designs came before this and both did too much. The first was four\n unrelated rows: a pill, a number, a link, a button. The second made the\n card a browser window — chrome, viewport, the image standing upright\n inside it — which looked like a site but answered nothing, because a\n merchant does not need to be shown that websites live in browsers.\n\n The link preview is the right frame because it is the one a merchant\n already knows and the one that matters: it is how the business appears\n the moment anyone shares it. Horizontal, image left, name and address\n right — so the card is literally a rehearsal of the thing it describes.\n\n Three facts, in the order they are wanted: what it looks like shared,\n where it lives, and whether anyone came. Nothing else.\n */}\n <a\n href={storefrontUrl ?? '#'}\n target=\"_blank\"\n rel=\"noreferrer\"\n /* THE PREVIEW IS GREY, not the panel's own fill. It carried\n `surface-interactive`, whose background is `--card-bg` — the exact\n fill of the `Panel` it sits inside — so the only thing separating\n the two was a hairline, and the card read as having no background at\n all. `--bg-muted` is the grey either side of that: #efefef on white,\n #202023 on the dark ground. The chip stands ON the panel, which is\n what a thing you can click should look like. */\n className=\"surface-interactive group/site flex items-stretch gap-0 overflow-hidden bg-[var(--bg-muted)]\"\n >\n {/* Square, and fixed. A share card's thumbnail never grows with the\n title; letting it would make two businesses' cards different shapes\n for no reason a reader could name. */}\n <span className=\"grid h-[68px] w-[68px] shrink-0 place-items-center overflow-hidden bg-[var(--bg-base)]\">\n {siteImage ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={siteImage}\n alt=\"\"\n className=\"h-full w-full object-cover transition-transform duration-[var(--motion-slow)] ease-out group-hover/site:scale-105\"\n />\n ) : (\n <Globe className=\"size-5 text-[var(--text-muted)]\"\n strokeWidth={1.5}\n />\n )}\n </span>\n\n <span className=\"flex min-w-0 flex-1 flex-col justify-center gap-0.5 px-3 py-2\">\n <span className=\"truncate text-md font-medium text-[var(--text-primary)]\">\n {businessName ?? 'Your website'}\n </span>\n <span className=\"flex min-w-0 items-center gap-1\">\n {isOpen && host ? (\n <>\n <Lock\n aria-hidden\n className=\"h-[9px] w-[9px] shrink-0 text-[var(--success)]\"\n />\n <span className=\"min-w-0 truncate text-sm text-[var(--text-tertiary)]\">\n {host}\n </span>\n </>\n ) : (\n /* \"Not published yet\" was a FACT with no way to act on it, and\n the only control that could open the site was a modal that\n appeared once and never again. A merchant who dismissed it had\n no route to publishing from the dashboard at all.\n\n Shopify's own onboarding guidance is dismissible cards rather\n than modals, and this is already the card about the site. So\n the state and the act that resolves it are in one place. */\n /* The address exists before the site is open, and showing it\n under a padlock said the opposite of the truth. While it is\n shut the line says so; the address is no use until it is. */\n <span className=\"min-w-0 truncate text-sm text-[var(--text-tertiary)]\">\n Not open yet\n </span>\n )}\n </span>\n </span>\n\n <span className=\"grid w-8 shrink-0 place-items-center text-[var(--text-muted)] transition-colors group-hover/site:text-[var(--text-primary)]\">\n <ArrowUpRight />\n </span>\n </a>\n\n {/* NO CONTROL UNDER THE PREVIEW.\n\n There was an \"Open my site\" button here, then — after John cut it —\n the same act surviving as an \"Open it now\" text link. Both are gone\n on his call, 2026-08-25. The card's job is to SHOW the storefront;\n \"Not open yet\" is the line that states it, and publishing is a\n once-ever act that was holding a permanent row on a board where\n every other card leads with content. Opening the site lives on the\n Website hub, which the preview itself links into. */}\n\n {/* THE QUESTION TRAFFIC IS ACTUALLY ASKING.\n A visit count is a vanity number: 480 is good or bad depending\n entirely on what the 480 did, and a merchant cannot act on it. What\n they can act on is the CONVERSION — the share of people who came and\n bought — because a high number says do more of whatever brought them\n and a low one says the site is not closing.\n\n So conversion leads and visits are the context under it, which is the\n inverse of what this card did. The funnel arrives in the same request\n as the view count and was being thrown away.\n\n Only when there is enough traffic to divide by. A rate off nine\n visits is noise with a decimal point, and reporting \"11.1%\" from one\n sale in nine would be worse than reporting nothing. */}\n {traffic && traffic.views >= MIN_FOR_RATE ? (\n <div className=\"mt-3\">\n <p className=\"flex items-baseline gap-2\">\n <span className=\"text-3xl font-semibold leading-none tracking-[-0.02em] tabular-nums text-[var(--text-primary)]\">\n {((traffic.orders / traffic.views) * 100).toFixed(1)}%\n </span>\n <span className=\"text-sm text-[var(--text-tertiary)]\">bought</span>\n <TrafficDelta now={traffic.views} prior={traffic.prior} />\n </p>\n\n {/* The drop-off, as three counts on one line. Where people leave is\n the whole diagnosis: visits high and carts low is a catalogue\n problem, carts high and orders low is a checkout problem, and one\n rate alone cannot tell those apart. */}\n <p className=\"mt-2 flex items-center gap-2 text-sm text-[var(--text-tertiary)]\">\n <Step value={traffic.views} label=\"visits\" />\n <ArrowRight aria-hidden className=\"h-[11px] w-[11px] shrink-0\" />\n <Step value={traffic.carts} label=\"carts\" />\n <ArrowRight aria-hidden className=\"h-[11px] w-[11px] shrink-0\" />\n <Step value={traffic.orders} label=\"orders\" />\n </p>\n\n {traffic.topSource && (\n <p className=\"mt-2 truncate text-sm text-[var(--text-muted)]\">\n Most from{' '}\n <span className=\"font-medium text-[var(--text-secondary)]\">\n {traffic.topSource.source}\n </span>{' '}\n · {Math.round(traffic.topSource.percentage)}%\n </p>\n )}\n </div>\n ) : (\n traffic && (\n // Too little traffic to divide by, so it reports the count and says\n // so rather than dressing a tiny sample as a rate.\n <div className=\"mt-3\">\n {/* TWO COUNTS, not one. Visits alone says people arrived and\n stops there; the merchant's actual question underneath it is\n whether any of them bought. Below `MIN_FOR_RATE` the two\n cannot honestly be divided into a percentage — that is what\n the line beneath says — but they can both be stated, and a\n week of 14 visits and 2 orders is a different week from 14\n and 0 in every way that matters. Same window, same request:\n `checkoutComplete` off the funnel already in the payload. */}\n {/* NO \"not enough traffic to measure conversion\" UNDER THIS.\n Cut on John's call, 2026-08-25. It explained an absence — why\n the card was not showing a percentage — to someone who had not\n asked for one and could not act on the answer. The counts are\n the card's whole claim at this stage; a line apologising for\n the number it is not showing makes a week of real visits read\n as a shortfall. The rate appears on its own once there is\n enough traffic to divide by, and needs no trailer before then. */}\n <div className=\"flex flex-wrap items-baseline gap-x-6 gap-y-1\">\n <Count\n icon={Eye}\n value={traffic.views}\n label={`${traffic.views === 1 ? 'visit' : 'visits'} this week`}\n />\n <Count\n icon={ShoppingBag}\n value={traffic.orders}\n label={traffic.orders === 1 ? 'order' : 'orders'}\n />\n </div>\n </div>\n )\n )}\n\n {/* No \"View site\" button.\n The share-card preview above IS the link — it is an `<a>` to the\n storefront and always has been — so a button underneath was a second\n control for the same click, on a card whose whole argument is that it\n shows you the thing rather than describing it. John, looking at it:\n *\"the open site button on the website card is not needed, people can\n legit click on section that shows the site\"*.\n\n \"Add your own domain\" stays, and is not the same case: it is the one\n job on this card the preview cannot do. */}\n {!hasCustomDomain && (\n <div className=\"mt-auto pt-3\">\n <PanelAction href=\"/dashboard/business/domains\">\n Add your own domain\n </PanelAction>\n </div>\n )}\n </Panel>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/dashboard/home/widgets/website-widget.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport {\n ArrowRight,\n ArrowUpRight,\n Eye,\n Globe,\n Lock,\n ShoppingBag,\n TrendingDown,\n TrendingUp,\n} from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { analyticsApi } from '@withpotter/lib/api/analytics';\nimport { useApiQuery } from '@withpotter/hooks/use-api-query';\nimport { middleEllipsis } from './middle-ellipsis';\nimport { Panel, PanelAction } from '../panel';\n\n/**\n * The storefront, once there is one.\n *\n * WHY THIS WIDGET EXISTS. Deleting the \"Finish setting up\" panel only works if\n * every chore it held has somewhere better to be. Three of the four did:\n * \"add your first product\" belongs to Today, \"take payments\" is already in\n * Blocked because it stops money outright, and both are boxes whose empty state\n * the chore can occupy. \"Design your site\" and \"get a domain\" had nowhere — no\n * widget on the board was ever going to show their result — so the panel could\n * only have been relocated, not removed. This is the box those two fill.\n */\n/**\n * Views over the trailing week.\n *\n * IT FETCHES ITS OWN, a deliberate exception to the board's rule against\n * per-widget requests. That rule exists because six widgets reading money would\n * mean six copies of one request, which is why `MoneyBoardProvider` exists.\n * Nothing else on the board wants page views, so a provider for one consumer\n * would be ceremony — and `getHomeSummary`, which the money widgets share, does\n * not carry them.\n *\n * SEVEN DAYS, not the money board's window, and the two do not have to agree.\n * The money widgets share one range precisely so revenue-over-30 never appears\n * beside orders-over-7; this card is not part of that sentence.\n */\ninterface Traffic {\n /** Visits over the trailing seven days. */\n views: number;\n /** The same window, one week earlier, for the change. */\n prior: number;\n /** Distinct people, as opposed to page loads. */\n visitors: number;\n /** One point per day, oldest first. Drives the sparkline. */\n series: number[];\n /** Where most of them came from, and what share that was. */\n topSource?: { source: string; percentage: number };\n /** The page they looked at most. */\n topPage?: string;\n /** Sessions that reached a cart, and sessions that bought. */\n carts: number;\n orders: number;\n}\n\nfunction useWeeklyTraffic(enabled: boolean) {\n /*\n * Two windows, so the number can say which way it is going. A visit count\n * with no comparison is a fact you cannot act on: 40 is good or bad only\n * against last week.\n *\n * Through the query cache rather than an effect. `/analytics/dashboard` is a\n * twenty-odd-field payload and this asked for two of them on every mount of\n * the home, with nothing holding the answer — dev's double invoke made it\n * four requests for a card most merchants scroll past. Keyed by the day,\n * which is the resolution a week-long comparison has, so returning to the\n * home inside the same day costs nothing.\n */\n const iso = (d: Date) => d.toISOString().slice(0, 10);\n const today = iso(new Date());\n const windows = useMemo(() => {\n const end = new Date(`${today}T00:00:00.000Z`);\n const days = (n: number) => new Date(end.getTime() - n * 86_400_000);\n return {\n now: { startDate: iso(days(6)), endDate: today },\n before: { startDate: iso(days(13)), endDate: iso(days(7)) },\n };\n }, [today]);\n\n // Silent on failure, both of them: this card's job is the storefront, and a\n // failed traffic read must not turn a working Website card into an error.\n const { data: now } = useApiQuery(\n ['home-traffic', today],\n (slug) => analyticsApi.getDashboard(slug, windows.now),\n { enabled, retry: false },\n );\n const { data: before } = useApiQuery(\n ['home-traffic-previous', today],\n (slug) => analyticsApi.getDashboard(slug, windows.before),\n { enabled, retry: false },\n );\n\n return useMemo<Traffic | null>(() => {\n if (!now) return null;\n // All of this arrives in the SAME response as the view count, so the card\n // gets four more facts for no extra request. It would have been a waste to\n // fetch `overview` and throw the rest of the payload away.\n const sources = now.trafficSources ?? [];\n const pages = now.topPages ?? [];\n return {\n views: now.overview?.totalPageViews ?? 0,\n prior: before?.overview?.totalPageViews ?? 0,\n visitors: now.overview?.uniqueVisitors ?? 0,\n series: (now.pageViewsTimeSeries ?? []).map((p) => p.value ?? 0),\n topSource: sources.length\n ? { source: sources[0].source, percentage: sources[0].percentage }\n : undefined,\n topPage: pages.length ? pages[0].pageUrl : undefined,\n carts: now.funnel?.addToCart ?? 0,\n orders: now.funnel?.checkoutComplete ?? 0,\n };\n }, [now, before]);\n}\n\n/**\n * Week on week, and it stays silent when it cannot be honest.\n *\n * A first week has no prior to compare against, and rendering \"+100%\" against\n * zero is a number that means nothing dressed as a number that means something.\n * The analytics rule is that a figure ships only if it can change what someone\n * does next; a delta off no baseline cannot.\n */\nfunction TrafficDelta({ now, prior }: { now: number; prior: number }) {\n if (prior <= 0 || now === prior) return null;\n const pct = Math.round(((now - prior) / prior) * 100);\n const up = pct > 0;\n return (\n <span\n className={`ml-auto inline-flex items-center gap-0.5 text-sm font-medium tabular-nums ${\n up ? 'text-[var(--success)]' : 'text-[var(--text-tertiary)]'\n }`}\n >\n {up ? (\n <TrendingUp className=\"h-[11px] w-[11px]\" />\n ) : (\n <TrendingDown className=\"h-[11px] w-[11px]\" />\n )}\n {Math.abs(pct)}%\n </span>\n );\n}\n\n/**\n * Below this, a conversion rate is arithmetic on noise. One sale in nine visits\n * is 11.1%, which reads as a triumph and means nothing.\n */\nconst MIN_FOR_RATE = 30;\n\n/** One step of the funnel: the count, then what it counts. */\nfunction Step({ value, label }: { value: number; label: string }) {\n return (\n <span className=\"shrink-0\">\n <span className=\"font-medium tabular-nums text-[var(--text-secondary)]\">\n {value.toLocaleString()}\n </span>{' '}\n {label}\n </span>\n );\n}\n\n/**\n * A headline count and what it counts, for the pre-conversion state.\n *\n * THE GLYPH LEADS, and it is the one its own screen already uses: `Eye` is\n * what the Traffic tab marks views with, `ShoppingBag` is what the Activity\n * widget draws beside `order_placed`. Two counts sitting side by side are\n * told apart at a glance by shape long before anyone reads either label —\n * which is the whole reason to have them — and picking a third icon for a\n * concept the app has already settled would cost that.\n *\n * `self-center` because the row is baseline-aligned: the number and its label\n * share a baseline, which is the detail that makes them read as one phrase,\n * and a 16px glyph hung off that same line sits low against a 24px figure.\n */\nfunction Count({\n icon: Icon,\n value,\n label,\n}: {\n icon: LucideIcon;\n value: number;\n label: string;\n}) {\n return (\n <p className=\"flex items-baseline gap-2\">\n <Icon\n aria-hidden\n className=\"size-4 shrink-0 self-center text-[var(--text-muted)]\"\n />\n {/* 17px, not 20. Two of these sit side by side now, and at `text-2xl`\n they were the loudest thing in a card whose subject is the site\n above them — a week's visit count competing with the storefront it\n describes. One step down the scale (John, 2026-08-25) still reads as\n a figure against a 13px label. */}\n <span className=\"text-xl font-semibold tabular-nums tracking-[-0.02em] text-[var(--text-primary)]\">\n {value.toLocaleString()}\n </span>\n <span className=\"text-sm text-[var(--text-tertiary)]\">{label}</span>\n </p>\n );\n}\n\nexport function WebsiteWidget({\n storefrontUrl,\n hasCustomDomain,\n siteImage,\n businessName,\n}: {\n storefrontUrl?: string | null;\n hasCustomDomain: boolean;\n /** The business's own image. Absent is normal and draws a mark instead. */\n siteImage?: string | null;\n businessName?: string;\n}) {\n const { tenant } = useAuth();\n const isOpen = tenant?.status === 'active';\n const host = storefrontUrl?.replace(/^https?:\\/\\//, '').replace(/\\/$/, '');\n // Only asked for once the site is published: there is no traffic to a\n // storefront that does not exist, and asking would spend a request to be\n // told zero.\n const traffic = useWeeklyTraffic(!!storefrontUrl);\n\n return (\n <Panel\n title=\"Website\"\n className=\"h-full\"\n bodyClassName=\"flex flex-col min-h-0\"\n >\n {/*\n IT IS A LINK PREVIEW — the card that appears when somebody pastes your\n address into WhatsApp or Slack.\n\n Two designs came before this and both did too much. The first was four\n unrelated rows: a pill, a number, a link, a button. The second made the\n card a browser window — chrome, viewport, the image standing upright\n inside it — which looked like a site but answered nothing, because a\n merchant does not need to be shown that websites live in browsers.\n\n The link preview is the right frame because it is the one a merchant\n already knows and the one that matters: it is how the business appears\n the moment anyone shares it. Horizontal, image left, name and address\n right — so the card is literally a rehearsal of the thing it describes.\n\n Three facts, in the order they are wanted: what it looks like shared,\n where it lives, and whether anyone came. Nothing else.\n */}\n <a\n href={storefrontUrl ?? '#'}\n target=\"_blank\"\n rel=\"noreferrer\"\n /* THE PREVIEW IS GREY, not the panel's own fill. It carried\n `surface-interactive`, whose background is `--card-bg` — the exact\n fill of the `Panel` it sits inside — so the only thing separating\n the two was a hairline, and the card read as having no background at\n all. `--bg-muted` is the grey either side of that: #efefef on white,\n #202023 on the dark ground. The chip stands ON the panel, which is\n what a thing you can click should look like. */\n className=\"surface-interactive group/site flex items-stretch gap-0 overflow-hidden bg-[var(--bg-muted)]\"\n >\n {/* Square, and fixed. A share card's thumbnail never grows with the\n title; letting it would make two businesses' cards different shapes\n for no reason a reader could name. */}\n <span className=\"grid h-[68px] w-[68px] shrink-0 place-items-center overflow-hidden bg-[var(--bg-base)]\">\n {siteImage ? (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={siteImage}\n alt=\"\"\n className=\"h-full w-full object-cover transition-transform duration-[var(--motion-slow)] ease-out group-hover/site:scale-105\"\n />\n ) : (\n <Globe className=\"size-5 text-[var(--text-muted)]\"\n strokeWidth={1.5}\n />\n )}\n </span>\n\n <span className=\"flex min-w-0 flex-1 flex-col justify-center gap-0.5 px-3 py-2\">\n <span className=\"truncate text-md font-medium text-[var(--text-primary)]\">\n {businessName ?? 'Your website'}\n </span>\n <span className=\"flex min-w-0 items-center gap-1\">\n {isOpen && host ? (\n <>\n <Lock\n aria-hidden\n className=\"h-[9px] w-[9px] shrink-0 text-[var(--success)]\"\n />\n <span\n title={host}\n className=\"min-w-0 truncate text-sm text-[var(--text-tertiary)]\"\n >\n {middleEllipsis(host)}\n </span>\n </>\n ) : (\n /* \"Not published yet\" was a FACT with no way to act on it, and\n the only control that could open the site was a modal that\n appeared once and never again. A merchant who dismissed it had\n no route to publishing from the dashboard at all.\n\n Shopify's own onboarding guidance is dismissible cards rather\n than modals, and this is already the card about the site. So\n the state and the act that resolves it are in one place. */\n /* The address exists before the site is open, and showing it\n under a padlock said the opposite of the truth. While it is\n shut the line says so; the address is no use until it is. */\n <span className=\"min-w-0 truncate text-sm text-[var(--text-tertiary)]\">\n Not open yet\n </span>\n )}\n </span>\n </span>\n\n <span className=\"grid w-8 shrink-0 place-items-center text-[var(--text-muted)] transition-colors group-hover/site:text-[var(--text-primary)]\">\n <ArrowUpRight />\n </span>\n </a>\n\n {/* NO CONTROL UNDER THE PREVIEW.\n\n There was an \"Open my site\" button here, then — after John cut it —\n the same act surviving as an \"Open it now\" text link. Both are gone\n on his call, 2026-08-25. The card's job is to SHOW the storefront;\n \"Not open yet\" is the line that states it, and publishing is a\n once-ever act that was holding a permanent row on a board where\n every other card leads with content. Opening the site lives on the\n Website hub, which the preview itself links into. */}\n\n {/* THE QUESTION TRAFFIC IS ACTUALLY ASKING.\n A visit count is a vanity number: 480 is good or bad depending\n entirely on what the 480 did, and a merchant cannot act on it. What\n they can act on is the CONVERSION — the share of people who came and\n bought — because a high number says do more of whatever brought them\n and a low one says the site is not closing.\n\n So conversion leads and visits are the context under it, which is the\n inverse of what this card did. The funnel arrives in the same request\n as the view count and was being thrown away.\n\n Only when there is enough traffic to divide by. A rate off nine\n visits is noise with a decimal point, and reporting \"11.1%\" from one\n sale in nine would be worse than reporting nothing. */}\n {traffic && traffic.views >= MIN_FOR_RATE ? (\n <div className=\"mt-3\">\n <p className=\"flex items-baseline gap-2\">\n <span className=\"text-3xl font-semibold leading-none tracking-[-0.02em] tabular-nums text-[var(--text-primary)]\">\n {((traffic.orders / traffic.views) * 100).toFixed(1)}%\n </span>\n <span className=\"text-sm text-[var(--text-tertiary)]\">bought</span>\n <TrafficDelta now={traffic.views} prior={traffic.prior} />\n </p>\n\n {/* The drop-off, as three counts on one line. Where people leave is\n the whole diagnosis: visits high and carts low is a catalogue\n problem, carts high and orders low is a checkout problem, and one\n rate alone cannot tell those apart. */}\n <p className=\"mt-2 flex items-center gap-2 text-sm text-[var(--text-tertiary)]\">\n <Step value={traffic.views} label=\"visits\" />\n <ArrowRight aria-hidden className=\"h-[11px] w-[11px] shrink-0\" />\n <Step value={traffic.carts} label=\"carts\" />\n <ArrowRight aria-hidden className=\"h-[11px] w-[11px] shrink-0\" />\n <Step value={traffic.orders} label=\"orders\" />\n </p>\n\n {traffic.topSource && (\n <p className=\"mt-2 truncate text-sm text-[var(--text-muted)]\">\n Most from{' '}\n <span className=\"font-medium text-[var(--text-secondary)]\">\n {traffic.topSource.source}\n </span>{' '}\n · {Math.round(traffic.topSource.percentage)}%\n </p>\n )}\n </div>\n ) : (\n traffic && (\n // Too little traffic to divide by, so it reports the count and says\n // so rather than dressing a tiny sample as a rate.\n <div className=\"mt-3\">\n {/* TWO COUNTS, not one. Visits alone says people arrived and\n stops there; the merchant's actual question underneath it is\n whether any of them bought. Below `MIN_FOR_RATE` the two\n cannot honestly be divided into a percentage — that is what\n the line beneath says — but they can both be stated, and a\n week of 14 visits and 2 orders is a different week from 14\n and 0 in every way that matters. Same window, same request:\n `checkoutComplete` off the funnel already in the payload. */}\n {/* NO \"not enough traffic to measure conversion\" UNDER THIS.\n Cut on John's call, 2026-08-25. It explained an absence — why\n the card was not showing a percentage — to someone who had not\n asked for one and could not act on the answer. The counts are\n the card's whole claim at this stage; a line apologising for\n the number it is not showing makes a week of real visits read\n as a shortfall. The rate appears on its own once there is\n enough traffic to divide by, and needs no trailer before then. */}\n <div className=\"flex flex-wrap items-baseline gap-x-6 gap-y-1\">\n <Count\n icon={Eye}\n value={traffic.views}\n label={`${traffic.views === 1 ? 'visit' : 'visits'} this week`}\n />\n <Count\n icon={ShoppingBag}\n value={traffic.orders}\n label={traffic.orders === 1 ? 'order' : 'orders'}\n />\n </div>\n </div>\n )\n )}\n\n {/* No \"View site\" button.\n The share-card preview above IS the link — it is an `<a>` to the\n storefront and always has been — so a button underneath was a second\n control for the same click, on a card whose whole argument is that it\n shows you the thing rather than describing it. John, looking at it:\n *\"the open site button on the website card is not needed, people can\n legit click on section that shows the site\"*.\n\n \"Add your own domain\" stays, and is not the same case: it is the one\n job on this card the preview cannot do. */}\n {!hasCustomDomain && (\n <div className=\"mt-auto pt-3\">\n <PanelAction href=\"/dashboard/business/domains\">\n Add your own domain\n </PanelAction>\n </div>\n )}\n </Panel>\n );\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withpotter/components",
3
- "version": "0.13.0-next.82",
3
+ "version": "0.13.0-next.84",
4
4
  "description": "Box dashboard feature components: editors, mobile screens, pickers, and composite UI built on @withpotter/ui-kit.",
5
5
  "license": "UNLICENSED",
6
6
  "sideEffects": [
@@ -50,9 +50,9 @@
50
50
  "@tiptap/react": "^3.22.5",
51
51
  "@tiptap/starter-kit": "^3.22.5",
52
52
  "@tiptap/suggestion": "^3.22.5",
53
- "@withpotter/hooks": "^0.13.0-next.82",
54
- "@withpotter/lib": "^0.13.0-next.82",
55
- "@withpotter/ui-kit": "^0.13.0-next.82",
53
+ "@withpotter/hooks": "^0.13.0-next.84",
54
+ "@withpotter/lib": "^0.13.0-next.84",
55
+ "@withpotter/ui-kit": "^0.13.0-next.84",
56
56
  "@xyflow/react": "^12.11.2",
57
57
  "dompurify": "^3.3.1",
58
58
  "jsqr": "1.4.0",
@@ -77,7 +77,7 @@
77
77
  "typescript": "^5"
78
78
  },
79
79
  "publishConfig": {
80
- "registry": "https://registry.npmjs.org",
80
+ "registry": "https://registry.npmjs.org/",
81
81
  "access": "restricted"
82
82
  }
83
83
  }