@burdenoff/microfe-ggnomad 2026.828.1 → 2026.1016.1

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 (33) hide show
  1. package/dist/ggnomad/components/CreateProviderDialog.d.ts +22 -0
  2. package/dist/ggnomad/components/CreateProviderDialog.d.ts.map +1 -0
  3. package/dist/ggnomad/components/CreateProviderDialog.js +209 -0
  4. package/dist/ggnomad/components/CreateProviderDialog.js.map +1 -0
  5. package/dist/ggnomad/components/ProviderDashboardCard.d.ts +28 -0
  6. package/dist/ggnomad/components/ProviderDashboardCard.d.ts.map +1 -0
  7. package/dist/ggnomad/components/ProviderDashboardCard.js +222 -0
  8. package/dist/ggnomad/components/ProviderDashboardCard.js.map +1 -0
  9. package/dist/ggnomad/components/index.d.ts +2 -0
  10. package/dist/ggnomad/components/index.d.ts.map +1 -1
  11. package/dist/ggnomad/components/index.js +2 -0
  12. package/dist/ggnomad/graphql.d.ts +1 -0
  13. package/dist/ggnomad/graphql.d.ts.map +1 -1
  14. package/dist/ggnomad/graphql.js +65 -47
  15. package/dist/ggnomad/graphql.js.map +1 -1
  16. package/dist/ggnomad/pages/ProviderPage.d.ts.map +1 -1
  17. package/dist/ggnomad/pages/ProviderPage.js +249 -134
  18. package/dist/ggnomad/pages/ProviderPage.js.map +1 -1
  19. package/dist/ggnomad/utils/format.d.ts +18 -0
  20. package/dist/ggnomad/utils/format.d.ts.map +1 -1
  21. package/dist/ggnomad/utils/format.js +23 -7
  22. package/dist/ggnomad/utils/format.js.map +1 -1
  23. package/dist/ggnomad/utils/providerListControls.d.ts +53 -0
  24. package/dist/ggnomad/utils/providerListControls.d.ts.map +1 -0
  25. package/dist/ggnomad/utils/providerListControls.js +50 -0
  26. package/dist/ggnomad/utils/providerListControls.js.map +1 -0
  27. package/dist/ggnomad/utils/providerTypeMeta.d.ts +79 -0
  28. package/dist/ggnomad/utils/providerTypeMeta.d.ts.map +1 -0
  29. package/dist/ggnomad/utils/providerTypeMeta.js +81 -0
  30. package/dist/ggnomad/utils/providerTypeMeta.js.map +1 -0
  31. package/dist/shared/hooks/useTr.d.ts +17 -0
  32. package/dist/shared/hooks/useTr.d.ts.map +1 -0
  33. package/package.json +1 -1
