Skip to content

Commit 8246ebe

Browse files
committed
onboarding: Create org directly on slug
And then PATCH it with business details
1 parent 87352ab commit 8246ebe

2 files changed

Lines changed: 76 additions & 37 deletions

File tree

clients/apps/web/src/components/Onboarding/v2/BusinessDetailsStep.tsx

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client'
22

3+
import { useAuth } from '@/hooks'
4+
import { useCreateOrganization } from '@/hooks/queries'
35
import { useOnboardingV2Tracking } from '@/hooks/onboardingV2'
46
import { enums, schemas } from '@polar-sh/client'
57
import { Box } from '@polar-sh/orbit/Box'
@@ -221,9 +223,11 @@ function SubmitButton({ loading }: { loading: boolean }) {
221223

222224
export function BusinessDetailsStep() {
223225
const router = useRouter()
226+
const { setUserOrganizations } = useAuth()
224227
const { trackStepViewed, trackStepCompleted } = useOnboardingV2Tracking()
225228
const { data, updateData, setApiLoading, showApiResponse } =
226229
useOnboardingData()
230+
const createOrganization = useCreateOrganization()
227231
const [submitting, setSubmitting] = useState(false)
228232

229233
trackStepViewed('business')
@@ -247,7 +251,7 @@ export function BusinessDetailsStep() {
247251
},
248252
})
249253

250-
const { handleSubmit, setValue } = form
254+
const { handleSubmit, setError, setValue } = form
251255

252256
const onSubmit = async (formData: FormSchema) => {
253257
setSubmitting(true)
@@ -262,8 +266,46 @@ export function BusinessDetailsStep() {
262266
registeredBusinessName: formData.registeredBusinessName,
263267
})
264268

265-
trackStepCompleted('business')
266-
await showApiResponse(200, 'OK')
269+
const { data: organization, error } = await createOrganization.mutateAsync({
270+
name: formData.orgName,
271+
slug: formData.orgSlug,
272+
default_presentment_currency:
273+
formData.defaultCurrency as schemas['PresentmentCurrency'],
274+
country: (formData.businessCountry || undefined) as
275+
| schemas['OrganizationCreate']['country']
276+
| undefined,
277+
default_tax_behavior: 'location',
278+
legal_entity:
279+
formData.organizationType === 'company'
280+
? {
281+
type: 'company' as const,
282+
registered_name: formData.registeredBusinessName,
283+
}
284+
: { type: 'individual' as const },
285+
})
286+
287+
if (error) {
288+
setSubmitting(false)
289+
setError('root', {
290+
message:
291+
typeof error.detail === 'string'
292+
? error.detail
293+
: Array.isArray(error.detail)
294+
? (error.detail[0]?.msg ?? 'Failed to create organization')
295+
: 'Failed to create organization',
296+
})
297+
await showApiResponse(400, 'Failed to create organization')
298+
return
299+
}
300+
301+
setUserOrganizations((previous) => [...previous, organization])
302+
updateData({
303+
organizationId: organization.id,
304+
orgSlug: organization.slug,
305+
})
306+
307+
trackStepCompleted('business', { organization_id: organization.id })
308+
await showApiResponse(201, 'Created')
267309
router.push('/onboarding/product')
268310
}
269311

