@burdenoff/microfe-devportal 2026.1110.2 → 2026.1112.2
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/devportal/hooks/permissionRules.d.ts +1 -0
- package/dist/devportal/hooks/permissionRules.js +13 -0
- package/dist/devportal/hooks/permissionRules.js.map +1 -0
- package/dist/devportal/hooks/usePermission.js +9 -6
- package/dist/devportal/hooks/usePermission.js.map +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.js +120 -114
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +84 -73
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +134 -115
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.js +141 -143
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.d.ts +5 -0
- package/dist/devportal/providers/DevportalProvider.js +33 -32
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/devportal/types/index.d.ts +6 -0
- package/dist/devportal/types/index.js.map +1 -1
- package/dist/devportal/utils/errorMessage.d.ts +2 -0
- package/dist/devportal/utils/errorMessage.js +7 -0
- package/dist/devportal/utils/errorMessage.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateOrganizationPage.js","names":["_Fragment","CreateOrganizationPage","useDevportal","isSeller","useIsSellerSection","tr","useTr","errors","setErrors","useState","createOrganization","useCreateOrganizationMutation","createSellerOrganization","useCreateSellerOrganizationMutation","createBillingAccountMutation","useCreateBillingAccountMutation","linkBillingAccount","useLinkDevOrgBillingAccountMutation","bus","useDevportalEventBus","phase","setPhase","isSubmitting","billingChoice","setBillingChoice","selectedBillingAccountId","setSelectedBillingAccountId","billingAccountsData","billingAccountsLoading","useGetBillingAccountsByOrgQuery","tenantId","existingBillingAccounts","useMemo","a","formData","setFormData","handleNavigate","basePath","path","generateSlug","name","handleNameChange","prev","handleChange","field","value","validate","newErrors","PermissionGuard","PageLayout","orgSectionPath","input","orgId","setActivePhysicalOrgId","setActiveOrgId","billingAccountId","currentUser","error","PageSection","InputField","TextareaField","cn","Check","Link2","account","Loader2","X","Fragment","Building2"],"sources":["../../../src/devportal/pages/CreateOrganizationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { X, Building2, Check, Link2, Loader2 } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport {\n useIsSellerSection,\n orgSectionPath,\n} from \"../hooks/useIsSellerSection\";\nimport {\n useCreateOrganizationMutation,\n useCreateSellerOrganizationMutation,\n useGetBillingAccountsByOrgQuery,\n useLinkDevOrgBillingAccountMutation,\n} from \"../../generated/global-operations\";\nimport { useCreateBillingAccountMutation } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\n\ninterface CreateOrganizationFormData {\n name: string;\n slug: string;\n description: string;\n websiteUrl: string;\n contactEmail: string;\n supportEmail: string;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n contactEmail?: string;\n supportEmail?: string;\n}\n\ntype SubmitPhase =\n | \"idle\"\n | \"creating-org\"\n | \"setting-up-billing\"\n | \"linking-billing\"\n | \"redirecting\";\n\n/**\n * Create new organization page — single step.\n * The user can either have a billing account auto-created and linked, or\n * link one of their existing billing accounts up front. Legal/tax details\n * are completed later from organization settings.\n */\nexport const CreateOrganizationPage: FC = () => {\n const {\n navigate,\n basePath,\n currentUser,\n tenantId,\n setActiveOrgId,\n setActivePhysicalOrgId,\n } = useDevportal();\n const isSeller = useIsSellerSection();\n const tr = useTr();\n const [errors, setErrors] = useState<FormErrors>({});\n // Shared page for both kinds — which mutation runs is the trust boundary\n // for DeveloperOrgKind (BOFF-7285): createSellerOrganization always makes\n // a PHYSICAL org, createDeveloperOrganization always makes DIGITAL. Both\n // hooks are called unconditionally (rules of hooks); only one is invoked.\n const [createOrganization] = useCreateOrganizationMutation();\n const [createSellerOrganization] = useCreateSellerOrganizationMutation();\n const [createBillingAccountMutation] = useCreateBillingAccountMutation();\n const [linkBillingAccount] = useLinkDevOrgBillingAccountMutation();\n const bus = useDevportalEventBus();\n\n // Multi-step status so the button/banner reflect the whole create flow —\n // not just the org-creation mutation's own loading flag, which flips back\n // to false before the (awaited) billing setup/link steps finish.\n const [phase, setPhase] = useState<SubmitPhase>(\"idle\");\n const isSubmitting = phase !== \"idle\";\n\n const [billingChoice, setBillingChoice] = useState<\"auto\" | \"existing\">(\n \"auto\",\n );\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<\n string | null\n >(null);\n const { data: billingAccountsData, loading: billingAccountsLoading } =\n useGetBillingAccountsByOrgQuery({\n fetchPolicy: \"cache-and-network\",\n context: { headers: { \"x-org-id\": tenantId ?? \"\" } },\n });\n type BillingAccount = NonNullable<\n typeof billingAccountsData\n >[\"getBillingAccountsByOrg\"][number];\n const existingBillingAccounts: BillingAccount[] = useMemo(\n () => billingAccountsData?.getBillingAccountsByOrg ?? [],\n [billingAccountsData],\n );\n\n // Keep the \"existing account\" choice valid as the list loads: fall back to\n // auto-create if it turns out empty, and default-select once it arrives.\n useEffect(() => {\n if (billingChoice !== \"existing\" || billingAccountsLoading) return;\n if (existingBillingAccounts.length === 0) {\n setBillingChoice(\"auto\");\n return;\n }\n if (!selectedBillingAccountId) {\n setSelectedBillingAccountId(\n existingBillingAccounts.find((a) => a.isDefault)?.id ??\n existingBillingAccounts[0]?.id ??\n null,\n );\n }\n }, [\n billingChoice,\n billingAccountsLoading,\n existingBillingAccounts,\n selectedBillingAccountId,\n ]);\n\n const [formData, setFormData] = useState<CreateOrganizationFormData>({\n name: \"\",\n slug: \"\",\n description: \"\",\n websiteUrl: \"\",\n contactEmail: \"\",\n supportEmail: \"\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const generateSlug = (name: string): string => {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n };\n\n const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setFormData((prev) => ({\n ...prev,\n name,\n slug: prev.slug || generateSlug(name),\n }));\n if (errors.name) {\n setErrors((prev) => ({ ...prev, name: undefined }));\n }\n };\n\n const handleChange = (\n field: keyof CreateOrganizationFormData,\n value: string,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field as keyof FormErrors]) {\n setErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n };\n\n const validate = (): boolean => {\n const newErrors: FormErrors = {};\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.orgNameRequired\",\n \"Organization name is required\",\n );\n } else if (formData.name.length < 2) {\n newErrors.name = tr(\n \"devportal.orgNameTooShort\",\n \"Name must be at least 2 characters\",\n );\n } else if (formData.name.length > 100) {\n newErrors.name = tr(\n \"devportal.nameTooLong\",\n \"Name must be less than 100 characters\",\n );\n }\n\n if (!formData.slug.trim()) {\n newErrors.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug = tr(\n \"devportal.slugInvalid\",\n \"Slug can only contain lowercase letters, numbers, and hyphens\",\n );\n }\n\n if (\n formData.contactEmail &&\n !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.contactEmail)\n ) {\n newErrors.contactEmail = tr(\n \"devportal.invalidEmailAddress\",\n \"Invalid email address\",\n );\n }\n\n if (\n formData.supportEmail &&\n !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.supportEmail)\n ) {\n newErrors.supportEmail = tr(\n \"devportal.invalidEmailAddress\",\n \"Invalid email address\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validate()) return;\n\n setPhase(\"creating-org\");\n try {\n const input = {\n name: formData.name,\n slug: formData.slug || undefined,\n description: formData.description || undefined,\n websiteUrl: formData.websiteUrl || undefined,\n contactEmail: formData.contactEmail || undefined,\n supportEmail: formData.supportEmail || undefined,\n };\n\n const orgId = isSeller\n ? (await createSellerOrganization({ variables: { input } })).data\n ?.createSellerOrganization?.id\n : (await createOrganization({ variables: { input } })).data\n ?.createDeveloperOrganization?.id;\n\n if (!orgId) {\n setPhase(\"idle\");\n return;\n }\n\n bus.emit(\"devportal.org.created\", {\n organizationId: orgId,\n name: formData.name,\n });\n if (isSeller) {\n setActivePhysicalOrgId(orgId);\n } else {\n setActiveOrgId(orgId);\n }\n\n if (billingChoice === \"existing\" && selectedBillingAccountId) {\n setPhase(\"linking-billing\");\n try {\n await linkBillingAccount({\n variables: {\n organizationId: orgId,\n billingAccountId: selectedBillingAccountId,\n },\n });\n } catch {\n // non-blocking — can be linked later from the Revenue tab\n }\n } else {\n setPhase(\"setting-up-billing\");\n try {\n const billingResult = await createBillingAccountMutation({\n variables: {\n input: {\n name: formData.name,\n email: currentUser?.email ?? \"\",\n },\n },\n });\n const billingAccountId = billingResult.data?.createBillingAccount?.id;\n if (billingAccountId) {\n await linkBillingAccount({\n variables: { organizationId: orgId, billingAccountId },\n });\n }\n } catch {\n // non-blocking — can be linked later from the Revenue tab\n }\n }\n\n setPhase(\"redirecting\");\n handleNavigate(orgSectionPath(isSeller, orgId));\n } catch (error) {\n console.error(\"Failed to create organization:\", error);\n setPhase(\"idle\");\n }\n };\n\n const handleCancel = () => {\n handleNavigate(orgSectionPath(isSeller));\n };\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.createOrganization\", \"Create Organization\")}\n description={\n isSeller\n ? tr(\n \"devportal.createSellerOrganizationDescription\",\n \"Set up a seller organization to start selling physical products\",\n )\n : tr(\n \"devportal.createOrganizationDescription\",\n \"Set up a developer organization to collaborate with your team\",\n )\n }\n purpose={\n isSeller\n ? tr(\n \"devportal.createSellerOrganizationPurpose\",\n \"A seller organization is the shared owner of your physical listings, orders, and revenue. Create one here — you can invite members and complete legal/tax details afterwards.\",\n )\n : tr(\n \"devportal.createOrganizationPurpose\",\n \"An organization is the shared owner of your apps, members, and revenue. Create one here so your team works under a single identity — pick a name and tier now; you can invite members and verify it afterwards.\",\n )\n }\n backLink={`${basePath}${orgSectionPath(isSeller)}`}\n backLabel={tr(\"devportal.backToOrganizations\", \"Back to Organizations\")}\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {/* Organization Details */}\n <PageSection\n title={tr(\"devportal.organizationDetails\", \"Organization Details\")}\n description={tr(\n \"devportal.organizationDetailsDescription\",\n \"Basic information about your organization\",\n )}\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.organizationName\", \"Organization Name\")}\n value={formData.name}\n onChange={handleNameChange}\n placeholder={tr(\n \"devportal.organizationNamePlaceholder\",\n \"Acme Development\",\n )}\n required\n error={errors.name}\n hint={tr(\n \"devportal.organizationNameHint\",\n \"Your organization's display name\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={formData.slug}\n onChange={(e) =>\n handleChange(\"slug\", e.target.value.toLowerCase())\n }\n placeholder={tr(\"devportal.orgSlugPlaceholder\", \"acme-dev\")}\n required\n error={errors.slug}\n hint={tr(\n \"devportal.slugHint\",\n \"URL-friendly identifier (auto-generated from name)\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder={tr(\n \"devportal.orgDescriptionPlaceholder\",\n \"Tell us about your organization...\",\n )}\n rows={3}\n />\n\n <InputField\n label={tr(\"devportal.websiteUrl\", \"Website URL\")}\n type=\"url\"\n value={formData.websiteUrl}\n onChange={(e) => handleChange(\"websiteUrl\", e.target.value)}\n placeholder=\"https://example.com\"\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <InputField\n label={tr(\"devportal.contactEmail\", \"Contact Email\")}\n type=\"email\"\n value={formData.contactEmail}\n onChange={(e) => handleChange(\"contactEmail\", e.target.value)}\n placeholder=\"contact@example.com\"\n error={errors.contactEmail}\n />\n\n <InputField\n label={tr(\"devportal.supportEmail\", \"Support Email\")}\n type=\"email\"\n value={formData.supportEmail}\n onChange={(e) => handleChange(\"supportEmail\", e.target.value)}\n placeholder=\"support@example.com\"\n error={errors.supportEmail}\n />\n </div>\n </div>\n </PageSection>\n\n {/* Billing Account */}\n <PageSection\n title={tr(\"devportal.billingAccount\", \"Billing Account\")}\n description={tr(\n \"devportal.billingAccountSectionDescription\",\n \"Revenue from sales will be credited here. You can change this anytime from the Revenue page.\",\n )}\n >\n <div className=\"space-y-3\">\n <button\n type=\"button\"\n onClick={() => setBillingChoice(\"auto\")}\n disabled={isSubmitting}\n className={cn(\n \"flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n billingChoice === \"auto\"\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={16}\n className={cn(\n \"mt-0.5 flex-shrink-0\",\n billingChoice === \"auto\"\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium\">\n {tr(\n \"devportal.billingAutoCreate\",\n \"Create a new billing account automatically\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.billingAutoCreateHint\",\n \"We'll set one up using your organization name and account email. You can complete legal and tax details for payouts later in Organization Settings.\",\n )}\n </p>\n </div>\n </button>\n\n <button\n type=\"button\"\n onClick={() => setBillingChoice(\"existing\")}\n disabled={\n isSubmitting ||\n (!billingAccountsLoading &&\n existingBillingAccounts.length === 0)\n }\n className={cn(\n \"flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n billingChoice === \"existing\"\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={16}\n className={cn(\n \"mt-0.5 flex-shrink-0\",\n billingChoice === \"existing\"\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"flex items-center gap-1.5 text-sm font-medium\">\n <Link2 size={14} />\n {tr(\n \"devportal.billingLinkExisting\",\n \"Link an existing billing account\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {billingAccountsLoading\n ? tr(\n \"devportal.billingAccountsLoading\",\n \"Loading your billing accounts...\",\n )\n : existingBillingAccounts.length === 0\n ? tr(\n \"devportal.noBillingAccountsYet\",\n \"You don't have any billing accounts yet — create one from account billing settings first.\",\n )\n : tr(\n \"devportal.billingAccountsAvailable\",\n \"{{count}} billing account(s) available to link\",\n { count: existingBillingAccounts.length },\n )}\n </p>\n </div>\n </button>\n\n {billingChoice === \"existing\" &&\n existingBillingAccounts.length > 0 && (\n <div className=\"ml-8 space-y-2\">\n {existingBillingAccounts.map((account) => (\n <button\n key={account.id}\n type=\"button\"\n disabled={isSubmitting}\n onClick={() =>\n setSelectedBillingAccountId(account.id ?? null)\n }\n className={cn(\n \"flex w-full items-center gap-3 rounded-md border px-3 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n selectedBillingAccountId === account.id\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={14}\n className={cn(\n \"flex-shrink-0\",\n selectedBillingAccountId === account.id\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium\">{account.name}</p>\n {account.isDefault && (\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"devportal.revenue.defaultAccount\",\n \"Default\",\n )}\n </p>\n )}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n\n {/* Status while creating org + setting up billing */}\n {isSubmitting && (\n <div className=\"flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-sunken px-4 py-3 text-sm text-text-secondary\">\n <Loader2\n size={16}\n className=\"flex-shrink-0 animate-spin text-primary\"\n />\n {phase === \"creating-org\" &&\n tr(\n \"devportal.creatingOrgStatus\",\n \"Creating your organization...\",\n )}\n {phase === \"setting-up-billing\" &&\n tr(\n \"devportal.settingUpBillingStatus\",\n \"Setting up your billing account...\",\n )}\n {phase === \"linking-billing\" &&\n tr(\n \"devportal.linkingBillingStatus\",\n \"Linking your billing account...\",\n )}\n {phase === \"redirecting\" &&\n tr(\"devportal.redirectingStatus\", \"All set — redirecting...\")}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={16} />\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n {phase === \"setting-up-billing\" &&\n tr(\"devportal.settingUpBilling\", \"Setting up billing...\")}\n {phase === \"linking-billing\" &&\n tr(\"devportal.linkingBilling\", \"Linking billing...\")}\n {(phase === \"creating-org\" || phase === \"redirecting\") &&\n tr(\"devportal.creating\", \"Creating...\")}\n </>\n ) : (\n <>\n <Building2 size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </>\n )}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateOrganizationPage.js","names":["_Fragment","CreateOrganizationPage","useDevportal","isSeller","useIsSellerSection","tr","useTr","errors","setErrors","useState","submitError","setSubmitError","createOrganization","useCreateOrganizationMutation","createSellerOrganization","useCreateSellerOrganizationMutation","createBillingAccountMutation","useCreateBillingAccountMutation","linkBillingAccount","useLinkDevOrgBillingAccountMutation","bus","useDevportalEventBus","phase","setPhase","isSubmitting","billingChoice","setBillingChoice","selectedBillingAccountId","setSelectedBillingAccountId","billingAccountsData","billingAccountsLoading","useGetBillingAccountsByOrgQuery","tenantId","existingBillingAccounts","useMemo","a","formData","setFormData","handleNavigate","basePath","path","generateSlug","name","handleNameChange","prev","handleChange","field","value","validate","newErrors","PermissionGuard","PageLayout","orgSectionPath","input","orgId","setActivePhysicalOrgId","setActiveOrgId","billingAccountId","currentUser","error","errorMessage","PageSection","InputField","TextareaField","cn","Check","Link2","account","Loader2","X","Fragment","Building2"],"sources":["../../../src/devportal/pages/CreateOrganizationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { X, Building2, Check, Link2, Loader2 } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport {\n useIsSellerSection,\n orgSectionPath,\n} from \"../hooks/useIsSellerSection\";\nimport {\n useCreateOrganizationMutation,\n useCreateSellerOrganizationMutation,\n useGetBillingAccountsByOrgQuery,\n useLinkDevOrgBillingAccountMutation,\n} from \"../../generated/global-operations\";\nimport { useCreateBillingAccountMutation } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport { errorMessage } from \"../utils/errorMessage\";\n\ninterface CreateOrganizationFormData {\n name: string;\n slug: string;\n description: string;\n websiteUrl: string;\n contactEmail: string;\n supportEmail: string;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n contactEmail?: string;\n supportEmail?: string;\n}\n\ntype SubmitPhase =\n | \"idle\"\n | \"creating-org\"\n | \"setting-up-billing\"\n | \"linking-billing\"\n | \"redirecting\";\n\n/**\n * Create new organization page — single step.\n * The user can either have a billing account auto-created and linked, or\n * link one of their existing billing accounts up front. Legal/tax details\n * are completed later from organization settings.\n */\nexport const CreateOrganizationPage: FC = () => {\n const {\n navigate,\n basePath,\n currentUser,\n tenantId,\n setActiveOrgId,\n setActivePhysicalOrgId,\n } = useDevportal();\n const isSeller = useIsSellerSection();\n const tr = useTr();\n const [errors, setErrors] = useState<FormErrors>({});\n // A failed save stays visible instead of silently resetting the form.\n const [submitError, setSubmitError] = useState<string | null>(null);\n // Shared page for both kinds — which mutation runs is the trust boundary\n // for DeveloperOrgKind (BOFF-7285): createSellerOrganization always makes\n // a PHYSICAL org, createDeveloperOrganization always makes DIGITAL. Both\n // hooks are called unconditionally (rules of hooks); only one is invoked.\n const [createOrganization] = useCreateOrganizationMutation();\n const [createSellerOrganization] = useCreateSellerOrganizationMutation();\n const [createBillingAccountMutation] = useCreateBillingAccountMutation();\n const [linkBillingAccount] = useLinkDevOrgBillingAccountMutation();\n const bus = useDevportalEventBus();\n\n // Multi-step status so the button/banner reflect the whole create flow —\n // not just the org-creation mutation's own loading flag, which flips back\n // to false before the (awaited) billing setup/link steps finish.\n const [phase, setPhase] = useState<SubmitPhase>(\"idle\");\n const isSubmitting = phase !== \"idle\";\n\n const [billingChoice, setBillingChoice] = useState<\"auto\" | \"existing\">(\n \"auto\",\n );\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<\n string | null\n >(null);\n const { data: billingAccountsData, loading: billingAccountsLoading } =\n useGetBillingAccountsByOrgQuery({\n fetchPolicy: \"cache-and-network\",\n context: { headers: { \"x-org-id\": tenantId ?? \"\" } },\n });\n type BillingAccount = NonNullable<\n typeof billingAccountsData\n >[\"getBillingAccountsByOrg\"][number];\n const existingBillingAccounts: BillingAccount[] = useMemo(\n () => billingAccountsData?.getBillingAccountsByOrg ?? [],\n [billingAccountsData],\n );\n\n // Keep the \"existing account\" choice valid as the list loads: fall back to\n // auto-create if it turns out empty, and default-select once it arrives.\n useEffect(() => {\n if (billingChoice !== \"existing\" || billingAccountsLoading) return;\n if (existingBillingAccounts.length === 0) {\n setBillingChoice(\"auto\");\n return;\n }\n if (!selectedBillingAccountId) {\n setSelectedBillingAccountId(\n existingBillingAccounts.find((a) => a.isDefault)?.id ??\n existingBillingAccounts[0]?.id ??\n null,\n );\n }\n }, [\n billingChoice,\n billingAccountsLoading,\n existingBillingAccounts,\n selectedBillingAccountId,\n ]);\n\n const [formData, setFormData] = useState<CreateOrganizationFormData>({\n name: \"\",\n slug: \"\",\n description: \"\",\n websiteUrl: \"\",\n contactEmail: \"\",\n supportEmail: \"\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const generateSlug = (name: string): string => {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n };\n\n const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setFormData((prev) => ({\n ...prev,\n name,\n slug: prev.slug || generateSlug(name),\n }));\n if (errors.name) {\n setErrors((prev) => ({ ...prev, name: undefined }));\n }\n };\n\n const handleChange = (\n field: keyof CreateOrganizationFormData,\n value: string,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field as keyof FormErrors]) {\n setErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n };\n\n const validate = (): boolean => {\n const newErrors: FormErrors = {};\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.orgNameRequired\",\n \"Organization name is required\",\n );\n } else if (formData.name.length < 2) {\n newErrors.name = tr(\n \"devportal.orgNameTooShort\",\n \"Name must be at least 2 characters\",\n );\n } else if (formData.name.length > 100) {\n newErrors.name = tr(\n \"devportal.nameTooLong\",\n \"Name must be less than 100 characters\",\n );\n }\n\n if (!formData.slug.trim()) {\n newErrors.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug = tr(\n \"devportal.slugInvalid\",\n \"Slug can only contain lowercase letters, numbers, and hyphens\",\n );\n }\n\n if (\n formData.contactEmail &&\n !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.contactEmail)\n ) {\n newErrors.contactEmail = tr(\n \"devportal.invalidEmailAddress\",\n \"Invalid email address\",\n );\n }\n\n if (\n formData.supportEmail &&\n !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(formData.supportEmail)\n ) {\n newErrors.supportEmail = tr(\n \"devportal.invalidEmailAddress\",\n \"Invalid email address\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validate()) return;\n\n setSubmitError(null);\n setPhase(\"creating-org\");\n try {\n const input = {\n name: formData.name,\n slug: formData.slug || undefined,\n description: formData.description || undefined,\n websiteUrl: formData.websiteUrl || undefined,\n contactEmail: formData.contactEmail || undefined,\n supportEmail: formData.supportEmail || undefined,\n };\n\n const orgId = isSeller\n ? (await createSellerOrganization({ variables: { input } })).data\n ?.createSellerOrganization?.id\n : (await createOrganization({ variables: { input } })).data\n ?.createDeveloperOrganization?.id;\n\n if (!orgId) {\n setPhase(\"idle\");\n return;\n }\n\n bus.emit(\"devportal.org.created\", {\n organizationId: orgId,\n name: formData.name,\n });\n if (isSeller) {\n setActivePhysicalOrgId(orgId);\n } else {\n setActiveOrgId(orgId);\n }\n\n if (billingChoice === \"existing\" && selectedBillingAccountId) {\n setPhase(\"linking-billing\");\n try {\n await linkBillingAccount({\n variables: {\n organizationId: orgId,\n billingAccountId: selectedBillingAccountId,\n },\n });\n } catch {\n // non-blocking — can be linked later from the Revenue tab\n }\n } else {\n setPhase(\"setting-up-billing\");\n try {\n const billingResult = await createBillingAccountMutation({\n variables: {\n input: {\n name: formData.name,\n email: currentUser?.email ?? \"\",\n },\n },\n });\n const billingAccountId = billingResult.data?.createBillingAccount?.id;\n if (billingAccountId) {\n await linkBillingAccount({\n variables: { organizationId: orgId, billingAccountId },\n });\n }\n } catch {\n // non-blocking — can be linked later from the Revenue tab\n }\n }\n\n setPhase(\"redirecting\");\n handleNavigate(orgSectionPath(isSeller, orgId));\n } catch (error) {\n console.error(\"Failed to create organization:\", error);\n setSubmitError(\n errorMessage(\n error,\n tr(\n \"devportal.createOrganizationFailed\",\n \"We could not create your organization. Your details are still here, so you can try again.\",\n ),\n ),\n );\n setPhase(\"idle\");\n }\n };\n\n const handleCancel = () => {\n handleNavigate(orgSectionPath(isSeller));\n };\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.createOrganization\", \"Create Organization\")}\n description={\n isSeller\n ? tr(\n \"devportal.createSellerOrganizationDescription\",\n \"Set up a seller organization to start selling physical products\",\n )\n : tr(\n \"devportal.createOrganizationDescription\",\n \"Set up a developer organization to collaborate with your team\",\n )\n }\n purpose={\n isSeller\n ? tr(\n \"devportal.createSellerOrganizationPurpose\",\n \"A seller organization is the shared owner of your physical listings, orders, and revenue. Create one here — you can invite members and complete legal/tax details afterwards.\",\n )\n : tr(\n \"devportal.createOrganizationPurpose\",\n \"An organization is the shared owner of your apps, members, and revenue. Create one here so your team works under a single identity — pick a name and tier now; you can invite members and verify it afterwards.\",\n )\n }\n backLink={`${basePath}${orgSectionPath(isSeller)}`}\n backLabel={tr(\"devportal.backToOrganizations\", \"Back to Organizations\")}\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {submitError && (\n <div\n role=\"alert\"\n className=\"rounded-md bg-status-error-bg px-3 py-2 text-sm text-status-error-text\"\n >\n {submitError}\n </div>\n )}\n {/* Organization Details */}\n <PageSection\n title={tr(\"devportal.organizationDetails\", \"Organization Details\")}\n description={tr(\n \"devportal.organizationDetailsDescription\",\n \"Basic information about your organization\",\n )}\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.organizationName\", \"Organization Name\")}\n value={formData.name}\n onChange={handleNameChange}\n placeholder={tr(\n \"devportal.organizationNamePlaceholder\",\n \"Acme Development\",\n )}\n required\n error={errors.name}\n hint={tr(\n \"devportal.organizationNameHint\",\n \"Your organization's display name\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={formData.slug}\n onChange={(e) =>\n handleChange(\"slug\", e.target.value.toLowerCase())\n }\n placeholder={tr(\"devportal.orgSlugPlaceholder\", \"acme-dev\")}\n required\n error={errors.slug}\n hint={tr(\n \"devportal.slugHint\",\n \"URL-friendly identifier (auto-generated from name)\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder={tr(\n \"devportal.orgDescriptionPlaceholder\",\n \"Tell us about your organization...\",\n )}\n rows={3}\n />\n\n <InputField\n label={tr(\"devportal.websiteUrl\", \"Website URL\")}\n type=\"url\"\n value={formData.websiteUrl}\n onChange={(e) => handleChange(\"websiteUrl\", e.target.value)}\n placeholder=\"https://example.com\"\n />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <InputField\n label={tr(\"devportal.contactEmail\", \"Contact Email\")}\n type=\"email\"\n value={formData.contactEmail}\n onChange={(e) => handleChange(\"contactEmail\", e.target.value)}\n placeholder=\"contact@example.com\"\n error={errors.contactEmail}\n />\n\n <InputField\n label={tr(\"devportal.supportEmail\", \"Support Email\")}\n type=\"email\"\n value={formData.supportEmail}\n onChange={(e) => handleChange(\"supportEmail\", e.target.value)}\n placeholder=\"support@example.com\"\n error={errors.supportEmail}\n />\n </div>\n </div>\n </PageSection>\n\n {/* Billing Account */}\n <PageSection\n title={tr(\"devportal.billingAccount\", \"Billing Account\")}\n description={tr(\n \"devportal.billingAccountSectionDescription\",\n \"Revenue from sales will be credited here. You can change this anytime from the Revenue page.\",\n )}\n >\n <div className=\"space-y-3\">\n <button\n type=\"button\"\n onClick={() => setBillingChoice(\"auto\")}\n disabled={isSubmitting}\n className={cn(\n \"flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n billingChoice === \"auto\"\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={16}\n className={cn(\n \"mt-0.5 flex-shrink-0\",\n billingChoice === \"auto\"\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium\">\n {tr(\n \"devportal.billingAutoCreate\",\n \"Create a new billing account automatically\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(\n \"devportal.billingAutoCreateHint\",\n \"We'll set one up using your organization name and account email. You can complete legal and tax details for payouts later in Organization Settings.\",\n )}\n </p>\n </div>\n </button>\n\n <button\n type=\"button\"\n onClick={() => setBillingChoice(\"existing\")}\n disabled={\n isSubmitting ||\n (!billingAccountsLoading &&\n existingBillingAccounts.length === 0)\n }\n className={cn(\n \"flex w-full items-start gap-3 rounded-lg border px-4 py-3 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n billingChoice === \"existing\"\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={16}\n className={cn(\n \"mt-0.5 flex-shrink-0\",\n billingChoice === \"existing\"\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"flex items-center gap-1.5 text-sm font-medium\">\n <Link2 size={14} />\n {tr(\n \"devportal.billingLinkExisting\",\n \"Link an existing billing account\",\n )}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {billingAccountsLoading\n ? tr(\n \"devportal.billingAccountsLoading\",\n \"Loading your billing accounts...\",\n )\n : existingBillingAccounts.length === 0\n ? tr(\n \"devportal.noBillingAccountsYet\",\n \"You don't have any billing accounts yet — create one from account billing settings first.\",\n )\n : tr(\n \"devportal.billingAccountsAvailable\",\n \"{{count}} billing account(s) available to link\",\n { count: existingBillingAccounts.length },\n )}\n </p>\n </div>\n </button>\n\n {billingChoice === \"existing\" &&\n existingBillingAccounts.length > 0 && (\n <div className=\"ml-8 space-y-2\">\n {existingBillingAccounts.map((account) => (\n <button\n key={account.id}\n type=\"button\"\n disabled={isSubmitting}\n onClick={() =>\n setSelectedBillingAccountId(account.id ?? null)\n }\n className={cn(\n \"flex w-full items-center gap-3 rounded-md border px-3 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50\",\n selectedBillingAccountId === account.id\n ? \"border-primary bg-action-primary-bg/5\"\n : \"border-border-subtle hover:bg-bg-sunken\",\n )}\n >\n <Check\n size={14}\n className={cn(\n \"flex-shrink-0\",\n selectedBillingAccountId === account.id\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium\">{account.name}</p>\n {account.isDefault && (\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"devportal.revenue.defaultAccount\",\n \"Default\",\n )}\n </p>\n )}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n\n {/* Status while creating org + setting up billing */}\n {isSubmitting && (\n <div className=\"flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-sunken px-4 py-3 text-sm text-text-secondary\">\n <Loader2\n size={16}\n className=\"flex-shrink-0 animate-spin text-primary\"\n />\n {phase === \"creating-org\" &&\n tr(\n \"devportal.creatingOrgStatus\",\n \"Creating your organization...\",\n )}\n {phase === \"setting-up-billing\" &&\n tr(\n \"devportal.settingUpBillingStatus\",\n \"Setting up your billing account...\",\n )}\n {phase === \"linking-billing\" &&\n tr(\n \"devportal.linkingBillingStatus\",\n \"Linking your billing account...\",\n )}\n {phase === \"redirecting\" &&\n tr(\"devportal.redirectingStatus\", \"All set — redirecting...\")}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={16} />\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n {phase === \"setting-up-billing\" &&\n tr(\"devportal.settingUpBilling\", \"Setting up billing...\")}\n {phase === \"linking-billing\" &&\n tr(\"devportal.linkingBilling\", \"Linking billing...\")}\n {(phase === \"creating-org\" || phase === \"redirecting\") &&\n tr(\"devportal.creating\", \"Creating...\")}\n </>\n ) : (\n <>\n <Building2 size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </>\n )}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -3,13 +3,13 @@ import { PageLayout as t, PageSection as n } from "../components/PageLayout.js";
|
|
|
3
3
|
import { CheckboxField as r, InputField as i, SelectField as a, TagInput as o, TextareaField as ee } from "../components/FormField.js";
|
|
4
4
|
import { MarkdownEditor as te } from "../components/MarkdownEditor.js";
|
|
5
5
|
import { MultiSelectCombobox as ne } from "../components/MultiSelectCombobox.js";
|
|
6
|
-
import { StatusBadge as
|
|
7
|
-
import { ConfirmDialog as
|
|
8
|
-
import { LoadingState as
|
|
9
|
-
import { EmptyState as
|
|
10
|
-
import { useDevportal as
|
|
11
|
-
import { PermissionGuard as
|
|
12
|
-
import { nativeImpact as
|
|
6
|
+
import { StatusBadge as re } from "../components/StatusBadge.js";
|
|
7
|
+
import { ConfirmDialog as s } from "../components/ConfirmDialog.js";
|
|
8
|
+
import { LoadingState as ie } from "../components/LoadingState.js";
|
|
9
|
+
import { EmptyState as ae } from "../components/EmptyState.js";
|
|
10
|
+
import { useDevportal as oe } from "../providers/DevportalProvider.js";
|
|
11
|
+
import { PermissionGuard as se } from "../components/PermissionGuard.js";
|
|
12
|
+
import { nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
|
|
13
13
|
import "../components/index.js";
|
|
14
14
|
import { DEVPORTAL_PERMISSIONS as u } from "../constants/permissions.js";
|
|
15
15
|
import { useProducts as ce } from "../hooks/useProducts.js";
|
|
@@ -87,13 +87,13 @@ function Ae(e) {
|
|
|
87
87
|
} : null;
|
|
88
88
|
}
|
|
89
89
|
var b = () => {
|
|
90
|
-
let { navigate: f, basePath: b } =
|
|
90
|
+
let { navigate: f, basePath: b } = oe(), { appId: x } = Ee(), { t: S } = De(), { products: C } = ce(), { allowed: w } = d(u.APP_UPDATE), { allowed: T } = d(u.APP_DELETE), E = (e, t) => {
|
|
91
91
|
let n = S(e);
|
|
92
92
|
return n === e ? t : n;
|
|
93
|
-
}, [D, O] = m(!1), [k, A] = m({}), [je, j] = m(!1), [
|
|
93
|
+
}, [D, O] = m(!1), [k, A] = m({}), [je, j] = m(!1), [Me, M] = m(!1), [Ne, N] = m(!1), [P, F] = m(!1), [I, L] = m(!1), [R, z] = m(!1), [B, Pe] = m(null), [Fe, V] = m(!1), { data: H, loading: Ie, error: U, refetch: Le } = de({
|
|
94
94
|
variables: { id: x },
|
|
95
95
|
skip: !x
|
|
96
|
-
}), [
|
|
96
|
+
}), [W] = pe(), [Re] = ue(), [ze] = le(), [Be] = fe(), [Ve, G] = m(!1), K = Ae(H?.application);
|
|
97
97
|
p(() => {
|
|
98
98
|
let e = K?.name;
|
|
99
99
|
if (!x || !e) return;
|
|
@@ -142,42 +142,42 @@ var b = () => {
|
|
|
142
142
|
oauthRevokeUrl: K.oauthRevokeUrl || "",
|
|
143
143
|
requiresApproval: K.requiresApproval
|
|
144
144
|
});
|
|
145
|
-
let e =
|
|
146
|
-
X(t),
|
|
145
|
+
let e = H?.application?.manifest?.compatibility, t = Array.isArray(e?.products) ? e.products : [];
|
|
146
|
+
X(t), F(!1);
|
|
147
147
|
}
|
|
148
|
-
}, [
|
|
148
|
+
}, [H?.application]);
|
|
149
149
|
let Z = (e) => {
|
|
150
150
|
f?.(`${b}${e}`);
|
|
151
151
|
}, Q = (e, t) => {
|
|
152
152
|
J((n) => ({
|
|
153
153
|
...n,
|
|
154
154
|
[e]: t
|
|
155
|
-
})),
|
|
155
|
+
})), F(!0), k[e] && A((t) => ({
|
|
156
156
|
...t,
|
|
157
157
|
[e]: void 0
|
|
158
158
|
}));
|
|
159
159
|
}, $ = async () => {
|
|
160
160
|
if (x) {
|
|
161
|
-
|
|
161
|
+
z(!0);
|
|
162
162
|
try {
|
|
163
|
-
let e = (await
|
|
164
|
-
e && (
|
|
163
|
+
let e = (await Be({ variables: { id: x } })).data?.rotateApplicationSigningSecret?.signingSecret;
|
|
164
|
+
e && (Pe(e), L(!0));
|
|
165
165
|
} catch {
|
|
166
166
|
l("error");
|
|
167
167
|
} finally {
|
|
168
|
-
|
|
168
|
+
z(!1);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
},
|
|
172
|
-
await navigator.clipboard.writeText(e),
|
|
173
|
-
},
|
|
171
|
+
}, He = async (e) => {
|
|
172
|
+
await navigator.clipboard.writeText(e), V(!0), setTimeout(() => V(!1), 2e3);
|
|
173
|
+
}, Ue = () => {
|
|
174
174
|
let e = {};
|
|
175
175
|
return q.name.trim() ? q.name.length < 3 ? e.name = E("devportal.appNameMinLength", "Name must be at least 3 characters") : q.name.length > 100 && (e.name = E("devportal.appNameMaxLength", "Name must be less than 100 characters")) : e.name = E("devportal.appNameRequired", "Application name is required"), q.slug.trim() ? /^[a-z0-9-]+$/.test(q.slug) || (e.slug = E("devportal.slugInvalidChars", "Slug can only contain lowercase letters, numbers, and hyphens")) : e.slug = E("devportal.slugRequired", "Slug is required"), q.shortDescription.trim() ? q.shortDescription.length > 200 && (e.shortDescription = E("devportal.shortDescriptionMaxLength", "Short description must be less than 200 characters")) : e.shortDescription = E("devportal.shortDescriptionRequired", "Short description is required"), q.appType || (e.appType = E("devportal.appTypeRequired", "Application type is required")), q.category || (e.category = E("devportal.categoryRequired", "Category is required")), A(e), Object.keys(e).length === 0;
|
|
176
|
-
},
|
|
177
|
-
if (e.preventDefault(),
|
|
176
|
+
}, We = async (e) => {
|
|
177
|
+
if (e.preventDefault(), Ue() && K) {
|
|
178
178
|
O(!0);
|
|
179
179
|
try {
|
|
180
|
-
await
|
|
180
|
+
await W({ variables: {
|
|
181
181
|
id: K.id,
|
|
182
182
|
input: {
|
|
183
183
|
name: q.name,
|
|
@@ -198,40 +198,40 @@ var b = () => {
|
|
|
198
198
|
oauthRevokeUrl: q.oauthRevokeUrl || null,
|
|
199
199
|
compatibleProducts: Y
|
|
200
200
|
}
|
|
201
|
-
} }),
|
|
201
|
+
} }), F(!1);
|
|
202
202
|
} catch (e) {
|
|
203
203
|
console.error("Failed to update application:", e);
|
|
204
204
|
} finally {
|
|
205
205
|
O(!1);
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
},
|
|
208
|
+
}, Ge = async () => {
|
|
209
209
|
if (K) {
|
|
210
|
-
|
|
210
|
+
c("medium");
|
|
211
211
|
try {
|
|
212
|
-
await
|
|
212
|
+
await Re({ variables: { id: K.id } }), l("success"), Z("/apps");
|
|
213
213
|
} catch (e) {
|
|
214
214
|
l("error"), console.error("Failed to delete application:", e);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
},
|
|
217
|
+
}, Ke = async () => {
|
|
218
218
|
if (K) try {
|
|
219
|
-
await
|
|
219
|
+
await W({ variables: {
|
|
220
220
|
id: K.id,
|
|
221
221
|
input: { visibility: "PRIVATE" }
|
|
222
|
-
} }),
|
|
222
|
+
} }), M(!1);
|
|
223
223
|
} catch (e) {
|
|
224
224
|
console.error("Failed to deprecate application:", e);
|
|
225
225
|
}
|
|
226
|
-
},
|
|
226
|
+
}, qe = async () => {
|
|
227
227
|
if (K) try {
|
|
228
|
-
await
|
|
228
|
+
await ze({ variables: { id: K.id } }), N(!1), Z("/apps");
|
|
229
229
|
} catch (e) {
|
|
230
230
|
console.error("Failed to deactivate application:", e);
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
|
-
if (
|
|
234
|
-
if (
|
|
233
|
+
if (Ie) return /* @__PURE__ */ _(ie, { message: E("devportal.loadingApplication", "Loading application...") });
|
|
234
|
+
if (U) return /* @__PURE__ */ _(t, {
|
|
235
235
|
title: E("devportal.error", "Error"),
|
|
236
236
|
backLink: `${b}/apps`,
|
|
237
237
|
backLabel: E("devportal.backToApplications", "Back to Applications"),
|
|
@@ -248,7 +248,7 @@ var b = () => {
|
|
|
248
248
|
}),
|
|
249
249
|
/* @__PURE__ */ _("p", {
|
|
250
250
|
className: "text-sm text-text-secondary",
|
|
251
|
-
children:
|
|
251
|
+
children: U.message
|
|
252
252
|
})
|
|
253
253
|
]
|
|
254
254
|
})
|
|
@@ -257,15 +257,15 @@ var b = () => {
|
|
|
257
257
|
title: E("devportal.notFound", "Not Found"),
|
|
258
258
|
backLink: `${b}/apps`,
|
|
259
259
|
backLabel: E("devportal.backToApplications", "Back to Applications"),
|
|
260
|
-
children: /* @__PURE__ */ _(
|
|
260
|
+
children: /* @__PURE__ */ _(ae, {
|
|
261
261
|
illustration: "empty-generic",
|
|
262
262
|
icon: /* @__PURE__ */ _(ge, { size: 48 }),
|
|
263
263
|
title: E("devportal.applicationNotFound", "Application not found"),
|
|
264
264
|
description: E("devportal.applicationNotFoundDescription", "The application you're looking for doesn't exist or you don't have access to it.")
|
|
265
265
|
})
|
|
266
266
|
});
|
|
267
|
-
let
|
|
268
|
-
return /* @__PURE__ */ _(
|
|
267
|
+
let Je = K.status === "DRAFT" || K.status === "REJECTED", Ye = K.status === "PUBLISHED", Xe = K.status === "ACTIVE" || K.status === "PUBLISHED";
|
|
268
|
+
return /* @__PURE__ */ _(se, {
|
|
269
269
|
authOnly: !0,
|
|
270
270
|
children: /* @__PURE__ */ v(t, {
|
|
271
271
|
title: `${E("devportal.editAppTitlePrefix", "Edit:")} ${K.name}`,
|
|
@@ -275,14 +275,14 @@ var b = () => {
|
|
|
275
275
|
backLabel: E("devportal.backToApplication", "Back to Application"),
|
|
276
276
|
actions: /* @__PURE__ */ _("div", {
|
|
277
277
|
className: "flex items-center gap-2",
|
|
278
|
-
children: /* @__PURE__ */ _(
|
|
278
|
+
children: /* @__PURE__ */ _(re, {
|
|
279
279
|
status: K.status,
|
|
280
280
|
type: "app"
|
|
281
281
|
})
|
|
282
282
|
}),
|
|
283
283
|
children: [
|
|
284
284
|
/* @__PURE__ */ v("form", {
|
|
285
|
-
onSubmit:
|
|
285
|
+
onSubmit: We,
|
|
286
286
|
className: "space-y-8 max-w-3xl",
|
|
287
287
|
children: [
|
|
288
288
|
K.status === "PUBLISHED" && /* @__PURE__ */ v("div", {
|
|
@@ -412,7 +412,7 @@ var b = () => {
|
|
|
412
412
|
})),
|
|
413
413
|
selected: Y,
|
|
414
414
|
onChange: (e) => {
|
|
415
|
-
X(e),
|
|
415
|
+
X(e), F(!0);
|
|
416
416
|
},
|
|
417
417
|
placeholder: "Select products…",
|
|
418
418
|
searchPlaceholder: "Search products…"
|
|
@@ -495,7 +495,7 @@ var b = () => {
|
|
|
495
495
|
hint: "We POST to this URL when a user uninstalls your integration"
|
|
496
496
|
}),
|
|
497
497
|
(() => {
|
|
498
|
-
let e =
|
|
498
|
+
let e = B ?? H?.application?.signingSecret, t = "boff_sk_" + "•".repeat(32);
|
|
499
499
|
return /* @__PURE__ */ v("div", {
|
|
500
500
|
className: "space-y-1.5",
|
|
501
501
|
children: [
|
|
@@ -523,21 +523,21 @@ var b = () => {
|
|
|
523
523
|
children: [
|
|
524
524
|
/* @__PURE__ */ _("code", {
|
|
525
525
|
className: "flex-1 px-3 py-2 text-xs font-mono text-text-primary truncate",
|
|
526
|
-
children:
|
|
526
|
+
children: I ? e : t
|
|
527
527
|
}),
|
|
528
528
|
/* @__PURE__ */ _("button", {
|
|
529
529
|
type: "button",
|
|
530
|
-
onClick: () =>
|
|
530
|
+
onClick: () => L((e) => !e),
|
|
531
531
|
className: "px-2 py-2 text-text-secondary hover:text-text-primary border-l border-border-default",
|
|
532
|
-
title:
|
|
533
|
-
children: _(
|
|
532
|
+
title: I ? "Hide" : "Reveal",
|
|
533
|
+
children: _(I ? xe : be, { size: 14 })
|
|
534
534
|
}),
|
|
535
535
|
/* @__PURE__ */ _("button", {
|
|
536
536
|
type: "button",
|
|
537
|
-
onClick: () =>
|
|
537
|
+
onClick: () => He(e),
|
|
538
538
|
className: "px-2 py-2 text-text-secondary hover:text-text-primary border-l border-border-default",
|
|
539
539
|
title: "Copy to clipboard",
|
|
540
|
-
children:
|
|
540
|
+
children: Fe ? /* @__PURE__ */ _(ve, {
|
|
541
541
|
size: 14,
|
|
542
542
|
className: "text-green-500"
|
|
543
543
|
}) : /* @__PURE__ */ _(ye, { size: 14 })
|
|
@@ -545,26 +545,26 @@ var b = () => {
|
|
|
545
545
|
]
|
|
546
546
|
}), /* @__PURE__ */ v("button", {
|
|
547
547
|
type: "button",
|
|
548
|
-
onClick:
|
|
549
|
-
disabled:
|
|
548
|
+
onClick: () => G(!0),
|
|
549
|
+
disabled: R,
|
|
550
550
|
className: "flex items-center gap-1.5 px-3 py-2 text-xs rounded-md border border-border-default bg-bg-default text-text-primary hover:bg-bg-subtle disabled:opacity-50",
|
|
551
551
|
title: "Rotate signing secret",
|
|
552
552
|
children: [/* @__PURE__ */ _(h, {
|
|
553
553
|
size: 12,
|
|
554
|
-
className:
|
|
554
|
+
className: R ? "animate-spin" : ""
|
|
555
555
|
}), "Rotate"]
|
|
556
556
|
})]
|
|
557
557
|
}) : /* @__PURE__ */ v("button", {
|
|
558
558
|
type: "button",
|
|
559
559
|
onClick: $,
|
|
560
|
-
disabled:
|
|
560
|
+
disabled: R,
|
|
561
561
|
className: "flex items-center gap-1.5 px-3 py-2 text-xs rounded-md border border-border-default bg-bg-default text-text-primary hover:bg-bg-subtle disabled:opacity-50",
|
|
562
562
|
children: [/* @__PURE__ */ _(h, {
|
|
563
563
|
size: 12,
|
|
564
|
-
className:
|
|
564
|
+
className: R ? "animate-spin" : ""
|
|
565
565
|
}), "Generate Signing Secret"]
|
|
566
566
|
}),
|
|
567
|
-
|
|
567
|
+
B && /* @__PURE__ */ _("p", {
|
|
568
568
|
className: "text-xs text-amber-600 dark:text-amber-400",
|
|
569
569
|
children: "New secret generated. Copy it now — the old secret is already invalidated."
|
|
570
570
|
})
|
|
@@ -587,7 +587,7 @@ var b = () => {
|
|
|
587
587
|
})
|
|
588
588
|
})
|
|
589
589
|
}),
|
|
590
|
-
|
|
590
|
+
Je && /* @__PURE__ */ v("div", {
|
|
591
591
|
className: "rounded-lg border border-border-subtle bg-bg-sunken p-4 space-y-2",
|
|
592
592
|
children: [/* @__PURE__ */ v("p", {
|
|
593
593
|
className: "text-sm font-medium text-text-primary flex items-center gap-2",
|
|
@@ -615,11 +615,11 @@ var b = () => {
|
|
|
615
615
|
},
|
|
616
616
|
{
|
|
617
617
|
label: E("devportal.checkSaved", "All changes saved"),
|
|
618
|
-
ok: !
|
|
618
|
+
ok: !P
|
|
619
619
|
},
|
|
620
620
|
...K?.appType === "NODE" || K?.appType === "WORKFLOW" ? [{
|
|
621
621
|
label: E("devportal.checkVersion", "At least one version uploaded"),
|
|
622
|
-
ok: (
|
|
622
|
+
ok: (H?.application?.versions?.totalCount ?? 0) > 0
|
|
623
623
|
}] : []
|
|
624
624
|
].map((e) => /* @__PURE__ */ v("div", {
|
|
625
625
|
className: "flex items-center gap-2 text-sm",
|
|
@@ -641,7 +641,7 @@ var b = () => {
|
|
|
641
641
|
className: "flex items-center justify-end",
|
|
642
642
|
children: /* @__PURE__ */ _("button", {
|
|
643
643
|
type: "submit",
|
|
644
|
-
disabled: D || !
|
|
644
|
+
disabled: D || !P || !w,
|
|
645
645
|
"aria-disabled": !w,
|
|
646
646
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50",
|
|
647
647
|
children: D ? /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }), E("devportal.saving", "Saving...")] }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(Ce, { size: 16 }), E("devportal.saveChanges", "Save Changes")] })
|
|
@@ -660,15 +660,15 @@ var b = () => {
|
|
|
660
660
|
/* @__PURE__ */ v("div", {
|
|
661
661
|
className: "mt-4 flex gap-3",
|
|
662
662
|
children: [
|
|
663
|
-
|
|
663
|
+
Ye && /* @__PURE__ */ v("button", {
|
|
664
664
|
type: "button",
|
|
665
|
-
onClick: () =>
|
|
665
|
+
onClick: () => M(!0),
|
|
666
666
|
className: "inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors",
|
|
667
667
|
children: [/* @__PURE__ */ _(_e, { size: 14 }), E("devportal.deprecateApp", "Deprecate App")]
|
|
668
668
|
}),
|
|
669
|
-
|
|
669
|
+
Xe && /* @__PURE__ */ v("button", {
|
|
670
670
|
type: "button",
|
|
671
|
-
onClick: () =>
|
|
671
|
+
onClick: () => N(!0),
|
|
672
672
|
className: "inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors",
|
|
673
673
|
children: [/* @__PURE__ */ _(Se, { size: 14 }), E("devportal.deactivateApp", "Deactivate App")]
|
|
674
674
|
}),
|
|
@@ -687,32 +687,43 @@ var b = () => {
|
|
|
687
687
|
})
|
|
688
688
|
]
|
|
689
689
|
}),
|
|
690
|
-
/* @__PURE__ */ _(
|
|
690
|
+
/* @__PURE__ */ _(s, {
|
|
691
|
+
open: Ve,
|
|
692
|
+
onOpenChange: G,
|
|
693
|
+
title: E("devportal.rotateSigningSecret", "Rotate signing secret"),
|
|
694
|
+
description: E("devportal.rotateSigningSecretConfirm", "A new signing secret is created and the current one stops working straight away. Update every integration that verifies this app's signatures first, or they will start failing."),
|
|
695
|
+
confirmLabel: E("devportal.rotateSecret", "Rotate secret"),
|
|
696
|
+
variant: "warning",
|
|
697
|
+
onConfirm: async () => {
|
|
698
|
+
await $(), G(!1);
|
|
699
|
+
}
|
|
700
|
+
}),
|
|
701
|
+
/* @__PURE__ */ _(s, {
|
|
691
702
|
open: je,
|
|
692
703
|
onOpenChange: j,
|
|
693
704
|
title: E("devportal.deleteApplication", "Delete Application"),
|
|
694
705
|
description: E("devportal.deleteApplicationConfirm", `Are you sure you want to delete "${K.name}"? This action cannot be undone. All OAuth clients, API keys, webhooks, and analytics data will be permanently deleted.`),
|
|
695
706
|
confirmLabel: E("devportal.deleteApplication", "Delete Application"),
|
|
696
707
|
variant: "destructive",
|
|
697
|
-
onConfirm:
|
|
708
|
+
onConfirm: Ge
|
|
698
709
|
}),
|
|
699
|
-
/* @__PURE__ */ _(
|
|
700
|
-
open:
|
|
701
|
-
onOpenChange:
|
|
710
|
+
/* @__PURE__ */ _(s, {
|
|
711
|
+
open: Me,
|
|
712
|
+
onOpenChange: M,
|
|
702
713
|
title: E("devportal.deprecateApplication", "Deprecate Application"),
|
|
703
714
|
description: E("devportal.deprecateApplicationConfirm", `Are you sure you want to deprecate "${K.name}"? Existing users will still be able to use the app, but it will no longer be visible in the marketplace and new installations will be disabled.`),
|
|
704
715
|
confirmLabel: E("devportal.deprecateApplication", "Deprecate Application"),
|
|
705
716
|
variant: "warning",
|
|
706
|
-
onConfirm:
|
|
717
|
+
onConfirm: Ke
|
|
707
718
|
}),
|
|
708
|
-
/* @__PURE__ */ _(
|
|
709
|
-
open:
|
|
710
|
-
onOpenChange:
|
|
719
|
+
/* @__PURE__ */ _(s, {
|
|
720
|
+
open: Ne,
|
|
721
|
+
onOpenChange: N,
|
|
711
722
|
title: E("devportal.deactivateApplication", "Deactivate Application"),
|
|
712
723
|
description: E("devportal.deactivateApplicationConfirm", `Are you sure you want to deactivate "${K.name}"? This will revoke all active tokens and disable the app from the auth-svc layer. The app will no longer be able to authenticate or act on behalf of users.`),
|
|
713
724
|
confirmLabel: E("devportal.deactivateApplication", "Deactivate Application"),
|
|
714
725
|
variant: "destructive",
|
|
715
|
-
onConfirm:
|
|
726
|
+
onConfirm: qe
|
|
716
727
|
})
|
|
717
728
|
]
|
|
718
729
|
})
|