@@ -0,0 +1,22 @@
1
+ interface CreateProviderDialogProps {
2
+ open: boolean;
3
+ onOpenChange: (open: boolean) => void;
4
+ /** Called with the raw backend response once the mutation succeeds, so the
5
+ * caller can decide whether to refetch, navigate, etc. */
6
+ onCreated?: (provider: {
7
+ id: string;
8
+ name: string;
9
+ type: string;
10
+ status: string;
11
+ }) => void;
12
+ }
13
+ /**
14
+ * Form for onboarding a new B2B provider (BOFF-8177). The backend
15
+ * `createGgnomadProvider` mutation is gated by `ggnomad:create`, which
16
+ * every managed GGNomad role holds by default. On submit, the new
17
+ * provider appears at `status: ACTIVE` immediately (the backend hardcodes
18
+ * this on create; see `service.createProvider`).
19
+ */
20
+ export declare function CreateProviderDialog({ open, onOpenChange, onCreated }: CreateProviderDialogProps): import("react").JSX.Element;
21
+ export {};
22
+ //# sourceMappingURL=CreateProviderDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateProviderDialog.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/CreateProviderDialog.tsx"],"names":[],"mappings":"AAmBA,UAAU,yBAAyB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC;+DAC2D;IAC3D,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC5F;AA8BD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,yBAAyB,+BAsRhG"}
@@ -0,0 +1,209 @@
1
+ import { CREATE_GGNOMAD_PROVIDER_MUTATION as e } from "../graphql.js";
2
+ import { notify as t } from "../utils/notify.js";
3
+ import { getProviderTypeOptions as n } from "../utils/providerTypeMeta.js";
4
+ import { useState as r } from "react";
5
+ import { Briefcase as i } from "lucide-react";
6
+ import { useI18n as a } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
+ import { useMutation as o } from "@apollo/client/react";
8
+ import { ResponsiveDialog as s, ResponsiveDialogClose as c, ResponsiveDialogContent as l, ResponsiveDialogDescription as u, ResponsiveDialogFooter as d, ResponsiveDialogHeader as f, ResponsiveDialogTitle as p } from "@burdenoff/fe-libs/ui";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ //#region src/ggnomad/components/CreateProviderDialog.tsx
11
+ var g = "mt-1 w-full rounded-md border border-border-seam bg-bg-canvas px-3 py-2 text-sm text-text-primary placeholder-text-secondary focus:border-action-primary-bg focus:outline-none focus:ring-1 focus:ring-action-primary-bg disabled:opacity-50", _ = "block text-sm font-medium text-text-primary", v = /^[^\s@]+@[^\s@]+\.[^\s@]+$/, y = /^(https?:\/\/)[^\s]+$/i, b = /[^0-9+\-().\s]/g, x = 7, S = 15;
12
+ function C(e) {
13
+ return e.replace(b, "");
14
+ }
15
+ function w({ open: b, onOpenChange: w, onCreated: T }) {
16
+ let { t: E } = a(), D = n(E), [O, k] = r(""), [A, j] = r(""), [M, N] = r(""), [P, F] = r(""), [I, L] = r(""), [R, z] = r(""), [B, { loading: V }] = o(e), H = () => {
17
+ k(""), j(""), N(""), F(""), L(""), z("");
18
+ }, U = async () => {
19
+ if (!O.trim()) {
20
+ t.warning(E("ggnomad.provider.createDialog.nameRequiredTitle", "Name is required"), E("ggnomad.provider.createDialog.nameRequiredBody", "Give this provider a recognizable name."));
21
+ return;
22
+ }
23
+ if (!A.trim()) {
24
+ t.warning(E("ggnomad.provider.createDialog.typeRequiredTitle", "Type is required"), E("ggnomad.provider.createDialog.typeRequiredBody", "Pick a provider type from the dropdown."));
25
+ return;
26
+ }
27
+ let e = M.trim();
28
+ if (e && !v.test(e)) {
29
+ t.warning(E("ggnomad.provider.createDialog.invalidEmailTitle", "Invalid email"), E("ggnomad.provider.createDialog.invalidEmailBody", "Enter a valid email address or leave the field blank."));
30
+ return;
31
+ }
32
+ let n = I.trim();
33
+ if (n && !y.test(n)) {
34
+ t.warning(E("ggnomad.provider.createDialog.invalidWebsiteTitle", "Invalid website"), E("ggnomad.provider.createDialog.invalidWebsiteBody", "Website must start with http:// or https://"));
35
+ return;
36
+ }
37
+ let r = P.trim(), i = r.replace(/\D/g, "").length;
38
+ if (r && (i < x || i > S)) {
39
+ t.warning(E("ggnomad.provider.createDialog.invalidPhoneTitle", "Invalid phone number"), E("ggnomad.provider.createDialog.invalidPhoneBody", "Enter a valid phone number or leave the field blank."));
40
+ return;
41
+ }
42
+ let a = {
43
+ name: O.trim(),
44
+ type: A,
45
+ contactEmail: e || null,
46
+ contactPhone: r || null,
47
+ website: n || null,
48
+ description: R.trim() || null
49
+ };
50
+ try {
51
+ let e = await B({ variables: { input: a } });
52
+ if (e.error) {
53
+ t.error(E("ggnomad.provider.createDialog.errorTitle", "Could not create provider"), e.error.message);
54
+ return;
55
+ }
56
+ let n = e.data?.createGgnomadProvider;
57
+ t.success(E("ggnomad.provider.createDialog.successTitle", "Provider created"), n?.name ?? O), H(), w(!1), n && T?.(n);
58
+ } catch (e) {
59
+ t.error(E("ggnomad.provider.createDialog.errorTitle", "Could not create provider"), e instanceof Error ? e.message : String(e));
60
+ }
61
+ };
62
+ return /* @__PURE__ */ m(s, {
63
+ open: b,
64
+ onOpenChange: (e) => {
65
+ e || H(), w(e);
66
+ },
67
+ children: /* @__PURE__ */ h(l, {
68
+ className: "max-w-2xl",
69
+ children: [
70
+ /* @__PURE__ */ h(f, { children: [/* @__PURE__ */ h(p, {
71
+ className: "flex items-center gap-2",
72
+ children: [/* @__PURE__ */ m(i, { className: "size-4" }), E("ggnomad.provider.createDialog.title", "New provider")]
73
+ }), /* @__PURE__ */ m(u, { children: E("ggnomad.provider.createDialog.description", "Onboard a new B2B supplier. They become ACTIVE immediately and can be selected as the host when you create listings.") })] }),
74
+ /* @__PURE__ */ h("div", {
75
+ className: "grid gap-4 sm:grid-cols-2",
76
+ children: [
77
+ /* @__PURE__ */ h("div", {
78
+ className: "sm:col-span-2",
79
+ children: [/* @__PURE__ */ h("label", {
80
+ htmlFor: "provider-name",
81
+ className: _,
82
+ children: [
83
+ E("ggnomad.provider.createDialog.nameLabel", "Provider name"),
84
+ " ",
85
+ /* @__PURE__ */ m("span", {
86
+ className: "text-action-danger-text",
87
+ children: "*"
88
+ })
89
+ ]
90
+ }), /* @__PURE__ */ m("input", {
91
+ id: "provider-name",
92
+ type: "text",
93
+ value: O,
94
+ onChange: (e) => k(e.target.value),
95
+ disabled: V,
96
+ className: g,
97
+ placeholder: E("ggnomad.provider.createDialog.namePlaceholder", "e.g. Alpine Treks Co.")
98
+ })]
99
+ }),
100
+ /* @__PURE__ */ h("div", {
101
+ className: "sm:col-span-2",
102
+ children: [/* @__PURE__ */ h("label", {
103
+ htmlFor: "provider-type",
104
+ className: _,
105
+ children: [
106
+ E("ggnomad.provider.createDialog.typeLabel", "Type"),
107
+ " ",
108
+ /* @__PURE__ */ m("span", {
109
+ className: "text-action-danger-text",
110
+ children: "*"
111
+ })
112
+ ]
113
+ }), /* @__PURE__ */ h("select", {
114
+ id: "provider-type",
115
+ value: A,
116
+ onChange: (e) => j(e.target.value),
117
+ disabled: V,
118
+ className: g,
119
+ children: [/* @__PURE__ */ m("option", {
120
+ value: "",
121
+ children: E("ggnomad.provider.createDialog.typePlaceholder", "Select a type…")
122
+ }), D.map((e) => /* @__PURE__ */ m("option", {
123
+ value: e.value,
124
+ children: e.label
125
+ }, e.value))]
126
+ })]
127
+ }),
128
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
129
+ htmlFor: "provider-email",
130
+ className: _,
131
+ children: E("ggnomad.provider.createDialog.emailLabel", "Contact email (optional)")
132
+ }), /* @__PURE__ */ m("input", {
133
+ id: "provider-email",
134
+ type: "email",
135
+ value: M,
136
+ onChange: (e) => N(e.target.value),
137
+ disabled: V,
138
+ className: g,
139
+ placeholder: "hello@alpinetreks.com"
140
+ })] }),
141
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
142
+ htmlFor: "provider-phone",
143
+ className: _,
144
+ children: E("ggnomad.provider.createDialog.phoneLabel", "Contact phone (optional)")
145
+ }), /* @__PURE__ */ m("input", {
146
+ id: "provider-phone",
147
+ type: "tel",
148
+ inputMode: "tel",
149
+ autoComplete: "tel",
150
+ value: P,
151
+ onChange: (e) => F(C(e.target.value)),
152
+ disabled: V,
153
+ className: g,
154
+ placeholder: "+91 98765 43210"
155
+ })] }),
156
+ /* @__PURE__ */ h("div", {
157
+ className: "sm:col-span-2",
158
+ children: [/* @__PURE__ */ m("label", {
159
+ htmlFor: "provider-website",
160
+ className: _,
161
+ children: E("ggnomad.provider.createDialog.websiteLabel", "Website (optional)")
162
+ }), /* @__PURE__ */ m("input", {
163
+ id: "provider-website",
164
+ type: "url",
165
+ value: I,
166
+ onChange: (e) => L(e.target.value),
167
+ disabled: V,
168
+ className: g,
169
+ placeholder: "https://alpinetreks.com"
170
+ })]
171
+ }),
172
+ /* @__PURE__ */ h("div", {
173
+ className: "sm:col-span-2",
174
+ children: [/* @__PURE__ */ m("label", {
175
+ htmlFor: "provider-description",
176
+ className: _,
177
+ children: E("ggnomad.provider.createDialog.descriptionLabel", "Description (optional)")
178
+ }), /* @__PURE__ */ m("textarea", {
179
+ id: "provider-description",
180
+ value: R,
181
+ onChange: (e) => z(e.target.value),
182
+ disabled: V,
183
+ rows: 3,
184
+ className: g,
185
+ placeholder: E("ggnomad.provider.createDialog.descriptionPlaceholder", "Short description shown to travelers on listings.")
186
+ })]
187
+ })
188
+ ]
189
+ }),
190
+ /* @__PURE__ */ h(d, { children: [/* @__PURE__ */ m(c, {
191
+ disabled: V,
192
+ children: E("ggnomad.provider.createDialog.cancel", "Cancel")
193
+ }), /* @__PURE__ */ m("button", {
194
+ type: "button",
195
+ onClick: () => {
196
+ U();
197
+ },
198
+ disabled: V || !O.trim() || !A.trim(),
199
+ className: "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50",
200
+ children: V ? E("ggnomad.provider.createDialog.creating", "Creating…") : E("ggnomad.provider.createDialog.create", "Create provider")
201
+ })] })
202
+ ]
203
+ })
204
+ });
205
+ }
206
+ //#endregion
207
+ export { w as CreateProviderDialog };
208
+
209
+ //# sourceMappingURL=CreateProviderDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateProviderDialog.js","names":[],"sources":["../../../src/ggnomad/components/CreateProviderDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Briefcase } from 'lucide-react';\nimport {\n ResponsiveDialog,\n ResponsiveDialogClose,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\nimport { CREATE_GGNOMAD_PROVIDER_MUTATION } from '../graphql';\nimport { notify } from '../utils/notify';\nimport { getProviderTypeOptions } from '../utils/providerTypeMeta';\n\ninterface CreateProviderDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Called with the raw backend response once the mutation succeeds, so the\n * caller can decide whether to refetch, navigate, etc. */\n onCreated?: (provider: { id: string; name: string; type: string; status: string }) => void;\n}\n\ninterface CreateGgnomadProviderInput {\n name: string;\n type: string;\n contactEmail?: string | null;\n contactPhone?: string | null;\n website?: string | null;\n description?: string | null;\n}\n\nconst fieldClass =\n 'mt-1 w-full rounded-md border border-border-seam bg-bg-canvas px-3 py-2 text-sm text-text-primary placeholder-text-secondary focus:border-action-primary-bg focus:outline-none focus:ring-1 focus:ring-action-primary-bg disabled:opacity-50';\nconst labelClass = 'block text-sm font-medium text-text-primary';\n\n// Lightweight format checks — anything stricter belongs in the backend.\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst URL_RE = /^(https?:\\/\\/)[^\\s]+$/i;\n\n// Phone inputs accept digits plus common dial/format punctuation only.\n// Letters are stripped as they are typed so invalid input never lands.\nconst PHONE_DISALLOWED_RE = /[^0-9+\\-().\\s]/g;\n// E.164: up to 15 digits; we require at least 7 to be a plausible number.\nconst PHONE_MIN_DIGITS = 7;\nconst PHONE_MAX_DIGITS = 15;\n\nfunction sanitizePhoneInput(value: string): string {\n return value.replace(PHONE_DISALLOWED_RE, '');\n}\n\n/**\n * Form for onboarding a new B2B provider (BOFF-8177). The backend\n * `createGgnomadProvider` mutation is gated by `ggnomad:create`, which\n * every managed GGNomad role holds by default. On submit, the new\n * provider appears at `status: ACTIVE` immediately (the backend hardcodes\n * this on create; see `service.createProvider`).\n */\nexport function CreateProviderDialog({ open, onOpenChange, onCreated }: CreateProviderDialogProps) {\n const { t } = useI18n();\n const providerTypeOptions = getProviderTypeOptions(t);\n const [name, setName] = useState('');\n const [type, setType] = useState('');\n const [contactEmail, setContactEmail] = useState('');\n const [contactPhone, setContactPhone] = useState('');\n const [website, setWebsite] = useState('');\n const [description, setDescription] = useState('');\n\n const [createProvider, { loading }] = useMutation(\n CREATE_GGNOMAD_PROVIDER_MUTATION as TypedDocumentNode<\n {\n createGgnomadProvider: {\n id: string;\n name: string;\n type: string;\n status: string;\n };\n },\n { input: CreateGgnomadProviderInput }\n >\n );\n\n const resetForm = () => {\n setName('');\n setType('');\n setContactEmail('');\n setContactPhone('');\n setWebsite('');\n setDescription('');\n };\n\n const handleCreate = async () => {\n if (!name.trim()) {\n notify.warning(\n t('ggnomad.provider.createDialog.nameRequiredTitle', 'Name is required'),\n t(\n 'ggnomad.provider.createDialog.nameRequiredBody',\n 'Give this provider a recognizable name.'\n )\n );\n return;\n }\n if (!type.trim()) {\n notify.warning(\n t('ggnomad.provider.createDialog.typeRequiredTitle', 'Type is required'),\n t(\n 'ggnomad.provider.createDialog.typeRequiredBody',\n 'Pick a provider type from the dropdown.'\n )\n );\n return;\n }\n\n const trimmedEmail = contactEmail.trim();\n if (trimmedEmail && !EMAIL_RE.test(trimmedEmail)) {\n notify.warning(\n t('ggnomad.provider.createDialog.invalidEmailTitle', 'Invalid email'),\n t(\n 'ggnomad.provider.createDialog.invalidEmailBody',\n 'Enter a valid email address or leave the field blank.'\n )\n );\n return;\n }\n const trimmedWebsite = website.trim();\n if (trimmedWebsite && !URL_RE.test(trimmedWebsite)) {\n notify.warning(\n t('ggnomad.provider.createDialog.invalidWebsiteTitle', 'Invalid website'),\n t(\n 'ggnomad.provider.createDialog.invalidWebsiteBody',\n 'Website must start with http:// or https://'\n )\n );\n return;\n }\n const trimmedPhone = contactPhone.trim();\n const phoneDigits = trimmedPhone.replace(/\\D/g, '').length;\n if (trimmedPhone && (phoneDigits < PHONE_MIN_DIGITS || phoneDigits > PHONE_MAX_DIGITS)) {\n notify.warning(\n t('ggnomad.provider.createDialog.invalidPhoneTitle', 'Invalid phone number'),\n t(\n 'ggnomad.provider.createDialog.invalidPhoneBody',\n 'Enter a valid phone number or leave the field blank.'\n )\n );\n return;\n }\n\n const input: CreateGgnomadProviderInput = {\n name: name.trim(),\n type: type as string,\n contactEmail: trimmedEmail || null,\n contactPhone: trimmedPhone || null,\n website: trimmedWebsite || null,\n description: description.trim() || null,\n };\n\n try {\n const result = await createProvider({ variables: { input } });\n // With errorPolicy: 'all' the mutation does not throw on failure; errors\n // surface on result.error. Without this check a failed create would\n // show \"Provider created\" and reset the form as if it had succeeded.\n if (result.error) {\n notify.error(\n t('ggnomad.provider.createDialog.errorTitle', 'Could not create provider'),\n result.error.message\n );\n return;\n }\n const created = result.data?.createGgnomadProvider;\n notify.success(\n t('ggnomad.provider.createDialog.successTitle', 'Provider created'),\n created?.name ?? name\n );\n resetForm();\n onOpenChange(false);\n if (created) onCreated?.(created);\n } catch (err) {\n notify.error(\n t('ggnomad.provider.createDialog.errorTitle', 'Could not create provider'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n if (!next) resetForm();\n onOpenChange(next);\n }}\n >\n <ResponsiveDialogContent className=\"max-w-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"flex items-center gap-2\">\n <Briefcase className=\"size-4\" />\n {t('ggnomad.provider.createDialog.title', 'New provider')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {t(\n 'ggnomad.provider.createDialog.description',\n 'Onboard a new B2B supplier. They become ACTIVE immediately and can be selected as the host when you create listings.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"sm:col-span-2\">\n <label htmlFor=\"provider-name\" className={labelClass}>\n {t('ggnomad.provider.createDialog.nameLabel', 'Provider name')}{' '}\n <span className=\"text-action-danger-text\">*</span>\n </label>\n <input\n id=\"provider-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n disabled={loading}\n className={fieldClass}\n placeholder={t(\n 'ggnomad.provider.createDialog.namePlaceholder',\n 'e.g. Alpine Treks Co.'\n )}\n />\n </div>\n\n <div className=\"sm:col-span-2\">\n <label htmlFor=\"provider-type\" className={labelClass}>\n {t('ggnomad.provider.createDialog.typeLabel', 'Type')}{' '}\n <span className=\"text-action-danger-text\">*</span>\n </label>\n <select\n id=\"provider-type\"\n value={type}\n onChange={(e) => setType(e.target.value as string)}\n disabled={loading}\n className={fieldClass}\n >\n <option value=\"\">\n {t('ggnomad.provider.createDialog.typePlaceholder', 'Select a type…')}\n </option>\n {providerTypeOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label htmlFor=\"provider-email\" className={labelClass}>\n {t('ggnomad.provider.createDialog.emailLabel', 'Contact email (optional)')}\n </label>\n <input\n id=\"provider-email\"\n type=\"email\"\n value={contactEmail}\n onChange={(e) => setContactEmail(e.target.value)}\n disabled={loading}\n className={fieldClass}\n placeholder=\"hello@alpinetreks.com\"\n />\n </div>\n\n <div>\n <label htmlFor=\"provider-phone\" className={labelClass}>\n {t('ggnomad.provider.createDialog.phoneLabel', 'Contact phone (optional)')}\n </label>\n <input\n id=\"provider-phone\"\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel\"\n value={contactPhone}\n onChange={(e) => setContactPhone(sanitizePhoneInput(e.target.value))}\n disabled={loading}\n className={fieldClass}\n placeholder=\"+91 98765 43210\"\n />\n </div>\n\n <div className=\"sm:col-span-2\">\n <label htmlFor=\"provider-website\" className={labelClass}>\n {t('ggnomad.provider.createDialog.websiteLabel', 'Website (optional)')}\n </label>\n <input\n id=\"provider-website\"\n type=\"url\"\n value={website}\n onChange={(e) => setWebsite(e.target.value)}\n disabled={loading}\n className={fieldClass}\n placeholder=\"https://alpinetreks.com\"\n />\n </div>\n\n <div className=\"sm:col-span-2\">\n <label htmlFor=\"provider-description\" className={labelClass}>\n {t('ggnomad.provider.createDialog.descriptionLabel', 'Description (optional)')}\n </label>\n <textarea\n id=\"provider-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n disabled={loading}\n rows={3}\n className={fieldClass}\n placeholder={t(\n 'ggnomad.provider.createDialog.descriptionPlaceholder',\n 'Short description shown to travelers on listings.'\n )}\n />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <ResponsiveDialogClose disabled={loading}>\n {t('ggnomad.provider.createDialog.cancel', 'Cancel')}\n </ResponsiveDialogClose>\n <button\n type=\"button\"\n onClick={() => {\n void handleCreate();\n }}\n disabled={loading || !name.trim() || !type.trim()}\n className=\"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50\"\n >\n {loading\n ? t('ggnomad.provider.createDialog.creating', 'Creating…')\n : t('ggnomad.provider.createDialog.create', 'Create provider')}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;;AAoCA,IAAM,IACJ,gPACI,IAAa,+CAGb,IAAW,8BACX,IAAS,0BAIT,IAAsB,mBAEtB,IAAmB,GACnB,IAAmB;AAEzB,SAAS,EAAmB,GAAuB;CACjD,OAAO,EAAM,QAAQ,GAAqB,EAAE;AAC9C;AASA,SAAgB,EAAqB,EAAE,SAAM,iBAAc,gBAAwC;CACjG,IAAM,EAAE,SAAM,EAAQ,GAChB,IAAsB,EAAuB,CAAC,GAC9C,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAM,KAAW,EAAS,EAAE,GAC7B,CAAC,GAAc,KAAmB,EAAS,EAAE,GAC7C,CAAC,GAAc,KAAmB,EAAS,EAAE,GAC7C,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAa,KAAkB,EAAS,EAAE,GAE3C,CAAC,GAAgB,EAAE,gBAAa,EACpC,CAWF,GAEM,UAAkB;EAMtB,AALA,EAAQ,EAAE,GACV,EAAQ,EAAE,GACV,EAAgB,EAAE,GAClB,EAAgB,EAAE,GAClB,EAAW,EAAE,GACb,EAAe,EAAE;CACnB,GAEM,IAAe,YAAY;EAC/B,IAAI,CAAC,EAAK,KAAK,GAAG;GAChB,EAAO,QACL,EAAE,mDAAmD,kBAAkB,GACvE,EACE,kDACA,yCACF,CACF;GACA;EACF;EACA,IAAI,CAAC,EAAK,KAAK,GAAG;GAChB,EAAO,QACL,EAAE,mDAAmD,kBAAkB,GACvE,EACE,kDACA,yCACF,CACF;GACA;EACF;EAEA,IAAM,IAAe,EAAa,KAAK;EACvC,IAAI,KAAgB,CAAC,EAAS,KAAK,CAAY,GAAG;GAChD,EAAO,QACL,EAAE,mDAAmD,eAAe,GACpE,EACE,kDACA,uDACF,CACF;GACA;EACF;EACA,IAAM,IAAiB,EAAQ,KAAK;EACpC,IAAI,KAAkB,CAAC,EAAO,KAAK,CAAc,GAAG;GAClD,EAAO,QACL,EAAE,qDAAqD,iBAAiB,GACxE,EACE,oDACA,6CACF,CACF;GACA;EACF;EACA,IAAM,IAAe,EAAa,KAAK,GACjC,IAAc,EAAa,QAAQ,OAAO,EAAE,EAAE;EACpD,IAAI,MAAiB,IAAc,KAAoB,IAAc,IAAmB;GACtF,EAAO,QACL,EAAE,mDAAmD,sBAAsB,GAC3E,EACE,kDACA,sDACF,CACF;GACA;EACF;EAEA,IAAM,IAAoC;GACxC,MAAM,EAAK,KAAK;GACV;GACN,cAAc,KAAgB;GAC9B,cAAc,KAAgB;GAC9B,SAAS,KAAkB;GAC3B,aAAa,EAAY,KAAK,KAAK;EACrC;EAEA,IAAI;GACF,IAAM,IAAS,MAAM,EAAe,EAAE,WAAW,EAAE,SAAM,EAAE,CAAC;GAI5D,IAAI,EAAO,OAAO;IAChB,EAAO,MACL,EAAE,4CAA4C,2BAA2B,GACzE,EAAO,MAAM,OACf;IACA;GACF;GACA,IAAM,IAAU,EAAO,MAAM;GAO7B,AANA,EAAO,QACL,EAAE,8CAA8C,kBAAkB,GAClE,GAAS,QAAQ,CACnB,GACA,EAAU,GACV,EAAa,EAAK,GACd,KAAS,IAAY,CAAO;EAClC,SAAS,GAAK;GACZ,EAAO,MACL,EAAE,4CAA4C,2BAA2B,GACzE,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG,CACjD;EACF;CACF;CAEA,OACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAS;GAEtB,AADK,KAAM,EAAU,GACrB,EAAa,CAAI;EACnB;YAEA,kBAAC,GAAD;GAAyB,WAAU;aAAnC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAuB,WAAU;eAAjC,CACE,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAC9B,EAAE,uCAAuC,cAAc,CACnC;QACvB,kBAAC,GAAD,EAAA,UACG,EACC,6CACA,sHACF,EAC2B,CAAA,CACP,EAAA,CAAA;IAExB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAgB,WAAW;kBAA1C;SACG,EAAE,2CAA2C,eAAe;SAAG;SAChE,kBAAC,QAAD;UAAM,WAAU;oBAA0B;SAAO,CAAA;QAC5C;WACP,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO;QACP,WAAW,MAAM,EAAQ,EAAE,OAAO,KAAK;QACvC,UAAU;QACV,WAAW;QACX,aAAa,EACX,iDACA,uBACF;OACD,CAAA,CACE;;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAgB,WAAW;kBAA1C;SACG,EAAE,2CAA2C,MAAM;SAAG;SACvD,kBAAC,QAAD;UAAM,WAAU;oBAA0B;SAAO,CAAA;QAC5C;WACP,kBAAC,UAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAQ,EAAE,OAAO,KAAe;QACjD,UAAU;QACV,WAAW;kBALb,CAOE,kBAAC,UAAD;SAAQ,OAAM;mBACX,EAAE,iDAAiD,gBAAgB;QAC9D,CAAA,GACP,EAAoB,KAAK,MACxB,kBAAC,UAAD;SAAwB,OAAO,EAAI;mBAChC,EAAI;QACC,GAFK,EAAI,KAET,CACT,CACK;SACL;;MAEL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAiB,WAAW;iBACxC,EAAE,4CAA4C,0BAA0B;MACpE,CAAA,GACP,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAgB,EAAE,OAAO,KAAK;OAC/C,UAAU;OACV,WAAW;OACX,aAAY;MACb,CAAA,CACE,EAAA,CAAA;MAEL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAiB,WAAW;iBACxC,EAAE,4CAA4C,0BAA0B;MACpE,CAAA,GACP,kBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,WAAU;OACV,cAAa;OACb,OAAO;OACP,WAAW,MAAM,EAAgB,EAAmB,EAAE,OAAO,KAAK,CAAC;OACnE,UAAU;OACV,WAAW;OACX,aAAY;MACb,CAAA,CACE,EAAA,CAAA;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAmB,WAAW;kBAC1C,EAAE,8CAA8C,oBAAoB;OAChE,CAAA,GACP,kBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO;QACP,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;QAC1C,UAAU;QACV,WAAW;QACX,aAAY;OACb,CAAA,CACE;;MAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAuB,WAAW;kBAC9C,EAAE,kDAAkD,wBAAwB;OACxE,CAAA,GACP,kBAAC,YAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;QAC9C,UAAU;QACV,MAAM;QACN,WAAW;QACX,aAAa,EACX,wDACA,mDACF;OACD,CAAA,CACE;;KACF;;IAEL,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAuB,UAAU;eAC9B,EAAE,wCAAwC,QAAQ;IAC9B,CAAA,GACvB,kBAAC,UAAD;KACE,MAAK;KACL,eAAe;MACb,EAAkB;KACpB;KACA,UAAU,KAAW,CAAC,EAAK,KAAK,KAAK,CAAC,EAAK,KAAK;KAChD,WAAU;eAET,IACG,EAAE,0CAA0C,WAAW,IACvD,EAAE,wCAAwC,iBAAiB;IACzD,CAAA,CACc,EAAA,CAAA;GACD;;CACT,CAAA;AAEtB"}
@@ -0,0 +1,28 @@
1
+ interface ProviderDashboardCardProps {
2
+ id: string;
3
+ name: string;
4
+ type: string;
5
+ status: string;
6
+ description?: string | null;
7
+ listingCount: number;
8
+ totalRevenue: number;
9
+ rating?: number | null;
10
+ reviewCount: number;
11
+ contactEmail?: string | null;
12
+ contactPhone?: string | null;
13
+ website?: string | null;
14
+ createdAt: string;
15
+ }
16
+ /**
17
+ * Provider Dashboard card. Distinct from the shared EntityCard on
18
+ * purpose: EntityCard is built for listing inventory with a 16:10
19
+ * cover image; providers use a flat banner + avatar treatment instead
20
+ * so each operator's card stands apart visually.
21
+ *
22
+ * The banner color is keyed by provider type so the tint reads as a
23
+ * type indicator. Green/red are intentionally not in the banner
24
+ * palette because green means "Active" and red means "error".
25
+ */
26
+ export declare function ProviderDashboardCard({ id, name, type, status, description, listingCount, totalRevenue, rating, reviewCount, contactEmail, contactPhone, website, createdAt, }: ProviderDashboardCardProps): import("react").JSX.Element;
27
+ export {};
28
+ //# sourceMappingURL=ProviderDashboardCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProviderDashboardCard.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/ProviderDashboardCard.tsx"],"names":[],"mappings":"AAaA,UAAU,0BAA0B;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,EAAE,EACF,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,SAAS,GACV,EAAE,0BAA0B,+BAmL5B"}
@@ -0,0 +1,222 @@
1
+ import { formatDate as e, formatRevenue as t } from "../utils/format.js";
2
+ import { bannerTintFor as n, initialsFor as r, labelForProviderType as i, sentenceCase as a } from "../utils/providerTypeMeta.js";
3
+ import { CalendarPlus as o, CircleDot as s, Globe as c, Mail as l, Phone as u, Star as d, Store as f } from "lucide-react";
4
+ import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
5
+ import { Link as m } from "react-router";
6
+ import { GlassCard as h } from "@burdenoff/fe-libs/ui";
7
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
8
+ //#region src/ggnomad/components/ProviderDashboardCard.tsx
9
+ function y({ id: a, name: s, type: g, status: y, description: T, listingCount: E, totalRevenue: D, rating: O, reviewCount: k, contactEmail: A, contactPhone: j, website: M, createdAt: N }) {
10
+ let { t: P } = p(), F = n(g), I = s, L = e(N), R = r(s), z = x(M), B = E >= 1e4 ? new Intl.NumberFormat("en-US", {
11
+ notation: "compact",
12
+ maximumFractionDigits: 1
13
+ }).format(E) : E.toLocaleString(), V = t(D), H = O && Number(O) > 0 ? Number(O).toFixed(1) : null;
14
+ return /* @__PURE__ */ v(h, {
15
+ treatment: "plain",
16
+ className: "flex h-full flex-col overflow-hidden rounded-2xl border border-border-seam",
17
+ children: [/* @__PURE__ */ v("div", {
18
+ className: "relative flex h-[84px] items-start justify-end p-3",
19
+ children: [/* @__PURE__ */ _("div", {
20
+ className: "absolute inset-0",
21
+ style: { backgroundColor: F.backgroundColor },
22
+ "aria-hidden": "true"
23
+ }), /* @__PURE__ */ _("div", {
24
+ className: "relative z-10",
25
+ children: /* @__PURE__ */ _(w, {
26
+ status: y,
27
+ t: P
28
+ })
29
+ })]
30
+ }), /* @__PURE__ */ v("div", {
31
+ className: "relative flex flex-1 flex-col px-[22px] pb-[22px] pt-0",
32
+ children: [
33
+ /* @__PURE__ */ _("div", {
34
+ className: "-mt-[38px] mb-3 flex h-[76px] w-[76px] items-center justify-center overflow-hidden rounded-full border-[4px] border-bg-canvas bg-bg-canvas",
35
+ children: /* @__PURE__ */ _("span", {
36
+ className: "flex size-full items-center justify-center text-2xl font-medium",
37
+ style: F,
38
+ children: R
39
+ })
40
+ }),
41
+ /* @__PURE__ */ _("p", {
42
+ className: "truncate text-xl font-medium text-text-primary",
43
+ title: I,
44
+ children: I
45
+ }),
46
+ /* @__PURE__ */ _("p", {
47
+ className: "mt-0.5 text-sm text-text-secondary",
48
+ children: i(g, P)
49
+ }),
50
+ /* @__PURE__ */ _("p", {
51
+ className: "mt-1.5 min-h-[20px] truncate text-sm " + (T ? "text-text-secondary" : "italic text-text-secondary/60"),
52
+ title: T ?? "",
53
+ children: T || P("ggnomad.provider.noDescription", "No description")
54
+ }),
55
+ /* @__PURE__ */ v("dl", {
56
+ className: "mt-5 grid border-y border-border-seam",
57
+ style: { gridTemplateColumns: "1fr 1.4fr 1fr" },
58
+ children: [
59
+ /* @__PURE__ */ _(S, {
60
+ label: P("ggnomad.provider.listings", "Listings"),
61
+ value: /* @__PURE__ */ _("span", {
62
+ title: String(E),
63
+ className: "min-w-0 truncate",
64
+ children: B
65
+ })
66
+ }),
67
+ /* @__PURE__ */ _(S, {
68
+ label: P("ggnomad.provider.revenue", "Revenue"),
69
+ border: !0,
70
+ value: /* @__PURE__ */ _("span", {
71
+ title: V.exact,
72
+ className: "min-w-0 truncate",
73
+ children: V.display
74
+ })
75
+ }),
76
+ /* @__PURE__ */ _(S, {
77
+ label: P("ggnomad.provider.rating", "Rating"),
78
+ border: !0,
79
+ value: H === null ? /* @__PURE__ */ _("span", {
80
+ className: "min-w-0 truncate text-text-secondary/70",
81
+ title: P("ggnomad.provider.noRatings", "No ratings yet"),
82
+ children: P("ggnomad.provider.ratingNew", "New")
83
+ }) : /* @__PURE__ */ v("span", {
84
+ className: "inline-flex items-center gap-1",
85
+ title: H + (k > 0 ? P("ggnomad.provider.reviewCount", " ({{n}} reviews)", { n: k }) : ""),
86
+ children: [/* @__PURE__ */ _(d, {
87
+ className: "size-4 fill-status-warning-text text-status-warning-text",
88
+ "aria-hidden": "true"
89
+ }), /* @__PURE__ */ _("span", {
90
+ className: "min-w-0 truncate",
91
+ children: H
92
+ })]
93
+ })
94
+ })
95
+ ]
96
+ }),
97
+ /* @__PURE__ */ v("div", {
98
+ className: "mt-4 space-y-2",
99
+ children: [
100
+ /* @__PURE__ */ _(C, {
101
+ icon: l,
102
+ value: A ?? null,
103
+ href: A ? `mailto:${A}` : null,
104
+ label: P("ggnomad.provider.contactEmail", "Email"),
105
+ t: P
106
+ }),
107
+ /* @__PURE__ */ _(C, {
108
+ icon: u,
109
+ value: j ?? null,
110
+ href: j ? `tel:${j}` : null,
111
+ label: P("ggnomad.provider.contactPhone", "Phone"),
112
+ t: P
113
+ }),
114
+ /* @__PURE__ */ _(C, {
115
+ icon: c,
116
+ value: b(M),
117
+ href: z,
118
+ label: P("ggnomad.provider.website", "Website"),
119
+ external: !0,
120
+ t: P
121
+ })
122
+ ]
123
+ }),
124
+ /* @__PURE__ */ v("footer", {
125
+ className: "mt-auto pt-5",
126
+ children: [/* @__PURE__ */ v("p", {
127
+ className: "flex items-center gap-1.5 text-[13px] text-text-secondary",
128
+ children: [/* @__PURE__ */ _(o, {
129
+ className: "size-3.5 shrink-0",
130
+ "aria-hidden": "true"
131
+ }), /* @__PURE__ */ _("span", { children: L ? P("ggnomad.provider.joined", "Joined {{date}}").replace("{{date}}", L) : P("ggnomad.provider.joinedEmpty", "Joined —") })]
132
+ }), /* @__PURE__ */ v(m, {
133
+ to: `${a}/storefront`,
134
+ className: "mt-2 flex h-[42px] w-full items-center justify-center gap-1.5 rounded-md border border-border-seam bg-bg-canvas text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas",
135
+ children: [/* @__PURE__ */ _(f, {
136
+ className: "size-4",
137
+ "aria-hidden": "true"
138
+ }), P("ggnomad.provider.manageStorefront", "Manage storefront")]
139
+ })]
140
+ })
141
+ ]
142
+ })]
143
+ });
144
+ }
145
+ function b(e) {
146
+ return e ? e.replace(/^https?:\/\//i, "").replace(/\/+$/, "") : null;
147
+ }
148
+ function x(e) {
149
+ return e && /^https?:\/\//i.test(e) ? e : null;
150
+ }
151
+ function S({ label: e, value: t, border: n }) {
152
+ return /* @__PURE__ */ v("div", {
153
+ className: "flex flex-col items-start py-2.5 pr-3 " + (n ? "border-l border-border-seam pl-3.5" : "pl-0"),
154
+ children: [/* @__PURE__ */ _("dt", {
155
+ className: "text-xs text-text-secondary",
156
+ children: e
157
+ }), /* @__PURE__ */ _("dd", {
158
+ className: "mt-0.5 text-[22px] font-medium tabular-nums text-text-primary",
159
+ children: t
160
+ })]
161
+ });
162
+ }
163
+ function C({ icon: e, value: t, href: n, label: r, external: i, t: a }) {
164
+ let o = !!(n && t), s = /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(e, {
165
+ className: "size-4 shrink-0 text-text-secondary",
166
+ "aria-hidden": "true"
167
+ }), /* @__PURE__ */ _("span", {
168
+ className: "min-w-0 truncate " + (o ? "text-action-primary-bg hover:underline" : "text-text-secondary/60"),
169
+ title: t ?? a("ggnomad.provider.notAddedFor", "{{label}} not added", { label: r }),
170
+ children: t ?? a("ggnomad.provider.notAdded", "Not added")
171
+ })] }), c = "flex h-[22px] items-center gap-2 text-sm";
172
+ return o && n ? /* @__PURE__ */ _("a", {
173
+ href: n,
174
+ target: i ? "_blank" : void 0,
175
+ rel: i ? "noreferrer noopener" : void 0,
176
+ className: c,
177
+ children: s
178
+ }) : /* @__PURE__ */ _("div", {
179
+ className: c,
180
+ style: { cursor: "default" },
181
+ children: s
182
+ });
183
+ }
184
+ function w({ status: e, t }) {
185
+ let { bg: n, fg: r, label: i } = T(e, t);
186
+ return /* @__PURE__ */ v("span", {
187
+ className: "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium " + n + " " + r,
188
+ children: [/* @__PURE__ */ _(s, {
189
+ className: "size-3",
190
+ "aria-hidden": "true"
191
+ }), i]
192
+ });
193
+ }
194
+ function T(e, t) {
195
+ switch (e) {
196
+ case "ACTIVE": return {
197
+ bg: "bg-status-success-bg-subtle border-status-success-text/40",
198
+ fg: "text-status-success-text",
199
+ label: t("ggnomad.provider.statusActive", "Active")
200
+ };
201
+ case "PENDING": return {
202
+ bg: "bg-status-warning-bg-subtle border-status-warning-text/40",
203
+ fg: "text-status-warning-text",
204
+ label: t("ggnomad.provider.statusPending", "Pending")
205
+ };
206
+ case "SUSPENDED":
207
+ case "ARCHIVED": return {
208
+ bg: "bg-status-error-bg-subtle border-status-error-text/40",
209
+ fg: "text-status-error-text",
210
+ label: t("ggnomad.provider.status" + a(e), a(e))
211
+ };
212
+ default: return {
213
+ bg: "bg-bg-sunken border-border-seam",
214
+ fg: "text-text-secondary",
215
+ label: a(e) || e
216
+ };
217
+ }
218
+ }
219
+ //#endregion
220
+ export { y as ProviderDashboardCard };
221
+
222
+ //# sourceMappingURL=ProviderDashboardCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProviderDashboardCard.js","names":[],"sources":["../../../src/ggnomad/components/ProviderDashboardCard.tsx"],"sourcesContent":["import { Link } from 'react-router';\nimport { CalendarPlus, CircleDot, Globe, Mail, Phone, Star, Store } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nimport { formatDate, formatRevenue } from '../utils/format';\nimport {\n bannerTintFor,\n initialsFor,\n labelForProviderType,\n sentenceCase,\n} from '../utils/providerTypeMeta';\n\ninterface ProviderDashboardCardProps {\n id: string;\n name: string;\n type: string;\n status: string;\n description?: string | null;\n listingCount: number;\n totalRevenue: number;\n rating?: number | null;\n reviewCount: number;\n contactEmail?: string | null;\n contactPhone?: string | null;\n website?: string | null;\n createdAt: string;\n}\n\n/**\n * Provider Dashboard card. Distinct from the shared EntityCard on\n * purpose: EntityCard is built for listing inventory with a 16:10\n * cover image; providers use a flat banner + avatar treatment instead\n * so each operator's card stands apart visually.\n *\n * The banner color is keyed by provider type so the tint reads as a\n * type indicator. Green/red are intentionally not in the banner\n * palette because green means \"Active\" and red means \"error\".\n */\nexport function ProviderDashboardCard({\n id,\n name,\n type,\n status,\n description,\n listingCount,\n totalRevenue,\n rating,\n reviewCount,\n contactEmail,\n contactPhone,\n website,\n createdAt,\n}: ProviderDashboardCardProps) {\n const { t } = useI18n();\n const tint = bannerTintFor(type);\n const displayName = name;\n const joined = formatDate(createdAt);\n const initials = initialsFor(name);\n const websiteHref = safeWebsiteHref(website);\n\n const listingsDisplay =\n listingCount >= 10_000\n ? new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: 1,\n }).format(listingCount)\n : listingCount.toLocaleString();\n const revenue = formatRevenue(totalRevenue);\n const ratingDisplay = rating && Number(rating) > 0 ? Number(rating).toFixed(1) : null;\n\n return (\n <GlassCard\n treatment=\"plain\"\n className=\"flex h-full flex-col overflow-hidden rounded-2xl border border-border-seam\"\n >\n {/* 1. BANNER — solid tinted band, status pill pinned top-right.\n * The aria-hidden wrapper carries only the decorative background\n * so the status badge (its sibling) stays in the accessibility\n * tree — without this scoping, the badge's status text would be\n * invisible to screen readers because aria-hidden cascades to\n * all descendants. */}\n <div className=\"relative flex h-[84px] items-start justify-end p-3\">\n <div\n className=\"absolute inset-0\"\n style={{ backgroundColor: tint.backgroundColor }}\n aria-hidden=\"true\"\n />\n <div className=\"relative z-10\">\n <ProviderDashboardStatusBadge status={status} t={t} />\n </div>\n </div>\n\n {/* Body. The negative top margin pulls the avatar up so it overlaps\n * the banner by ~half its diameter. min-h-[96px] on the avatar row\n * reserves the vertical space so the name/type rows below don't\n * shift on cards without descriptions or contact rows. */}\n <div className=\"relative flex flex-1 flex-col px-[22px] pb-[22px] pt-0\">\n {/* 3. AVATAR — 76px circle, 4px ring in the card surface color. */}\n <div className=\"-mt-[38px] mb-3 flex h-[76px] w-[76px] items-center justify-center overflow-hidden rounded-full border-[4px] border-bg-canvas bg-bg-canvas\">\n <span\n className=\"flex size-full items-center justify-center text-2xl font-medium\"\n style={tint}\n >\n {initials}\n </span>\n </div>\n\n {/* 4. IDENTITY BLOCK — name, type, description (single line, fixed\n * min-height so all cards align). */}\n <p className=\"truncate text-xl font-medium text-text-primary\" title={displayName}>\n {displayName}\n </p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{labelForProviderType(type, t)}</p>\n <p\n className={\n 'mt-1.5 min-h-[20px] truncate text-sm ' +\n (description ? 'text-text-secondary' : 'italic text-text-secondary/60')\n }\n title={description ?? ''}\n >\n {description || t('ggnomad.provider.noDescription', 'No description')}\n </p>\n\n {/* 5. STATS ROW — three columns, hairline borders above + below and\n * between, no individual tiles. Revenue column gets extra room.\n * Every value gets min-w-0 truncate as a safety net for extreme\n * strings. */}\n <dl\n className=\"mt-5 grid border-y border-border-seam\"\n style={{ gridTemplateColumns: '1fr 1.4fr 1fr' }}\n >\n <ProviderDashboardStat\n label={t('ggnomad.provider.listings', 'Listings')}\n value={\n <span title={String(listingCount)} className=\"min-w-0 truncate\">\n {listingsDisplay}\n </span>\n }\n />\n <ProviderDashboardStat\n label={t('ggnomad.provider.revenue', 'Revenue')}\n border\n value={\n <span title={revenue.exact} className=\"min-w-0 truncate\">\n {revenue.display}\n </span>\n }\n />\n <ProviderDashboardStat\n label={t('ggnomad.provider.rating', 'Rating')}\n border\n value={\n ratingDisplay !== null ? (\n <span\n className=\"inline-flex items-center gap-1\"\n title={\n ratingDisplay +\n (reviewCount > 0\n ? t('ggnomad.provider.reviewCount', ' ({{n}} reviews)', { n: reviewCount })\n : '')\n }\n >\n <Star\n className=\"size-4 fill-status-warning-text text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <span className=\"min-w-0 truncate\">{ratingDisplay}</span>\n </span>\n ) : (\n <span\n className=\"min-w-0 truncate text-text-secondary/70\"\n title={t('ggnomad.provider.noRatings', 'No ratings yet')}\n >\n {t('ggnomad.provider.ratingNew', 'New')}\n </span>\n )\n }\n />\n </dl>\n\n {/* 6. CONTACT ROWS — ALWAYS exactly three, always rendered with the\n * same icon and 22px height so all cards in a row are the same\n * height. Missing values render as \"Not added\" in muted color, no\n * link, no hover, no pointer cursor. */}\n <div className=\"mt-4 space-y-2\">\n <ProviderDashboardContactRow\n icon={Mail}\n value={contactEmail ?? null}\n href={contactEmail ? `mailto:${contactEmail}` : null}\n label={t('ggnomad.provider.contactEmail', 'Email')}\n t={t}\n />\n <ProviderDashboardContactRow\n icon={Phone}\n value={contactPhone ?? null}\n href={contactPhone ? `tel:${contactPhone}` : null}\n label={t('ggnomad.provider.contactPhone', 'Phone')}\n t={t}\n />\n <ProviderDashboardContactRow\n icon={Globe}\n value={formatWebsiteDisplay(website)}\n href={websiteHref}\n label={t('ggnomad.provider.website', 'Website')}\n external\n t={t}\n />\n </div>\n\n {/* 7. FOOTER — pinned to the bottom of the card. Date on its own\n * line, then a full-width outlined secondary button. */}\n <footer className=\"mt-auto pt-5\">\n <p className=\"flex items-center gap-1.5 text-[13px] text-text-secondary\">\n <CalendarPlus className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span>\n {joined\n ? t('ggnomad.provider.joined', 'Joined {{date}}').replace('{{date}}', joined)\n : t('ggnomad.provider.joinedEmpty', 'Joined —')}\n </span>\n </p>\n <Link\n to={`${id}/storefront`}\n className=\"mt-2 flex h-[42px] w-full items-center justify-center gap-1.5 rounded-md border border-border-seam bg-bg-canvas text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas\"\n >\n <Store className=\"size-4\" aria-hidden=\"true\" />\n {t('ggnomad.provider.manageStorefront', 'Manage storefront')}\n </Link>\n </footer>\n </div>\n </GlassCard>\n );\n}\n\n/**\n * Strip the protocol and trailing slash from a URL for display, e.g.\n * \"https://chrisleverseo.com/tools/random-url-generator/\" becomes\n * \"chrisleverseo.com/tools/random-url-generator\". Never used for the\n * actual href, only the visible label, so this can be aggressive.\n */\nfunction formatWebsiteDisplay(url: string | null | undefined): string | null {\n if (!url) return null;\n return url.replace(/^https?:\\/\\//i, '').replace(/\\/+$/, '');\n}\n\n/**\n * Only return the URL as a clickable href if it starts with http(s)://.\n * Providers created via CLI/MCP don't go through the create-dialog input\n * validator, so a malformed value like \"acme.com\" would otherwise be passed\n * to the anchor's `href` and break navigation. Returns null otherwise so\n * the row renders as plain text instead.\n */\nfunction safeWebsiteHref(url: string | null | undefined): string | null {\n if (!url) return null;\n return /^https?:\\/\\//i.test(url) ? url : null;\n}\n\ninterface StatProps {\n label: string;\n value: React.ReactNode;\n /** Render the left vertical divider for non-first columns. */\n border?: boolean;\n}\n\nfunction ProviderDashboardStat({ label, value, border }: StatProps) {\n // Spec: each column left-aligns label and value, gets 14px left padding\n // except the first, and a hairline vertical divider on cols 2 & 3. The\n // parent <dl> already supplies the top + bottom border; this cell only\n // owns its left divider + padding. Right-edge padding is added in the\n // Rating column (the last one) so the row is balanced.\n return (\n <div\n className={\n 'flex flex-col items-start py-2.5 pr-3 ' +\n (border ? 'border-l border-border-seam pl-3.5' : 'pl-0')\n }\n >\n <dt className=\"text-xs text-text-secondary\">{label}</dt>\n <dd className=\"mt-0.5 text-[22px] font-medium tabular-nums text-text-primary\">{value}</dd>\n </div>\n );\n}\n\ninterface ContactRowProps {\n icon: typeof Mail;\n value: string | null;\n href: string | null;\n label: string;\n external?: boolean;\n t: (key: string, fallback: string, vars?: Record<string, string | number>) => string;\n}\n\nfunction ProviderDashboardContactRow({\n icon: Icon,\n value,\n href,\n label,\n external,\n t,\n}: ContactRowProps) {\n const isLink = Boolean(href && value);\n const content = (\n <>\n <Icon className=\"size-4 shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span\n className={\n 'min-w-0 truncate ' +\n (isLink ? 'text-action-primary-bg hover:underline' : 'text-text-secondary/60')\n }\n title={value ?? t('ggnomad.provider.notAddedFor', '{{label}} not added', { label })}\n >\n {value ?? t('ggnomad.provider.notAdded', 'Not added')}\n </span>\n </>\n );\n\n const rowStyle = 'flex h-[22px] items-center gap-2 text-sm';\n\n if (isLink && href) {\n return (\n <a\n href={href}\n target={external ? '_blank' : undefined}\n rel={external ? 'noreferrer noopener' : undefined}\n className={rowStyle}\n >\n {content}\n </a>\n );\n }\n // Missing value — render as plain text, no link, no hover, no pointer.\n return (\n <div className={rowStyle} style={{ cursor: 'default' }}>\n {content}\n </div>\n );\n}\n\ninterface StatusBadgeProps {\n status: string;\n t: (key: string, fallback: string) => string;\n}\n\n/**\n * Small status pill that lives inside the banner top-right. Inline here\n * (not the shared StatusBadge) because the shared component takes the\n * raw uppercase enum and renders the dot inline with the label; we\n * want sentence-case for the card.\n */\nfunction ProviderDashboardStatusBadge({ status, t }: StatusBadgeProps) {\n const { bg, fg, label } = statusTone(status, t);\n return (\n <span\n className={\n 'inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium ' +\n bg +\n ' ' +\n fg\n }\n >\n <CircleDot className=\"size-3\" aria-hidden=\"true\" />\n {label}\n </span>\n );\n}\n\ninterface StatusTone {\n bg: string;\n fg: string;\n label: string;\n}\n\nfunction statusTone(status: string, t: (key: string, fallback: string) => string): StatusTone {\n switch (status) {\n case 'ACTIVE':\n return {\n bg: 'bg-status-success-bg-subtle border-status-success-text/40',\n fg: 'text-status-success-text',\n label: t('ggnomad.provider.statusActive', 'Active'),\n };\n case 'PENDING':\n return {\n bg: 'bg-status-warning-bg-subtle border-status-warning-text/40',\n fg: 'text-status-warning-text',\n label: t('ggnomad.provider.statusPending', 'Pending'),\n };\n case 'SUSPENDED':\n case 'ARCHIVED':\n return {\n bg: 'bg-status-error-bg-subtle border-status-error-text/40',\n fg: 'text-status-error-text',\n label: t('ggnomad.provider.status' + sentenceCase(status), sentenceCase(status)),\n };\n default:\n return {\n bg: 'bg-bg-sunken border-border-seam',\n fg: 'text-text-secondary',\n label: sentenceCase(status) || status,\n };\n }\n}\n"],"mappings":"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"}
@@ -9,6 +9,7 @@ export { BookingPanel } from './BookingPanel';
9
9
  export { ReviewsSection } from './ReviewsSection';