@@ -372,6 +414,12 @@ export function BusinessDetailsStep() {
372414

373415
<CurrencyAndCountryFields />
374416

417+
{form.formState.errors.root && (
418+
<p className="text-sm text-red-500 dark:text-red-500">
419+
{form.formState.errors.root.message}
420+
</p>
421+
)}
422+
375423
<SubmitButton loading={submitting} />
376424
</form>
377425
</Form>

clients/apps/web/src/components/Onboarding/v2/ProductDetailsStep.tsx

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client'
22

3-
import { useAuth } from '@/hooks'
43
import * as Sentry from '@sentry/nextjs'
5-
import { useCreateOrganization } from '@/hooks/queries'
4+
import { useUpdateOrganization } from '@/hooks/queries'
65
import { schemas } from '@polar-sh/client'
76
import { Box } from '@polar-sh/orbit/Box'
87
import Button from '@polar-sh/ui/components/atoms/Button'
@@ -66,11 +65,10 @@ interface FormSchema {
6665

6766
export function ProductDetailsStep() {
6867
const router = useRouter()
69-
const { setUserOrganizations } = useAuth()
7068
const { data, updateData, setApiLoading, showApiResponse } =
7169
useOnboardingData()
7270
const { trackStepViewed, trackStepCompleted } = useOnboardingV2Tracking()
73-
const createOrganization = useCreateOrganization()
71+
const updateOrganization = useUpdateOrganization()
7472
const [loading, setLoading] = useState<
7573
'validating' | 'submitting' | 'submitting-anyway' | null
7674
>(null)
@@ -132,36 +130,32 @@ export function ProductDetailsStep() {
132130
const submitOrg = async (formData: FormSchema) => {
133131
setApiLoading(true)
134132

133+
if (!data.organizationId) {
134+
form.setError('root', {
135+
message: 'Organization setup is incomplete. Please start again.',
136+
})
137+
await showApiResponse(400, 'Failed to update organization')
138+
return false
139+
}
140+
135141
const switching = formData.currentlySellingOn.length > 0
136142
const switchingFrom = (
137143
switching ? formData.currentlySellingOn[0] : null
138144
) as schemas['OrganizationDetails']['switching_from']
139145

140-
const { data: org, error } = await createOrganization.mutateAsync({
141-
name: data.orgName!,
142-
slug: data.orgSlug!,
143-
default_presentment_currency:
144-
(data.defaultCurrency as schemas['PresentmentCurrency']) || 'usd',
145-
country: (data.businessCountry || data.country || undefined) as
146-
| schemas['OrganizationCreate']['country']
147-
| undefined,
148-
default_tax_behavior: 'location',
149-
legal_entity:
150-
data.organizationType === 'company'
151-
? {
152-
type: 'company' as const,
153-
registered_name: data.registeredBusinessName!,
154-
}
155-
: { type: 'individual' as const },
156-
...(formData.supportEmail && { email: formData.supportEmail }),
157-
...(formData.productUrl && { website: formData.productUrl }),
158-
details: {
159-
product_description: formData.productDescription,
160-
selling_categories: formData.sellingCategories,
161-
pricing_models: formData.pricingModel,
162-
switching,
163-
switching_from: switchingFrom,
164-
} satisfies schemas['OrganizationDetails'],
146+
const { error } = await updateOrganization.mutateAsync({
147+
id: data.organizationId,
148+
body: {
149+
...(formData.supportEmail && { email: formData.supportEmail }),
150+
...(formData.productUrl && { website: formData.productUrl }),
151+
details: {
152+
product_description: formData.productDescription,
153+
selling_categories: formData.sellingCategories,
154+
pricing_models: formData.pricingModel,
155+
switching,
156+
switching_from: switchingFrom,
157+
} satisfies schemas['OrganizationDetails'],
158+
},
165159
})
166160

167161
if (error) {
@@ -173,14 +167,11 @@ export function ProductDetailsStep() {
173167
? (error.detail[0]?.msg ?? 'Validation failed')
174168
: 'Something went wrong, please try again.',
175169
})
176-
showApiResponse(400, 'Failed to create organization')
170+
await showApiResponse(400, 'Failed to update organization')
177171
return false
178172
}
179173

180-
setUserOrganizations((prev) => [...prev, org])
181-
updateData({ organizationId: org.id, orgSlug: org.slug })
182-
183-
trackStepCompleted('product', { organization_id: org.id })
174+
trackStepCompleted('product', { organization_id: data.organizationId })
184175
await showApiResponse(200, 'OK')
185176
router.push('/onboarding/complete')
186177
return true

0 commit comments

Comments
 (0)