@burdenoff/microfe-subscriberbot 2026.706.1 → 2026.706.3
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.
- package/dist/subscriberbot/SubscriberBotRoutes.js +14 -6
- package/dist/subscriberbot/SubscriberBotRoutes.js.map +1 -1
- package/dist/subscriberbot/components/Modal.js +51 -0
- package/dist/subscriberbot/components/Modal.js.map +1 -0
- package/dist/subscriberbot/components/index.js +7 -0
- package/dist/subscriberbot/components/viz.js +196 -0
- package/dist/subscriberbot/components/viz.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +364 -161
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/AssetVaultPage.js +182 -0
- package/dist/subscriberbot/pages/AssetVaultPage.js.map +1 -0
- package/dist/subscriberbot/pages/DashboardPage.js +272 -199
- package/dist/subscriberbot/pages/DashboardPage.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +682 -126
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InsightsPage.js +220 -0
- package/dist/subscriberbot/pages/InsightsPage.js.map +1 -0
- package/dist/subscriberbot/pages/InvoicesPage.js +592 -81
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +344 -169
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +401 -116
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +185 -93
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +735 -135
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js +217 -153
- package/dist/subscriberbot/pages/providers/ProviderNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +216 -111
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +902 -250
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js +242 -192
- package/dist/subscriberbot/pages/relationships/RelationshipNewPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +174 -131
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderNewPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/providers/ProviderNewPage.tsx"],"sourcesContent":["/**\n * Create provider\n * @module subscriberbot/pages/providers\n *\n * Add a provider to the registry. Writes `subCreateProvider`.\n */\n\nimport { useState } from 'react';\nimport { ArrowLeft } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useCreateProvider } from '../../hooks/useSubscriberBotApi';\nimport type { SubCreateProviderInput, SubProviderCategory } from '../../types';\n\nconst CATEGORIES: SubProviderCategory[] = [\n 'SAAS',\n 'AI_SERVICE',\n 'STREAMING',\n 'NEWSLETTER',\n 'NEWS_DIGEST',\n 'MEMBERSHIP',\n 'COMMUNITY',\n 'EDUCATION',\n 'FINANCIAL',\n 'INSURANCE',\n 'TELECOM',\n 'UTILITY',\n 'ASSOCIATION',\n 'LICENSE',\n 'CONTRACT',\n 'OTHER',\n];\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nexport function ProviderNewPage() {\n const { navigateTo } = useSubscriberBot();\n const create = useCreateProvider();\n\n const [name, setName] = useState('');\n const [category, setCategory] = useState<SubProviderCategory>('SAAS');\n const [description, setDescription] = useState('');\n const [websiteUrl, setWebsiteUrl] = useState('');\n const [region, setRegion] = useState('');\n const [industry, setIndustry] = useState('');\n const [tags, setTags] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim()) {\n setFormError('A provider name is required.');\n return;\n }\n const parsedTags = tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n const input: SubCreateProviderInput = {\n name: name.trim(),\n category,\n ...(description.trim() ? { description: description.trim() } : {}),\n ...(websiteUrl.trim() ? { websiteUrl: websiteUrl.trim() } : {}),\n ...(region.trim() ? { region: region.trim() } : {}),\n ...(industry.trim() ? { industry: industry.trim() } : {}),\n ...(parsedTags.length ? { tags: parsedTags } : {}),\n };\n create.mutate(input, {\n onSuccess: (provider) => navigateTo(`/providers/${provider.id}`),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create provider.'),\n });\n };\n\n return (\n <PageLayout\n title=\"Add provider\"\n description=\"Register a new provider in the registry\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n Add a provider that isn't in the registry yet so you can attach relationships, plans and\n assets to it.\n </PagePurpose>\n\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-5\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n\n <div>\n <label htmlFor=\"prov-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"prov-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. OpenAI, Netflix, AWS\"\n className={inputCls}\n required\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"prov-category\" className={labelCls}>\n Category\n </label>\n <select\n id=\"prov-category\"\n value={category}\n onChange={(e) => setCategory(e.target.value as SubProviderCategory)}\n className={inputCls}\n >\n {CATEGORIES.map((c) => (\n <option key={c} value={c}>\n {c.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"prov-website\" className={labelCls}>\n Website\n </label>\n <input\n id=\"prov-website\"\n type=\"url\"\n value={websiteUrl}\n onChange={(e) => setWebsiteUrl(e.target.value)}\n placeholder=\"https://…\"\n className={inputCls}\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"prov-region\" className={labelCls}>\n Region\n </label>\n <input\n id=\"prov-region\"\n type=\"text\"\n value={region}\n onChange={(e) => setRegion(e.target.value)}\n placeholder=\"e.g. Global, US, EU\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"prov-industry\" className={labelCls}>\n Industry\n </label>\n <input\n id=\"prov-industry\"\n type=\"text\"\n value={industry}\n onChange={(e) => setIndustry(e.target.value)}\n placeholder=\"e.g. Cloud, Media\"\n className={inputCls}\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"prov-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"prov-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"What does this provider offer?\"\n className={inputCls}\n />\n </div>\n\n <div>\n <label htmlFor=\"prov-tags\" className={labelCls}>\n Tags <span className=\"text-text-secondary\">(comma-separated)</span>\n </label>\n <input\n id=\"prov-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n placeholder=\"ai, productivity, enterprise\"\n className={inputCls}\n />\n </div>\n\n <div className=\"flex gap-3\">\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create provider'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers')}\n className=\"rounded-lg border border-border-subtle px-5 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAeA,IAAM,IAAoC;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IACJ,wMACI,IAAW;AAEjB,SAAgB,IAAkB;CAChC,IAAM,EAAE,kBAAe,GAAkB,EACnC,IAAS,GAAmB,EAE5B,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAU,KAAe,EAA8B,OAAO,EAC/D,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAW,KAAgB,EAAwB,KAAK;AA6B/D,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,aAAa;GACvC,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;;YAVb,CAaE,kBAAC,GAAD;GAAa,WAAU;aAAO;GAGhB,CAAA,EAEd,kBAAC,QAAD;GAAM,WA9CY,MAAuB;AAG3C,QAFA,EAAE,gBAAgB,EAClB,EAAa,KAAK,EACd,CAAC,EAAK,MAAM,EAAE;AAChB,OAAa,+BAA+B;AAC5C;;IAEF,IAAM,IAAa,EAChB,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ,EACZ,IAAgC;KACpC,MAAM,EAAK,MAAM;KACjB;KACA,GAAI,EAAY,MAAM,GAAG,EAAE,aAAa,EAAY,MAAM,EAAE,GAAG,EAAE;KACjE,GAAI,EAAW,MAAM,GAAG,EAAE,YAAY,EAAW,MAAM,EAAE,GAAG,EAAE;KAC9D,GAAI,EAAO,MAAM,GAAG,EAAE,QAAQ,EAAO,MAAM,EAAE,GAAG,EAAE;KAClD,GAAI,EAAS,MAAM,GAAG,EAAE,UAAU,EAAS,MAAM,EAAE,GAAG,EAAE;KACxD,GAAI,EAAW,SAAS,EAAE,MAAM,GAAY,GAAG,EAAE;KAClD;AACD,MAAO,OAAO,GAAO;KACnB,YAAY,MAAa,EAAW,cAAc,EAAS,KAAK;KAChE,UAAU,MACR,EAAa,aAAe,QAAQ,EAAI,UAAU,6BAA6B;KAClF,CAAC;;GAsB8B,WAAU;aAAxC;IACG,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAGR,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAY,WAAW;eAAtC,CAAgD,SACzC,kBAAC,QAAD;MAAM,WAAU;gBAAyB;MAAQ,CAAA,CAChD;QACR,kBAAC,SAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;KACxC,aAAY;KACZ,WAAW;KACX,UAAA;KACA,CAAA,CACE,EAAA,CAAA;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAgB,WAAW;gBAAU;MAE5C,CAAA,EACR,kBAAC,UAAD;MACE,IAAG;MACH,OAAO;MACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAA6B;MACnE,WAAW;gBAEV,EAAW,KAAK,MACf,kBAAC,UAAD;OAAgB,OAAO;iBACpB,EAAE,QAAQ,MAAM,IAAI;OACd,EAFI,EAEJ,CACT;MACK,CAAA,CACL,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAe,WAAW;gBAAU;MAE3C,CAAA,EACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;MAC9C,aAAY;MACZ,WAAW;MACX,CAAA,CACE,EAAA,CAAA,CACF;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAc,WAAW;gBAAU;MAE1C,CAAA,EACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,aAAY;MACZ,WAAW;MACX,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,SAAQ;MAAgB,WAAW;gBAAU;MAE5C,CAAA,EACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;MAC5C,aAAY;MACZ,WAAW;MACX,CAAA,CACE,EAAA,CAAA,CACF;;IAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAY,WAAW;eAAU;KAExC,CAAA,EACR,kBAAC,YAAD;KACE,IAAG;KACH,OAAO;KACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;KAC/C,MAAM;KACN,aAAY;KACZ,WAAW;KACX,CAAA,CACE,EAAA,CAAA;IAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAY,WAAW;eAAtC,CAAgD,SACzC,kBAAC,QAAD;MAAM,WAAU;gBAAsB;MAAwB,CAAA,CAC7D;QACR,kBAAC,SAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;KACxC,aAAY;KACZ,WAAW;KACX,CAAA,CACE,EAAA,CAAA;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,EAAO;MACjB,WAAU;gBAET,EAAO,YAAY,cAAc;MAC3B,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,aAAa;MACvC,WAAU;gBACX;MAEQ,CAAA,CACL;;IACD;KACI"}
|
|
1
|
+
{"version":3,"file":"ProviderNewPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/providers/ProviderNewPage.tsx"],"sourcesContent":["/**\n * Create provider\n * @module subscriberbot/pages/providers\n *\n * Add a provider to the registry. Writes `subCreateProvider`. A card-wrapped\n * form with category chips and a live marketplace-style preview that mirrors\n * exactly what will land in the registry.\n */\n\nimport { useState } from 'react';\nimport { ArrowLeft, Building2, Sparkles } from 'lucide-react';\nimport { PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useCreateProvider } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { SubCreateProviderInput, SubProviderCategory } from '../../types';\n\nconst CATEGORIES: SubProviderCategory[] = [\n 'SAAS',\n 'AI_SERVICE',\n 'STREAMING',\n 'NEWSLETTER',\n 'NEWS_DIGEST',\n 'MEMBERSHIP',\n 'COMMUNITY',\n 'EDUCATION',\n 'FINANCIAL',\n 'INSURANCE',\n 'TELECOM',\n 'UTILITY',\n 'ASSOCIATION',\n 'LICENSE',\n 'CONTRACT',\n 'OTHER',\n];\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\nexport function ProviderNewPage() {\n const { navigateTo } = useSubscriberBot();\n const create = useCreateProvider();\n\n const [name, setName] = useState('');\n const [category, setCategory] = useState<SubProviderCategory>('SAAS');\n const [description, setDescription] = useState('');\n const [websiteUrl, setWebsiteUrl] = useState('');\n const [region, setRegion] = useState('');\n const [industry, setIndustry] = useState('');\n const [tags, setTags] = useState('');\n const [formError, setFormError] = useState<string | null>(null);\n\n const parsedTags = tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim()) {\n setFormError('A provider name is required.');\n return;\n }\n const input: SubCreateProviderInput = {\n name: name.trim(),\n category,\n ...(description.trim() ? { description: description.trim() } : {}),\n ...(websiteUrl.trim() ? { websiteUrl: websiteUrl.trim() } : {}),\n ...(region.trim() ? { region: region.trim() } : {}),\n ...(industry.trim() ? { industry: industry.trim() } : {}),\n ...(parsedTags.length ? { tags: parsedTags } : {}),\n };\n create.mutate(input, {\n onSuccess: (provider) => navigateTo(`/providers/${provider.id}`),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not create provider.'),\n });\n };\n\n return (\n <PageLayout\n title=\"Add provider\"\n description=\"Register a new provider in the registry\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n Add a provider that isn't in the registry yet so you can attach relationships, plans and\n assets to it.\n </PagePurpose>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Form */}\n <GlassCard treatment=\"metric\" className=\"p-6 lg:col-span-2\">\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n\n <div>\n <label htmlFor=\"prov-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"prov-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. OpenAI, Netflix, AWS\"\n className={inputCls}\n required\n />\n </div>\n\n <div>\n <span className={labelCls}>Category</span>\n <div className=\"flex flex-wrap gap-2\" role=\"group\" aria-label=\"Provider category\">\n {CATEGORIES.map((c) => {\n const active = category === c;\n return (\n <button\n key={c}\n type=\"button\"\n aria-pressed={active}\n onClick={() => setCategory(c)}\n className={cn(\n 'rounded-full border px-3 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n {formatEnumLabel(c)}\n </button>\n );\n })}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"prov-website\" className={labelCls}>\n Website\n </label>\n <input\n id=\"prov-website\"\n type=\"url\"\n value={websiteUrl}\n onChange={(e) => setWebsiteUrl(e.target.value)}\n placeholder=\"https://…\"\n className={inputCls}\n />\n </div>\n <div>\n <label htmlFor=\"prov-region\" className={labelCls}>\n Region\n </label>\n <input\n id=\"prov-region\"\n type=\"text\"\n value={region}\n onChange={(e) => setRegion(e.target.value)}\n placeholder=\"e.g. Global, US, EU\"\n className={inputCls}\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"prov-industry\" className={labelCls}>\n Industry\n </label>\n <input\n id=\"prov-industry\"\n type=\"text\"\n value={industry}\n onChange={(e) => setIndustry(e.target.value)}\n placeholder=\"e.g. Cloud, Media\"\n className={inputCls}\n />\n </div>\n\n <div>\n <label htmlFor=\"prov-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"prov-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"What does this provider offer?\"\n className={inputCls}\n />\n </div>\n\n <div>\n <label htmlFor=\"prov-tags\" className={labelCls}>\n Tags <span className=\"text-text-secondary\">(comma-separated)</span>\n </label>\n <input\n id=\"prov-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n placeholder=\"ai, productivity, enterprise\"\n className={inputCls}\n />\n </div>\n\n <div className=\"flex flex-wrap gap-3 border-t border-border-subtle pt-5\">\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Creating…' : 'Create provider'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers')}\n className=\"rounded-lg border border-border-subtle px-5 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n\n {/* Live preview — reflects the form state (no fabricated data) */}\n <div className=\"lg:sticky lg:top-6 lg:self-start\">\n <p className=\"mb-2 inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary\">\n <Sparkles className=\"h-3.5 w-3.5\" /> Registry preview\n </p>\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <Building2 className=\"h-5 w-5\" />\n </span>\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-[11px] font-medium text-status-warning-text\">\n Unverified\n </span>\n </div>\n <p className=\"mt-3 truncate text-base font-semibold text-text-primary\">\n {name.trim() || 'Provider name'}\n </p>\n <span className=\"mt-1 inline-flex w-fit rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {formatEnumLabel(category)}\n </span>\n {description.trim() ? (\n <p className=\"mt-3 line-clamp-3 text-xs leading-relaxed text-text-secondary\">\n {description.trim()}\n </p>\n ) : (\n <p className=\"mt-3 text-xs italic text-text-secondary/70\">\n A short description will appear here.\n </p>\n )}\n {(region.trim() || industry.trim() || parsedTags.length > 0) && (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border-subtle pt-3\">\n {industry.trim() && (\n <span className=\"text-[11px] text-text-secondary\">{industry.trim()}</span>\n )}\n {region.trim() && (\n <span className=\"text-[11px] text-text-secondary\">· {region.trim()}</span>\n )}\n {parsedTags.slice(0, 4).map((t) => (\n <span\n key={t}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[11px] text-text-secondary\"\n >\n {t}\n </span>\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -5,16 +5,18 @@ import { formatEnumLabel as r } from "../../utils/formatters.js";
|
|
|
5
5
|
import { StatusBadge as i } from "../../components/StatusBadge.js";
|
|
6
6
|
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
7
7
|
import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { MiniProgress as c } from "../../components/viz.js";
|
|
9
|
+
import "../../components/index.js";
|
|
10
|
+
import { useProviders as l } from "../../hooks/useSubscriberBotApi.js";
|
|
11
|
+
import { useState as u } from "react";
|
|
12
|
+
import { Activity as d, BadgeCheck as f, Building2 as p, ChevronRight as m, Plus as h, Search as g, ShieldCheck as _, Store as v } from "lucide-react";
|
|
13
|
+
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
14
|
+
import { EmphasisPanel as S, GlassCard as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
|
|
13
15
|
//#region src/subscriberbot/pages/providers/ProvidersListPage.tsx
|
|
14
|
-
var
|
|
16
|
+
var T = 24, E = [
|
|
15
17
|
{
|
|
16
18
|
value: "",
|
|
17
|
-
label: "All
|
|
19
|
+
label: "All"
|
|
18
20
|
},
|
|
19
21
|
{
|
|
20
22
|
value: "SAAS",
|
|
@@ -80,7 +82,7 @@ var y = 24, b = [
|
|
|
80
82
|
value: "OTHER",
|
|
81
83
|
label: "Other"
|
|
82
84
|
}
|
|
83
|
-
],
|
|
85
|
+
], D = [
|
|
84
86
|
{
|
|
85
87
|
value: "",
|
|
86
88
|
label: "Any status"
|
|
@@ -102,145 +104,248 @@ var y = 24, b = [
|
|
|
102
104
|
label: "Deprecated"
|
|
103
105
|
}
|
|
104
106
|
];
|
|
105
|
-
function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
function O(e) {
|
|
108
|
+
return e >= 70 ? "positive" : e >= 45 ? "accent" : e >= 25 ? "warning" : "negative";
|
|
109
|
+
}
|
|
110
|
+
function k(e) {
|
|
111
|
+
return e >= 67 ? {
|
|
112
|
+
label: "High churn risk",
|
|
113
|
+
cls: "bg-status-error-bg-subtle text-status-error-text"
|
|
114
|
+
} : e >= 34 ? {
|
|
115
|
+
label: "Moderate churn risk",
|
|
116
|
+
cls: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
117
|
+
} : {
|
|
118
|
+
label: "Low churn risk",
|
|
119
|
+
cls: "bg-status-success-bg-subtle text-status-success-text"
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function A({ provider: e, onOpen: n }) {
|
|
123
|
+
let a = e.vendorHealthScore, o = e.churnRiskScore, s = o == null ? null : k(o);
|
|
124
|
+
return /* @__PURE__ */ x(C, {
|
|
125
|
+
treatment: "glass",
|
|
126
|
+
role: "button",
|
|
127
|
+
tabIndex: 0,
|
|
128
|
+
onClick: n,
|
|
129
|
+
onKeyDown: (e) => {
|
|
130
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), n());
|
|
131
|
+
},
|
|
132
|
+
"aria-label": `Open ${e.name}`,
|
|
133
|
+
className: "group flex cursor-pointer flex-col p-5 outline-none transition-all hover:-translate-y-0.5 hover:shadow-lg focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ x("div", {
|
|
136
|
+
className: "flex items-start justify-between gap-2",
|
|
137
|
+
children: [/* @__PURE__ */ b("span", {
|
|
138
|
+
className: "flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary",
|
|
139
|
+
children: /* @__PURE__ */ b(p, { className: "h-5 w-5" })
|
|
140
|
+
}), /* @__PURE__ */ b(i, { status: e.status })]
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ b("p", {
|
|
143
|
+
className: "mt-3 truncate text-base font-semibold text-text-primary",
|
|
144
|
+
children: e.name
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ b("span", {
|
|
147
|
+
className: "mt-1 inline-flex w-fit rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
148
|
+
children: r(e.category)
|
|
149
|
+
}),
|
|
150
|
+
e.description ? /* @__PURE__ */ b("p", {
|
|
151
|
+
className: "mt-3 line-clamp-2 min-h-[2.5rem] text-xs leading-relaxed text-text-secondary",
|
|
152
|
+
children: e.description
|
|
153
|
+
}) : /* @__PURE__ */ b("p", {
|
|
154
|
+
className: "mt-3 min-h-[2.5rem] text-xs italic text-text-secondary/70",
|
|
155
|
+
children: "No description yet."
|
|
156
|
+
}),
|
|
157
|
+
/* @__PURE__ */ x("div", {
|
|
158
|
+
className: "mt-4 border-t border-border-subtle pt-3",
|
|
159
|
+
children: [/* @__PURE__ */ x("div", {
|
|
160
|
+
className: "flex items-center justify-between text-xs",
|
|
161
|
+
children: [/* @__PURE__ */ x("span", {
|
|
162
|
+
className: "inline-flex items-center gap-1 text-text-secondary",
|
|
163
|
+
children: [/* @__PURE__ */ b(_, { className: "h-3.5 w-3.5" }), " Vendor health"]
|
|
164
|
+
}), /* @__PURE__ */ b("span", {
|
|
165
|
+
className: "font-semibold tabular-nums text-text-primary",
|
|
166
|
+
children: a == null ? "—" : `${a}/100`
|
|
167
|
+
})]
|
|
168
|
+
}), /* @__PURE__ */ b(c, {
|
|
169
|
+
value: a ?? 0,
|
|
170
|
+
tone: a == null ? "accent" : O(a),
|
|
171
|
+
className: "mt-1.5"
|
|
172
|
+
})]
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ x("div", {
|
|
175
|
+
className: "mt-3 flex flex-wrap items-center justify-between gap-2",
|
|
176
|
+
children: [/* @__PURE__ */ x("div", {
|
|
177
|
+
className: "flex flex-wrap items-center gap-2",
|
|
178
|
+
children: [s && /* @__PURE__ */ b("span", {
|
|
179
|
+
className: t("inline-flex rounded-full px-2 py-0.5 text-[11px] font-medium", s.cls),
|
|
180
|
+
children: s.label
|
|
181
|
+
}), e.region && /* @__PURE__ */ b("span", {
|
|
182
|
+
className: "text-[11px] text-text-secondary",
|
|
183
|
+
children: e.region
|
|
184
|
+
})]
|
|
185
|
+
}), /* @__PURE__ */ x("span", {
|
|
186
|
+
className: "inline-flex items-center gap-0.5 text-xs font-medium text-text-link opacity-0 transition-opacity group-hover:opacity-100",
|
|
187
|
+
children: ["View ", /* @__PURE__ */ b(m, { className: "h-3.5 w-3.5" })]
|
|
188
|
+
})]
|
|
189
|
+
})
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function j() {
|
|
194
|
+
let { navigateTo: r } = e(), [i, c] = u(0), [m, _] = u(""), [O, k] = u(""), [j, M] = u(""), [N, P] = u(""), { data: F, isLoading: I, isError: L, refetch: R, isFetching: z } = l({
|
|
195
|
+
search: m || void 0,
|
|
196
|
+
category: j || void 0,
|
|
197
|
+
status: N || void 0,
|
|
110
198
|
pagination: {
|
|
111
|
-
limit:
|
|
112
|
-
offset:
|
|
199
|
+
limit: T,
|
|
200
|
+
offset: i * T
|
|
201
|
+
}
|
|
202
|
+
}), B = F?.items ?? [], V = F?.pageInfo.hasMore ?? !1, H = B.filter((e) => e.status === "VERIFIED").length, U = B.map((e) => e.vendorHealthScore).filter((e) => e != null), W = U.length ? Math.round(U.reduce((e, t) => e + t, 0) / U.length) : null, G = (e) => {
|
|
203
|
+
e.preventDefault(), _(O.trim()), c(0);
|
|
204
|
+
}, K = [
|
|
205
|
+
{
|
|
206
|
+
icon: v,
|
|
207
|
+
label: "On this page",
|
|
208
|
+
value: String(B.length)
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
icon: f,
|
|
212
|
+
label: "Verified",
|
|
213
|
+
value: String(H)
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
icon: d,
|
|
217
|
+
label: "Avg. vendor health",
|
|
218
|
+
value: W == null ? "—" : `${W}/100`
|
|
113
219
|
}
|
|
114
|
-
|
|
115
|
-
return /* @__PURE__ */
|
|
116
|
-
title: "
|
|
117
|
-
description: "
|
|
118
|
-
actions: /* @__PURE__ */
|
|
220
|
+
];
|
|
221
|
+
return /* @__PURE__ */ x(n, {
|
|
222
|
+
title: "Discover providers",
|
|
223
|
+
description: "Browse the global provider registry — the public face of the Subscription Knowledge Graph",
|
|
224
|
+
actions: /* @__PURE__ */ x("button", {
|
|
119
225
|
type: "button",
|
|
120
|
-
onClick: () =>
|
|
226
|
+
onClick: () => r("/providers/new"),
|
|
121
227
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
122
|
-
children: [/* @__PURE__ */ h
|
|
228
|
+
children: [/* @__PURE__ */ b(h, { className: "h-4 w-4" }), " Add provider"]
|
|
123
229
|
}),
|
|
124
230
|
children: [
|
|
125
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ b(w, {
|
|
126
232
|
className: "mb-4",
|
|
127
|
-
children: "
|
|
233
|
+
children: "Every provider exists here whether or not it integrates. Search and filter to find the ones you have relationships with, compare their vendor health, or add a new one."
|
|
128
234
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
130
|
-
onSubmit:
|
|
131
|
-
e.preventDefault(), E(D.trim()), w(0);
|
|
132
|
-
},
|
|
235
|
+
/* @__PURE__ */ b(S, { children: /* @__PURE__ */ x("form", {
|
|
236
|
+
onSubmit: G,
|
|
133
237
|
className: "relative",
|
|
134
|
-
children: [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
238
|
+
children: [
|
|
239
|
+
/* @__PURE__ */ b(g, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }),
|
|
240
|
+
/* @__PURE__ */ b("input", {
|
|
241
|
+
type: "text",
|
|
242
|
+
placeholder: "Search providers by name…",
|
|
243
|
+
value: O,
|
|
244
|
+
onChange: (e) => k(e.target.value),
|
|
245
|
+
className: "w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-24 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
246
|
+
}),
|
|
247
|
+
/* @__PURE__ */ b("button", {
|
|
248
|
+
type: "submit",
|
|
249
|
+
className: "absolute right-1.5 top-1/2 -translate-y-1/2 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90",
|
|
250
|
+
children: "Search"
|
|
251
|
+
})
|
|
252
|
+
]
|
|
141
253
|
}) }),
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
-
className: "mt-4 flex
|
|
144
|
-
children:
|
|
145
|
-
value
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
254
|
+
/* @__PURE__ */ b("div", {
|
|
255
|
+
className: "mt-4 flex items-center gap-2 overflow-x-auto pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
256
|
+
children: E.map((e) => {
|
|
257
|
+
let n = j === e.value;
|
|
258
|
+
return /* @__PURE__ */ b("button", {
|
|
259
|
+
type: "button",
|
|
260
|
+
"aria-pressed": n,
|
|
261
|
+
onClick: () => {
|
|
262
|
+
M(e.value), c(0);
|
|
263
|
+
},
|
|
264
|
+
className: t("shrink-0 rounded-full border px-3.5 py-1.5 text-xs font-medium transition-colors", n ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-subtle bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary"),
|
|
152
265
|
children: e.label
|
|
153
|
-
}, e.value)
|
|
154
|
-
})
|
|
155
|
-
|
|
266
|
+
}, e.value || "all");
|
|
267
|
+
})
|
|
268
|
+
}),
|
|
269
|
+
/* @__PURE__ */ b("div", {
|
|
270
|
+
className: "mt-3 flex flex-wrap items-center gap-2",
|
|
271
|
+
children: /* @__PURE__ */ b("select", {
|
|
272
|
+
"aria-label": "Filter by status",
|
|
273
|
+
value: N,
|
|
156
274
|
onChange: (e) => {
|
|
157
|
-
|
|
275
|
+
P(e.target.value), c(0);
|
|
158
276
|
},
|
|
159
277
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
160
|
-
children:
|
|
278
|
+
children: D.map((e) => /* @__PURE__ */ b("option", {
|
|
161
279
|
value: e.value,
|
|
162
280
|
children: e.label
|
|
163
281
|
}, e.value))
|
|
164
|
-
})
|
|
282
|
+
})
|
|
165
283
|
}),
|
|
166
|
-
|
|
284
|
+
!I && !L && B.length > 0 && /* @__PURE__ */ b("div", {
|
|
285
|
+
className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3",
|
|
286
|
+
children: K.map((e) => /* @__PURE__ */ x(C, {
|
|
287
|
+
treatment: "metric",
|
|
288
|
+
className: "flex items-center gap-3 p-4",
|
|
289
|
+
children: [/* @__PURE__ */ b("span", {
|
|
290
|
+
className: "flex size-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
291
|
+
children: /* @__PURE__ */ b(e.icon, { className: "h-4 w-4" })
|
|
292
|
+
}), /* @__PURE__ */ x("div", {
|
|
293
|
+
className: "min-w-0",
|
|
294
|
+
children: [/* @__PURE__ */ b("p", {
|
|
295
|
+
className: "truncate text-xs text-text-secondary",
|
|
296
|
+
children: e.label
|
|
297
|
+
}), /* @__PURE__ */ b("p", {
|
|
298
|
+
className: "text-lg font-semibold tabular-nums text-text-primary",
|
|
299
|
+
children: e.value
|
|
300
|
+
})]
|
|
301
|
+
})]
|
|
302
|
+
}, e.label))
|
|
303
|
+
}),
|
|
304
|
+
I ? /* @__PURE__ */ b(s, { label: "Loading providers…" }) : L ? /* @__PURE__ */ b(o, {
|
|
167
305
|
message: "Could not load the provider registry.",
|
|
168
|
-
onRetry: () => void
|
|
169
|
-
}) :
|
|
306
|
+
onRetry: () => void R()
|
|
307
|
+
}) : B.length === 0 ? /* @__PURE__ */ b("div", {
|
|
170
308
|
className: "mt-6",
|
|
171
|
-
children: /* @__PURE__ */
|
|
309
|
+
children: /* @__PURE__ */ b(a, {
|
|
172
310
|
illustration: "empty-search",
|
|
173
|
-
icon: /* @__PURE__ */
|
|
311
|
+
icon: /* @__PURE__ */ b(p, { className: "h-12 w-12" }),
|
|
174
312
|
title: "No providers found",
|
|
175
|
-
description:
|
|
176
|
-
action: /* @__PURE__ */
|
|
313
|
+
description: m ? `No providers match "${m}".` : "No providers in the registry with these filters yet.",
|
|
314
|
+
action: /* @__PURE__ */ b("button", {
|
|
177
315
|
type: "button",
|
|
178
|
-
onClick: () =>
|
|
316
|
+
onClick: () => r("/providers/new"),
|
|
179
317
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
180
318
|
children: "Add provider"
|
|
181
319
|
})
|
|
182
320
|
})
|
|
183
|
-
}) : /* @__PURE__ */
|
|
321
|
+
}) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b("div", {
|
|
184
322
|
className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
185
|
-
children:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
className: "flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4 text-left transition-shadow hover:shadow-md",
|
|
189
|
-
children: [
|
|
190
|
-
/* @__PURE__ */ g("div", {
|
|
191
|
-
className: "flex items-start justify-between gap-2",
|
|
192
|
-
children: [/* @__PURE__ */ h("span", {
|
|
193
|
-
className: "flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
194
|
-
children: /* @__PURE__ */ h(u, { className: "h-4 w-4" })
|
|
195
|
-
}), /* @__PURE__ */ h(i, { status: e.status })]
|
|
196
|
-
}),
|
|
197
|
-
/* @__PURE__ */ h("p", {
|
|
198
|
-
className: "mt-3 truncate text-sm font-semibold text-text-primary",
|
|
199
|
-
children: e.name
|
|
200
|
-
}),
|
|
201
|
-
/* @__PURE__ */ h("p", {
|
|
202
|
-
className: "text-xs text-text-secondary",
|
|
203
|
-
children: r(e.category)
|
|
204
|
-
}),
|
|
205
|
-
e.description && /* @__PURE__ */ h("p", {
|
|
206
|
-
className: "mt-2 line-clamp-2 text-xs text-text-secondary",
|
|
207
|
-
children: e.description
|
|
208
|
-
}),
|
|
209
|
-
/* @__PURE__ */ g("div", {
|
|
210
|
-
className: "mt-3 flex flex-wrap items-center gap-2 text-xs text-text-secondary",
|
|
211
|
-
children: [e.region && /* @__PURE__ */ h("span", { children: e.region }), e.vendorHealthScore != null && /* @__PURE__ */ g("span", {
|
|
212
|
-
className: "inline-flex items-center gap-1",
|
|
213
|
-
children: [
|
|
214
|
-
/* @__PURE__ */ h(p, { className: "h-3 w-3" }),
|
|
215
|
-
e.vendorHealthScore,
|
|
216
|
-
"/100"
|
|
217
|
-
]
|
|
218
|
-
})]
|
|
219
|
-
})
|
|
220
|
-
]
|
|
323
|
+
children: B.map((e) => /* @__PURE__ */ b(A, {
|
|
324
|
+
provider: e,
|
|
325
|
+
onOpen: () => r(`/providers/${e.id}`)
|
|
221
326
|
}, e.id))
|
|
222
|
-
}), /* @__PURE__ */
|
|
223
|
-
className: "mt-
|
|
224
|
-
children: [/* @__PURE__ */
|
|
327
|
+
}), /* @__PURE__ */ x("div", {
|
|
328
|
+
className: "mt-6 flex items-center justify-between",
|
|
329
|
+
children: [/* @__PURE__ */ x("p", {
|
|
225
330
|
className: "text-xs text-text-secondary",
|
|
226
331
|
children: [
|
|
227
332
|
"Page ",
|
|
228
|
-
|
|
229
|
-
|
|
333
|
+
i + 1,
|
|
334
|
+
z ? " · refreshing…" : ""
|
|
230
335
|
]
|
|
231
|
-
}), /* @__PURE__ */
|
|
336
|
+
}), /* @__PURE__ */ x("div", {
|
|
232
337
|
className: "flex gap-2",
|
|
233
|
-
children: [/* @__PURE__ */
|
|
338
|
+
children: [/* @__PURE__ */ b("button", {
|
|
234
339
|
type: "button",
|
|
235
|
-
disabled:
|
|
236
|
-
onClick: () =>
|
|
237
|
-
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium",
|
|
340
|
+
disabled: i === 0,
|
|
341
|
+
onClick: () => c((e) => Math.max(0, e - 1)),
|
|
342
|
+
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", i === 0 ? "cursor-not-allowed text-text-secondary opacity-50" : "text-text-primary hover:bg-bg-sunken"),
|
|
238
343
|
children: "Previous"
|
|
239
|
-
}), /* @__PURE__ */
|
|
344
|
+
}), /* @__PURE__ */ b("button", {
|
|
240
345
|
type: "button",
|
|
241
|
-
disabled: !
|
|
242
|
-
onClick: () =>
|
|
243
|
-
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium",
|
|
346
|
+
disabled: !V,
|
|
347
|
+
onClick: () => c((e) => e + 1),
|
|
348
|
+
className: t("rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium", V ? "text-text-primary hover:bg-bg-sunken" : "cursor-not-allowed text-text-secondary opacity-50"),
|
|
244
349
|
children: "Next"
|
|
245
350
|
})]
|
|
246
351
|
})]
|
|
@@ -249,6 +354,6 @@ function S() {
|
|
|
249
354
|
});
|
|
250
355
|
}
|
|
251
356
|
//#endregion
|
|
252
|
-
export {
|
|
357
|
+
export { j as ProvidersListPage };
|
|
253
358
|
|
|
254
359
|
//# sourceMappingURL=ProvidersListPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvidersListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/providers/ProvidersListPage.tsx"],"sourcesContent":["/**\n * Providers list (Global Provider Registry)\n * @module subscriberbot/pages/providers\n *\n * Discovery: search and browse the provider registry. Server-side search +\n * category/status filters + pagination via `subListProviders`\n * (a SubProviderConnection).\n */\n\nimport { useState } from 'react';\nimport { Plus, Search, Building2, ShieldCheck } from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useProviders } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { SubProviderCategory, SubProviderStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst CATEGORY_OPTIONS: Array<{ value: '' | SubProviderCategory; label: string }> = [\n { value: '', label: 'All categories' },\n { value: 'SAAS', label: 'SaaS' },\n { value: 'AI_SERVICE', label: 'AI service' },\n { value: 'STREAMING', label: 'Streaming' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'NEWS_DIGEST', label: 'News digest' },\n { value: 'MEMBERSHIP', label: 'Membership' },\n { value: 'COMMUNITY', label: 'Community' },\n { value: 'EDUCATION', label: 'Education' },\n { value: 'FINANCIAL', label: 'Financial' },\n { value: 'INSURANCE', label: 'Insurance' },\n { value: 'TELECOM', label: 'Telecom' },\n { value: 'UTILITY', label: 'Utility' },\n { value: 'ASSOCIATION', label: 'Association' },\n { value: 'LICENSE', label: 'License' },\n { value: 'CONTRACT', label: 'Contract' },\n { value: 'OTHER', label: 'Other' },\n];\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubProviderStatus; label: string }> = [\n { value: '', label: 'Any status' },\n { value: 'VERIFIED', label: 'Verified' },\n { value: 'CLAIMED', label: 'Claimed' },\n { value: 'UNVERIFIED', label: 'Unverified' },\n { value: 'DEPRECATED', label: 'Deprecated' },\n];\n\nexport function ProvidersListPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [search, setSearch] = useState('');\n const [searchInput, setSearchInput] = useState('');\n const [category, setCategory] = useState<'' | SubProviderCategory>('');\n const [status, setStatus] = useState<'' | SubProviderStatus>('');\n\n const { data, isLoading, isError, refetch, isFetching } = useProviders({\n search: search || undefined,\n category: category || undefined,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n\n const items = data?.items ?? [];\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n const submitSearch = (e: React.FormEvent) => {\n e.preventDefault();\n setSearch(searchInput.trim());\n setPage(0);\n };\n\n return (\n <PageLayout\n title=\"Providers\"\n description=\"Discover and browse the global provider registry\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> Add provider\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n The registry is the public face of the Subscription Knowledge Graph — every provider exists\n here whether or not it integrates. Search and filter to find the ones you have relationships\n with, or add a new one.\n </PagePurpose>\n\n <EmphasisPanel>\n <form onSubmit={submitSearch} className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search providers by name…\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </form>\n </EmphasisPanel>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n <select\n value={category}\n onChange={(e) => {\n setCategory(e.target.value as '' | SubProviderCategory);\n setPage(0);\n }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {CATEGORY_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubProviderStatus);\n setPage(0);\n }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading providers…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load the provider registry.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building2 className=\"h-12 w-12\" />}\n title=\"No providers found\"\n description={\n search\n ? `No providers match \"${search}\".`\n : 'No providers in the registry with these filters yet.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Add provider\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((p) => (\n <button\n key={p.id}\n type=\"button\"\n onClick={() => navigateTo(`/providers/${p.id}`)}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4 text-left transition-shadow hover:shadow-md\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Building2 className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={p.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{p.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(p.category)}</p>\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{p.description}</p>\n )}\n <div className=\"mt-3 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n {p.region && <span>{p.region}</span>}\n {p.vendorHealthScore != null && (\n <span className=\"inline-flex items-center gap-1\">\n <ShieldCheck className=\"h-3 w-3\" />\n {p.vendorHealthScore}/100\n </span>\n )}\n </div>\n </button>\n ))}\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProvidersListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/providers/ProvidersListPage.tsx"],"sourcesContent":["/**\n * Providers list (Global Provider Registry)\n * @module subscriberbot/pages/providers\n *\n * Discovery marketplace: search and browse the provider registry. Server-side\n * search + category/status filters + pagination via `subListProviders`\n * (a SubProviderConnection). Category filter chips, a responsive grid of glass\n * provider cards with a real vendor-health indicator + churn hint, and a small\n * live summary strip computed from the fetched page.\n */\n\nimport { useState } from 'react';\nimport {\n Plus,\n Search,\n Building2,\n ShieldCheck,\n ChevronRight,\n Store,\n Activity,\n BadgeCheck,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { MiniProgress } from '../../components';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useProviders } from '../../hooks/useSubscriberBotApi';\nimport { cn } from '../../utils/cn';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport type { SubProvider, SubProviderCategory, SubProviderStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst CATEGORY_OPTIONS: Array<{ value: '' | SubProviderCategory; label: string }> = [\n { value: '', label: 'All' },\n { value: 'SAAS', label: 'SaaS' },\n { value: 'AI_SERVICE', label: 'AI service' },\n { value: 'STREAMING', label: 'Streaming' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'NEWS_DIGEST', label: 'News digest' },\n { value: 'MEMBERSHIP', label: 'Membership' },\n { value: 'COMMUNITY', label: 'Community' },\n { value: 'EDUCATION', label: 'Education' },\n { value: 'FINANCIAL', label: 'Financial' },\n { value: 'INSURANCE', label: 'Insurance' },\n { value: 'TELECOM', label: 'Telecom' },\n { value: 'UTILITY', label: 'Utility' },\n { value: 'ASSOCIATION', label: 'Association' },\n { value: 'LICENSE', label: 'License' },\n { value: 'CONTRACT', label: 'Contract' },\n { value: 'OTHER', label: 'Other' },\n];\n\nconst STATUS_OPTIONS: Array<{ value: '' | SubProviderStatus; label: string }> = [\n { value: '', label: 'Any status' },\n { value: 'VERIFIED', label: 'Verified' },\n { value: 'CLAIMED', label: 'Claimed' },\n { value: 'UNVERIFIED', label: 'Unverified' },\n { value: 'DEPRECATED', label: 'Deprecated' },\n];\n\n/** Map a 0–100 vendor-health score to a MiniProgress tone. Higher is healthier. */\nfunction healthTone(score: number): 'positive' | 'accent' | 'warning' | 'negative' {\n if (score >= 70) return 'positive';\n if (score >= 45) return 'accent';\n if (score >= 25) return 'warning';\n return 'negative';\n}\n\n/** Map a 0–100 churn-risk score to a label + semantic tone. Higher is worse. */\nfunction churnMeta(score: number): { label: string; cls: string } {\n if (score >= 67)\n return {\n label: 'High churn risk',\n cls: 'bg-status-error-bg-subtle text-status-error-text',\n };\n if (score >= 34)\n return {\n label: 'Moderate churn risk',\n cls: 'bg-status-warning-bg-subtle text-status-warning-text',\n };\n return {\n label: 'Low churn risk',\n cls: 'bg-status-success-bg-subtle text-status-success-text',\n };\n}\n\nfunction ProviderCard({ provider, onOpen }: { provider: SubProvider; onOpen: () => void }) {\n const health = provider.vendorHealthScore;\n const churn = provider.churnRiskScore;\n const churnInfo = churn != null ? churnMeta(churn) : null;\n\n return (\n <GlassCard\n treatment=\"glass\"\n role=\"button\"\n tabIndex={0}\n onClick={onOpen}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onOpen();\n }\n }}\n aria-label={`Open ${provider.name}`}\n className=\"group flex cursor-pointer flex-col p-5 outline-none transition-all hover:-translate-y-0.5 hover:shadow-lg focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-10 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-text-primary\">\n <Building2 className=\"h-5 w-5\" />\n </span>\n <StatusBadge status={provider.status} />\n </div>\n\n <p className=\"mt-3 truncate text-base font-semibold text-text-primary\">{provider.name}</p>\n <span className=\"mt-1 inline-flex w-fit rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {formatEnumLabel(provider.category)}\n </span>\n\n {provider.description ? (\n <p className=\"mt-3 line-clamp-2 min-h-[2.5rem] text-xs leading-relaxed text-text-secondary\">\n {provider.description}\n </p>\n ) : (\n <p className=\"mt-3 min-h-[2.5rem] text-xs italic text-text-secondary/70\">\n No description yet.\n </p>\n )}\n\n <div className=\"mt-4 border-t border-border-subtle pt-3\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"inline-flex items-center gap-1 text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5\" /> Vendor health\n </span>\n <span className=\"font-semibold tabular-nums text-text-primary\">\n {health != null ? `${health}/100` : '—'}\n </span>\n </div>\n <MiniProgress\n value={health ?? 0}\n tone={health != null ? healthTone(health) : 'accent'}\n className=\"mt-1.5\"\n />\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {churnInfo && (\n <span\n className={cn(\n 'inline-flex rounded-full px-2 py-0.5 text-[11px] font-medium',\n churnInfo.cls\n )}\n >\n {churnInfo.label}\n </span>\n )}\n {provider.region && (\n <span className=\"text-[11px] text-text-secondary\">{provider.region}</span>\n )}\n </div>\n <span className=\"inline-flex items-center gap-0.5 text-xs font-medium text-text-link opacity-0 transition-opacity group-hover:opacity-100\">\n View <ChevronRight className=\"h-3.5 w-3.5\" />\n </span>\n </div>\n </GlassCard>\n );\n}\n\nexport function ProvidersListPage() {\n const { navigateTo } = useSubscriberBot();\n const [page, setPage] = useState(0);\n const [search, setSearch] = useState('');\n const [searchInput, setSearchInput] = useState('');\n const [category, setCategory] = useState<'' | SubProviderCategory>('');\n const [status, setStatus] = useState<'' | SubProviderStatus>('');\n\n const { data, isLoading, isError, refetch, isFetching } = useProviders({\n search: search || undefined,\n category: category || undefined,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n\n const items = data?.items ?? [];\n const hasMore = data?.pageInfo.hasMore ?? false;\n\n // Live summary strip — computed from the fetched page (never fabricated).\n const verifiedCount = items.filter((p) => p.status === 'VERIFIED').length;\n const healthScores = items\n .map((p) => p.vendorHealthScore)\n .filter((s): s is number => s != null);\n const avgHealth = healthScores.length\n ? Math.round(healthScores.reduce((a, b) => a + b, 0) / healthScores.length)\n : null;\n\n const submitSearch = (e: React.FormEvent) => {\n e.preventDefault();\n setSearch(searchInput.trim());\n setPage(0);\n };\n\n const summary: Array<{ icon: typeof Store; label: string; value: string }> = [\n { icon: Store, label: 'On this page', value: String(items.length) },\n { icon: BadgeCheck, label: 'Verified', value: String(verifiedCount) },\n {\n icon: Activity,\n label: 'Avg. vendor health',\n value: avgHealth != null ? `${avgHealth}/100` : '—',\n },\n ];\n\n return (\n <PageLayout\n title=\"Discover providers\"\n description=\"Browse the global provider registry — the public face of the Subscription Knowledge Graph\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> Add provider\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Every provider exists here whether or not it integrates. Search and filter to find the ones\n you have relationships with, compare their vendor health, or add a new one.\n </PagePurpose>\n\n <EmphasisPanel>\n <form onSubmit={submitSearch} className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search providers by name…\"\n value={searchInput}\n onChange={(e) => setSearchInput(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2.5 pl-10 pr-24 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <button\n type=\"submit\"\n className=\"absolute right-1.5 top-1/2 -translate-y-1/2 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:opacity-90\"\n >\n Search\n </button>\n </form>\n </EmphasisPanel>\n\n {/* Category filter chips */}\n <div className=\"mt-4 flex items-center gap-2 overflow-x-auto pb-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\">\n {CATEGORY_OPTIONS.map((o) => {\n const active = category === o.value;\n return (\n <button\n key={o.value || 'all'}\n type=\"button\"\n aria-pressed={active}\n onClick={() => {\n setCategory(o.value);\n setPage(0);\n }}\n className={cn(\n 'shrink-0 rounded-full border px-3.5 py-1.5 text-xs font-medium transition-colors',\n active\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle bg-bg-surface text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n )}\n >\n {o.label}\n </button>\n );\n })}\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <select\n aria-label=\"Filter by status\"\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as '' | SubProviderStatus);\n setPage(0);\n }}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {STATUS_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Live summary strip */}\n {!isLoading && !isError && items.length > 0 && (\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3\">\n {summary.map((s) => (\n <GlassCard\n key={s.label}\n treatment=\"metric\"\n className=\"flex items-center gap-3 p-4\"\n >\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <s.icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate text-xs text-text-secondary\">{s.label}</p>\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">{s.value}</p>\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n\n {isLoading ? (\n <LoadingView label=\"Loading providers…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load the provider registry.\" onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building2 className=\"h-12 w-12\" />}\n title=\"No providers found\"\n description={\n search\n ? `No providers match \"${search}\".`\n : 'No providers in the registry with these filters yet.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/providers/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Add provider\n </button>\n }\n />\n </div>\n ) : (\n <>\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((p) => (\n <ProviderCard key={p.id} provider={p} onOpen={() => navigateTo(`/providers/${p.id}`)} />\n ))}\n </div>\n\n <div className=\"mt-6 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Page {page + 1}\n {isFetching ? ' · refreshing…' : ''}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n page === 0\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setPage((p) => p + 1)}\n className={cn(\n 'rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium',\n !hasMore\n ? 'cursor-not-allowed text-text-secondary opacity-50'\n : 'text-text-primary hover:bg-bg-sunken'\n )}\n >\n Next\n </button>\n </div>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|