10
10
  export { ListingEditDialog } from './ListingEditDialog';
11
11
  export { CreateTravelPolicyDialog } from './CreateTravelPolicyDialog';
12
+ export { CreateProviderDialog } from './CreateProviderDialog';
12
13
  export { TicketDialog } from './TicketDialog';
13
14
  export type { GgnomadTicket } from './TicketDialog';
14
15
  export { RedeemTicketPanel } from './RedeemTicketPanel';
@@ -21,6 +22,7 @@ export { RatingSummary } from './RatingSummary';
21
22
  export { EcoAccessPanel } from './EcoAccessPanel';
22
23
  export { ProviderCard } from './ProviderCard';
23
24
  export type { ListingProvider } from './ProviderCard';
25
+ export { ProviderDashboardCard } from './ProviderDashboardCard';
24
26
  export { AvailabilityPanel } from './AvailabilityPanel';
25
27
  export { ListingFilters, ACCESSIBILITY_FEATURES, EMPTY_LISTING_FILTERS, hasActiveListingFilters, } from './ListingFilters';
26
28
  export type { ListingFilterState, ListingStatusFilter } from './ListingFilters';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
@@ -9,6 +9,7 @@ import "./BookingPanel.js";
9
9
  import "./ReviewsSection.js";
10
10
  import "./ListingEditDialog.js";
