// Dashbread Pro & Add-ons — plans and Pro Module Add-ons from the catalog bundled with this version of // Dashbread (Dashbread Pro swaps in live prices from dashbread.com), with this site's current plan from the // license state Pro supplies. Core never holds a license key and contacts no other site. import { useEffect, useState } from 'react'; import { __, _x, sprintf } from '@wordpress/i18n'; import { cfg } from '../api.js'; import { Badge, Button, Card, Icon, Notice, Spinner } from '../ui.jsx'; async function call(path, opts = {}) { const res = await fetch(cfg.rest + path, { credentials: 'same-origin', headers: { 'X-WP-Nonce': cfg.nonce, 'Content-Type': 'application/json' }, ...opts }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data?.message || sprintf( /* translators: %s: HTTP status code */ __('Request failed (%s)', 'dashbread'), res.status )); return data; } // dashbread.com sells in US dollars; shown the way the site's language writes dollars ("$299", "299 $"). const money = (c) => { if (c == null) return ''; try { return new Intl.NumberFormat(document.documentElement.lang || undefined, { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(c / 100); } catch (e) { return '$' + Math.round(c / 100); } }; const term = (t) => (t === 'year' ? __('/year', 'dashbread') : t === 'month' ? __('/month', 'dashbread') : t === 'one_time' ? ' ' + __('one-time', 'dashbread') : ''); const sites = (p) => (/unlimited|lifetime|bundle/.test(p.slug) ? __('Unlimited sites', 'dashbread') : /5-site/.test(p.slug) ? __('5 sites', 'dashbread') : p.tier === 'core' ? __('Free forever', 'dashbread') : __('1 site', 'dashbread')); const tierOf = (price) => (price >= 12900 ? 'Tier A' : price >= 7900 ? 'Tier B' : 'Tier C'); // Visible labels for the tier keys above (the keys themselves stay untranslated). const TIER_LABELS = { 'Tier A': __('Tier A', 'dashbread'), 'Tier B': __('Tier B', 'dashbread'), 'Tier C': __('Tier C', 'dashbread') }; function Info({ p, onClose, d }) { useEffect(() => { const k = (e) => e.key === 'Escape' && onClose(); document.addEventListener('keydown', k); return () => document.removeEventListener('keydown', k); }, []); return (
{p.tagline}
}{d.bundled_date ? sprintf( /* translators: %s: date the bundled price list was made */ __('Prices as of %s.', 'dashbread'), d.bundled_date ) : __('Prices as of this release.', 'dashbread')} {__('Current prices are on', 'dashbread')} dashbread.com.
}{__('Your plan:', 'dashbread')} {cur.label}{d.dev &&
{d.pro ? __('Unlock Pro and the Pro Module Add-ons for this site. Everything is licensed on dashbread.com; buying updates this site automatically.', 'dashbread') : __('Unlock Pro and the Pro Module Add-ons for this site. Everything is licensed on dashbread.com; buying updates this site automatically once Dashbread Pro is installed.', 'dashbread')}
{bundle ? sprintf( /* translators: %s: bundle price per year, e.g. $299 */ __('Buy à la carte, or get them all with the bundle (%s/year).', 'dashbread'), money(bundle.price) ) : __('Buy à la carte, or get them all with the bundle.', 'dashbread')} {sprintf( /* translators: %s: total yearly price of all add-ons */ __('À la carte total: %s/year.', 'dashbread'), money(alaCarte) )} {__('Add-ons require Pro; nothing to install — they switch on when your license grants them.', 'dashbread')}
{groups.map(([tier, list]) => ({a.tagline}
{__('Full apps that run inside the shell — courses, help desks, work orders — ship as their own plugins and register pages, widgets, flyout tabs and graph sources through Core\'s hooks. Building one? See Help › For developers.', 'dashbread')}
{data.pages.length > 0 &&{p.path}