11
11
  import "./CreateTravelPolicyDialog.js";
12
+ import "./CreateProviderDialog.js";
12
13
  import "./TicketDialog.js";
13
14
  import "./RedeemTicketPanel.js";
14
15
  import "./AuditTrailDialog.js";
@@ -18,6 +19,7 @@ import "./FactGrid.js";
18
19
  import "./RatingSummary.js";
19
20
  import "./EcoAccessPanel.js";
20
21
  import "./ProviderCard.js";
22
+ import "./ProviderDashboardCard.js";
21
23
  import "./AvailabilityPanel.js";
22
24
  import "./ListingFilters.js";
23
25
  import "./GroupTripPanel.js";
@@ -40,6 +40,7 @@ export declare const UPDATE_GGNOMAD_TRIP_PLAN_MUTATION: import('graphql').Docume
40
40
  export declare const DELETE_GGNOMAD_TRIP_PLAN_MUTATION: import('graphql').DocumentNode;
41
41
  export declare const ADD_GGNOMAD_TRIP_PLAN_ITEM_MUTATION: import('graphql').DocumentNode;
42
42
  export declare const REMOVE_GGNOMAD_TRIP_PLAN_ITEM_MUTATION: import('graphql').DocumentNode;
43
+ export declare const CREATE_GGNOMAD_PROVIDER_MUTATION: import('graphql').DocumentNode;
43
44
  export declare const GGNOMAD_REVIEW_SUMMARY_QUERY: import('graphql').DocumentNode;
44
45
  export declare const GGNOMAD_REVIEWS_QUERY: import('graphql').DocumentNode;
45
46
  export declare const CREATE_GGNOMAD_REVIEW_MUTATION: import('graphql').DocumentNode;