Adding api automation workflow
This commit is contained in:
+94
-2
@@ -176,6 +176,13 @@ const billingIntervalSchema = z.enum(['monthly', 'yearly']);
|
|||||||
const integrationTokenScopeSchema = z.enum(['read_only', 'read_write']);
|
const integrationTokenScopeSchema = z.enum(['read_only', 'read_write']);
|
||||||
const birdGenderSchema = z.enum(['unknown', 'male', 'female']);
|
const birdGenderSchema = z.enum(['unknown', 'male', 'female']);
|
||||||
const rescueVerificationStatusSchema = z.enum(['pending', 'approved', 'rejected']);
|
const rescueVerificationStatusSchema = z.enum(['pending', 'approved', 'rejected']);
|
||||||
|
const rescueOnboardingSchema = z.object({
|
||||||
|
name: z.string().trim().max(160).optional().or(z.literal('')),
|
||||||
|
city: z.string().trim().max(120).optional().or(z.literal('')),
|
||||||
|
state: z.string().trim().max(80).optional().or(z.literal('')),
|
||||||
|
ein: z.string().trim().max(32).optional().or(z.literal('')),
|
||||||
|
website: z.string().trim().url().max(2000).optional().or(z.literal('')),
|
||||||
|
});
|
||||||
|
|
||||||
const workspaceSchema = z.object({
|
const workspaceSchema = z.object({
|
||||||
name: z.string().trim().min(1).max(160),
|
name: z.string().trim().min(1).max(160),
|
||||||
@@ -183,6 +190,7 @@ const workspaceSchema = z.object({
|
|||||||
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
||||||
billingPlan: billingPlanSchema.optional(),
|
billingPlan: billingPlanSchema.optional(),
|
||||||
billingInterval: billingIntervalSchema.optional(),
|
billingInterval: billingIntervalSchema.optional(),
|
||||||
|
rescueOnboarding: rescueOnboardingSchema.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createWorkspaceSchema = z.object({
|
const createWorkspaceSchema = z.object({
|
||||||
@@ -191,6 +199,7 @@ const createWorkspaceSchema = z.object({
|
|||||||
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
billingEmail: z.string().trim().email().max(255).optional().or(z.literal('')),
|
||||||
billingPlan: billingPlanSchema.optional(),
|
billingPlan: billingPlanSchema.optional(),
|
||||||
billingInterval: billingIntervalSchema.optional(),
|
billingInterval: billingIntervalSchema.optional(),
|
||||||
|
rescueOnboarding: rescueOnboardingSchema.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const workspaceMemberSchema = z.object({
|
const workspaceMemberSchema = z.object({
|
||||||
@@ -328,6 +337,7 @@ const smtpPass = process.env.SMTP_PASS?.trim() ?? '';
|
|||||||
const smtpFromEmail = process.env.SMTP_FROM_EMAIL?.trim() ?? '';
|
const smtpFromEmail = process.env.SMTP_FROM_EMAIL?.trim() ?? '';
|
||||||
const smtpFromName = process.env.SMTP_FROM_NAME?.trim() || 'FlockPal';
|
const smtpFromName = process.env.SMTP_FROM_NAME?.trim() || 'FlockPal';
|
||||||
const rescueStatusNotificationEmail = process.env.RESCUE_STATUS_NOTIFICATION_EMAIL?.trim() || 'appadmin@flockpal.app';
|
const rescueStatusNotificationEmail = process.env.RESCUE_STATUS_NOTIFICATION_EMAIL?.trim() || 'appadmin@flockpal.app';
|
||||||
|
const rescueOnboardingWebhookUrl = 'https://n8n.blaishome.online/webhook/395cd538-5e0d-4e89-8070-9e66f571b7ee';
|
||||||
const stripeSecretKey = process.env.STRIPE_SECRET_KEY?.trim() ?? '';
|
const stripeSecretKey = process.env.STRIPE_SECRET_KEY?.trim() ?? '';
|
||||||
const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? '';
|
const stripeWebhookSecret = process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? '';
|
||||||
const withBillingRedirectState = (url: string, billingState: 'success' | 'cancelled' | 'portal') => {
|
const withBillingRedirectState = (url: string, billingState: 'success' | 'cancelled' | 'portal') => {
|
||||||
@@ -1044,6 +1054,54 @@ const sendRescueStatusNotification = async ({
|
|||||||
return { delivered: true };
|
return { delivered: true };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type RescueOnboardingPayload = z.infer<typeof rescueOnboardingSchema>;
|
||||||
|
|
||||||
|
const sendRescueOnboardingWebhook = async ({
|
||||||
|
action,
|
||||||
|
workspaceId,
|
||||||
|
flockName,
|
||||||
|
ownerEmail,
|
||||||
|
requestedByUserId,
|
||||||
|
rescueOnboarding,
|
||||||
|
}: {
|
||||||
|
action: 'created' | 'converted';
|
||||||
|
workspaceId: number;
|
||||||
|
flockName: string;
|
||||||
|
ownerEmail: string;
|
||||||
|
requestedByUserId: string;
|
||||||
|
rescueOnboarding: RescueOnboardingPayload;
|
||||||
|
}) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 10_000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(rescueOnboardingWebhookUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
Name: rescueOnboarding.name,
|
||||||
|
City: rescueOnboarding.city,
|
||||||
|
State: rescueOnboarding.state,
|
||||||
|
EIN: rescueOnboarding.ein,
|
||||||
|
Website: rescueOnboarding.website,
|
||||||
|
action,
|
||||||
|
workspaceId,
|
||||||
|
flockName,
|
||||||
|
ownerEmail,
|
||||||
|
requestedByUserId,
|
||||||
|
submittedAt: new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Rescue onboarding webhook returned ${response.status}`);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const issueMagicLinkInvite = async ({
|
const issueMagicLinkInvite = async ({
|
||||||
email,
|
email,
|
||||||
name,
|
name,
|
||||||
@@ -1937,7 +1995,7 @@ app.get('/api/admin/rescue-workspaces', requireAuth, requireSessionAuth, require
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.patch('/api/admin/rescue-workspaces/:workspaceId', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
|
app.patch('/api/admin/rescue-workspaces/:workspaceId', requireAuth, requireAdmin, requireWriteAccess, async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const parsed = z.object({ rescueVerificationStatus: rescueVerificationStatusSchema }).safeParse(req.body);
|
const parsed = z.object({ rescueVerificationStatus: rescueVerificationStatusSchema }).safeParse(req.body);
|
||||||
|
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
@@ -2169,6 +2227,23 @@ app.post('/api/workspaces', requireAuth, requireSessionAuth, async (req: Request
|
|||||||
try {
|
try {
|
||||||
const workspaceId = await getNextWorkspaceId();
|
const workspaceId = await getNextWorkspaceId();
|
||||||
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan);
|
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan);
|
||||||
|
|
||||||
|
if (parsed.data.workspaceType === 'rescue') {
|
||||||
|
if (!parsed.data.rescueOnboarding) {
|
||||||
|
res.status(400).json({ error: 'Rescue onboarding details are required.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendRescueOnboardingWebhook({
|
||||||
|
action: 'created',
|
||||||
|
workspaceId,
|
||||||
|
flockName: parsed.data.name,
|
||||||
|
ownerEmail: req.auth!.user.email,
|
||||||
|
requestedByUserId: req.auth!.user.id,
|
||||||
|
rescueOnboarding: parsed.data.rescueOnboarding,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const workspace = await createWorkspace({
|
const workspace = await createWorkspace({
|
||||||
id: workspaceId,
|
id: workspaceId,
|
||||||
name: parsed.data.name,
|
name: parsed.data.name,
|
||||||
@@ -2209,6 +2284,7 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
|||||||
const currentWorkspace = req.auth!.workspace;
|
const currentWorkspace = req.auth!.workspace;
|
||||||
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan ?? currentWorkspace.billing_plan);
|
const billingPlan = resolveBillingPlan(parsed.data.workspaceType, parsed.data.billingPlan ?? currentWorkspace.billing_plan);
|
||||||
const billingInterval = parsed.data.workspaceType === 'rescue' ? 'monthly' : (parsed.data.billingInterval ?? currentWorkspace.billing_interval);
|
const billingInterval = parsed.data.workspaceType === 'rescue' ? 'monthly' : (parsed.data.billingInterval ?? currentWorkspace.billing_interval);
|
||||||
|
const isConvertingToRescue = currentWorkspace.workspace_type !== 'rescue' && parsed.data.workspaceType === 'rescue';
|
||||||
const canUpdateWorkspace =
|
const canUpdateWorkspace =
|
||||||
isAdminUser(req.auth!.user) ||
|
isAdminUser(req.auth!.user) ||
|
||||||
subscriptionAllowsWrite(currentWorkspace) ||
|
subscriptionAllowsWrite(currentWorkspace) ||
|
||||||
@@ -2225,6 +2301,22 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isConvertingToRescue) {
|
||||||
|
if (!parsed.data.rescueOnboarding) {
|
||||||
|
res.status(400).json({ error: 'Rescue onboarding details are required.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendRescueOnboardingWebhook({
|
||||||
|
action: 'converted',
|
||||||
|
workspaceId: currentWorkspace.id,
|
||||||
|
flockName: parsed.data.name,
|
||||||
|
ownerEmail: req.auth!.user.email,
|
||||||
|
requestedByUserId: req.auth!.user.id,
|
||||||
|
rescueOnboarding: parsed.data.rescueOnboarding,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const workspace = await updateWorkspace({
|
const workspace = await updateWorkspace({
|
||||||
workspaceId: currentWorkspace.id,
|
workspaceId: currentWorkspace.id,
|
||||||
name: parsed.data.name,
|
name: parsed.data.name,
|
||||||
@@ -2234,7 +2326,7 @@ app.put('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(
|
|||||||
billingInterval,
|
billingInterval,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workspace?.workspace_type === 'rescue' && currentWorkspace.workspace_type !== 'rescue') {
|
if (workspace?.workspace_type === 'rescue' && isConvertingToRescue) {
|
||||||
await sendRescueStatusNotification({
|
await sendRescueStatusNotification({
|
||||||
workspace,
|
workspace,
|
||||||
ownerEmail: req.auth!.user.email,
|
ownerEmail: req.auth!.user.email,
|
||||||
|
|||||||
+185
-12
@@ -194,12 +194,21 @@ type MemorializeBirdFormState = {
|
|||||||
notifyOnMemorialDay: boolean;
|
notifyOnMemorialDay: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type RescueOnboardingFormState = {
|
||||||
|
name: string;
|
||||||
|
city: string;
|
||||||
|
state: string;
|
||||||
|
ein: string;
|
||||||
|
website: string;
|
||||||
|
};
|
||||||
|
|
||||||
type WorkspaceFormState = {
|
type WorkspaceFormState = {
|
||||||
name: string;
|
name: string;
|
||||||
workspaceType: WorkspaceType;
|
workspaceType: WorkspaceType;
|
||||||
billingEmail: string;
|
billingEmail: string;
|
||||||
billingPlan: HouseholdBillingPlan;
|
billingPlan: HouseholdBillingPlan;
|
||||||
billingInterval: BillingInterval;
|
billingInterval: BillingInterval;
|
||||||
|
rescueOnboarding: RescueOnboardingFormState;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WorkspaceMemberFormState = {
|
type WorkspaceMemberFormState = {
|
||||||
@@ -214,6 +223,7 @@ type WorkspaceCreateFormState = {
|
|||||||
billingEmail: string;
|
billingEmail: string;
|
||||||
billingPlan: HouseholdBillingPlan;
|
billingPlan: HouseholdBillingPlan;
|
||||||
billingInterval: BillingInterval;
|
billingInterval: BillingInterval;
|
||||||
|
rescueOnboarding: RescueOnboardingFormState;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AuthFormState = {
|
type AuthFormState = {
|
||||||
@@ -320,12 +330,21 @@ const emptyMemorializeBirdForm = (): MemorializeBirdFormState => ({
|
|||||||
notifyOnMemorialDay: false,
|
notifyOnMemorialDay: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emptyRescueOnboardingForm = (): RescueOnboardingFormState => ({
|
||||||
|
name: '',
|
||||||
|
city: '',
|
||||||
|
state: '',
|
||||||
|
ein: '',
|
||||||
|
website: '',
|
||||||
|
});
|
||||||
|
|
||||||
const emptyWorkspaceForm: WorkspaceFormState = {
|
const emptyWorkspaceForm: WorkspaceFormState = {
|
||||||
name: 'My Flock',
|
name: 'My Flock',
|
||||||
workspaceType: 'standard',
|
workspaceType: 'standard',
|
||||||
billingEmail: '',
|
billingEmail: '',
|
||||||
billingPlan: 'household_basic',
|
billingPlan: 'household_basic',
|
||||||
billingInterval: 'monthly',
|
billingInterval: 'monthly',
|
||||||
|
rescueOnboarding: emptyRescueOnboardingForm(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const emptyWorkspaceMemberForm: WorkspaceMemberFormState = {
|
const emptyWorkspaceMemberForm: WorkspaceMemberFormState = {
|
||||||
@@ -340,6 +359,7 @@ const emptyWorkspaceCreateForm: WorkspaceCreateFormState = {
|
|||||||
billingEmail: '',
|
billingEmail: '',
|
||||||
billingPlan: 'household_basic',
|
billingPlan: 'household_basic',
|
||||||
billingInterval: 'monthly',
|
billingInterval: 'monthly',
|
||||||
|
rescueOnboarding: emptyRescueOnboardingForm(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const emptyAuthForm: AuthFormState = {
|
const emptyAuthForm: AuthFormState = {
|
||||||
@@ -1594,6 +1614,7 @@ function App() {
|
|||||||
billingEmail: session.activeWorkspace.billingEmail ?? '',
|
billingEmail: session.activeWorkspace.billingEmail ?? '',
|
||||||
billingPlan: isHouseholdPlan(session.activeWorkspace.billingPlan) ? session.activeWorkspace.billingPlan : 'household_basic',
|
billingPlan: isHouseholdPlan(session.activeWorkspace.billingPlan) ? session.activeWorkspace.billingPlan : 'household_basic',
|
||||||
billingInterval: session.activeWorkspace.billingInterval,
|
billingInterval: session.activeWorkspace.billingInterval,
|
||||||
|
rescueOnboarding: emptyRescueOnboardingForm(),
|
||||||
});
|
});
|
||||||
setWorkspaceCreateForm((current) => ({
|
setWorkspaceCreateForm((current) => ({
|
||||||
...current,
|
...current,
|
||||||
@@ -2250,6 +2271,7 @@ function App() {
|
|||||||
billingEmail: workspaceCreateForm.billingEmail,
|
billingEmail: workspaceCreateForm.billingEmail,
|
||||||
billingPlan: workspaceCreateForm.workspaceType === 'rescue' ? undefined : workspaceCreateForm.billingPlan,
|
billingPlan: workspaceCreateForm.workspaceType === 'rescue' ? undefined : workspaceCreateForm.billingPlan,
|
||||||
billingInterval: workspaceCreateForm.workspaceType === 'rescue' ? undefined : workspaceCreateForm.billingInterval,
|
billingInterval: workspaceCreateForm.workspaceType === 'rescue' ? undefined : workspaceCreateForm.billingInterval,
|
||||||
|
rescueOnboarding: workspaceCreateForm.workspaceType === 'rescue' ? workspaceCreateForm.rescueOnboarding : undefined,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3123,6 +3145,7 @@ function App() {
|
|||||||
...workspaceForm,
|
...workspaceForm,
|
||||||
billingPlan: workspaceForm.workspaceType === 'rescue' ? undefined : workspaceForm.billingPlan,
|
billingPlan: workspaceForm.workspaceType === 'rescue' ? undefined : workspaceForm.billingPlan,
|
||||||
billingInterval: workspaceForm.workspaceType === 'rescue' ? undefined : workspaceForm.billingInterval,
|
billingInterval: workspaceForm.workspaceType === 'rescue' ? undefined : workspaceForm.billingInterval,
|
||||||
|
rescueOnboarding: workspace?.workspaceType === 'standard' && workspaceForm.workspaceType === 'rescue' ? workspaceForm.rescueOnboarding : undefined,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3156,6 +3179,7 @@ function App() {
|
|||||||
billingEmail: savedWorkspace.billingEmail ?? '',
|
billingEmail: savedWorkspace.billingEmail ?? '',
|
||||||
billingPlan: isHouseholdPlan(savedWorkspace.billingPlan) ? savedWorkspace.billingPlan : 'household_basic',
|
billingPlan: isHouseholdPlan(savedWorkspace.billingPlan) ? savedWorkspace.billingPlan : 'household_basic',
|
||||||
billingInterval: savedWorkspace.billingInterval,
|
billingInterval: savedWorkspace.billingInterval,
|
||||||
|
rescueOnboarding: emptyRescueOnboardingForm(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return savedWorkspace;
|
return savedWorkspace;
|
||||||
@@ -3259,6 +3283,7 @@ function App() {
|
|||||||
billingEmail: savedWorkspace.billingEmail ?? '',
|
billingEmail: savedWorkspace.billingEmail ?? '',
|
||||||
billingPlan: isHouseholdPlan(savedWorkspace.billingPlan) ? savedWorkspace.billingPlan : 'household_basic',
|
billingPlan: isHouseholdPlan(savedWorkspace.billingPlan) ? savedWorkspace.billingPlan : 'household_basic',
|
||||||
billingInterval: savedWorkspace.billingInterval,
|
billingInterval: savedWorkspace.billingInterval,
|
||||||
|
rescueOnboarding: emptyRescueOnboardingForm(),
|
||||||
});
|
});
|
||||||
} catch (workspaceError) {
|
} catch (workspaceError) {
|
||||||
setError(workspaceError instanceof Error ? workspaceError.message : 'Unable to cancel rescue status request.');
|
setError(workspaceError instanceof Error ? workspaceError.message : 'Unable to cancel rescue status request.');
|
||||||
@@ -4547,12 +4572,20 @@ function App() {
|
|||||||
Flock type
|
Flock type
|
||||||
<select
|
<select
|
||||||
value={workspaceForm.workspaceType}
|
value={workspaceForm.workspaceType}
|
||||||
onChange={(event) =>
|
onChange={(event) => {
|
||||||
|
const workspaceType = event.target.value as WorkspaceFormState['workspaceType'];
|
||||||
setWorkspaceForm({
|
setWorkspaceForm({
|
||||||
...workspaceForm,
|
...workspaceForm,
|
||||||
workspaceType: event.target.value as WorkspaceFormState['workspaceType'],
|
workspaceType,
|
||||||
})
|
rescueOnboarding:
|
||||||
}
|
workspaceType === 'rescue'
|
||||||
|
? {
|
||||||
|
...workspaceForm.rescueOnboarding,
|
||||||
|
name: workspaceForm.rescueOnboarding.name || workspaceForm.name,
|
||||||
|
}
|
||||||
|
: workspaceForm.rescueOnboarding,
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<option value="standard">Standard household</option>
|
<option value="standard">Standard household</option>
|
||||||
<option value="rescue">Rescue</option>
|
<option value="rescue">Rescue</option>
|
||||||
@@ -4567,6 +4600,72 @@ function App() {
|
|||||||
</span>
|
</span>
|
||||||
</article>
|
</article>
|
||||||
) : null}
|
) : null}
|
||||||
|
{workspace?.workspaceType === 'standard' && workspaceForm.workspaceType === 'rescue' ? (
|
||||||
|
<section className="settings-nested-card">
|
||||||
|
<h3>Rescue onboarding</h3>
|
||||||
|
<label>
|
||||||
|
Rescue Name
|
||||||
|
<input
|
||||||
|
value={workspaceForm.rescueOnboarding.name}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceForm({
|
||||||
|
...workspaceForm,
|
||||||
|
rescueOnboarding: { ...workspaceForm.rescueOnboarding, name: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
City
|
||||||
|
<input
|
||||||
|
value={workspaceForm.rescueOnboarding.city}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceForm({
|
||||||
|
...workspaceForm,
|
||||||
|
rescueOnboarding: { ...workspaceForm.rescueOnboarding, city: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
State
|
||||||
|
<input
|
||||||
|
value={workspaceForm.rescueOnboarding.state}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceForm({
|
||||||
|
...workspaceForm,
|
||||||
|
rescueOnboarding: { ...workspaceForm.rescueOnboarding, state: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
EIN
|
||||||
|
<input
|
||||||
|
value={workspaceForm.rescueOnboarding.ein}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceForm({
|
||||||
|
...workspaceForm,
|
||||||
|
rescueOnboarding: { ...workspaceForm.rescueOnboarding, ein: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Website
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
value={workspaceForm.rescueOnboarding.website}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceForm({
|
||||||
|
...workspaceForm,
|
||||||
|
rescueOnboarding: { ...workspaceForm.rescueOnboarding, website: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
{workspaceForm.workspaceType === 'rescue' ? (
|
{workspaceForm.workspaceType === 'rescue' ? (
|
||||||
<article className="summary-card">
|
<article className="summary-card">
|
||||||
<strong>{formatBillingPlanName('rescue_free')}</strong>
|
<strong>{formatBillingPlanName('rescue_free')}</strong>
|
||||||
@@ -4958,12 +5057,20 @@ function App() {
|
|||||||
Flock type
|
Flock type
|
||||||
<select
|
<select
|
||||||
value={workspaceCreateForm.workspaceType}
|
value={workspaceCreateForm.workspaceType}
|
||||||
onChange={(event) =>
|
onChange={(event) => {
|
||||||
|
const workspaceType = event.target.value as WorkspaceCreateFormState['workspaceType'];
|
||||||
setWorkspaceCreateForm({
|
setWorkspaceCreateForm({
|
||||||
...workspaceCreateForm,
|
...workspaceCreateForm,
|
||||||
workspaceType: event.target.value as WorkspaceCreateFormState['workspaceType'],
|
workspaceType,
|
||||||
})
|
rescueOnboarding:
|
||||||
}
|
workspaceType === 'rescue'
|
||||||
|
? {
|
||||||
|
...workspaceCreateForm.rescueOnboarding,
|
||||||
|
name: workspaceCreateForm.rescueOnboarding.name || workspaceCreateForm.name,
|
||||||
|
}
|
||||||
|
: workspaceCreateForm.rescueOnboarding,
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<option value="standard">Standard household</option>
|
<option value="standard">Standard household</option>
|
||||||
<option value="rescue">Rescue</option>
|
<option value="rescue">Rescue</option>
|
||||||
@@ -5011,10 +5118,76 @@ function App() {
|
|||||||
</article>
|
</article>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<article className="summary-card">
|
<>
|
||||||
<strong>{formatBillingPlanName('rescue_free')}</strong>
|
<section className="settings-nested-card">
|
||||||
<span>No billing is applied to rescue flocks.</span>
|
<h3>Rescue onboarding</h3>
|
||||||
</article>
|
<label>
|
||||||
|
Rescue Name
|
||||||
|
<input
|
||||||
|
value={workspaceCreateForm.rescueOnboarding.name}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceCreateForm({
|
||||||
|
...workspaceCreateForm,
|
||||||
|
rescueOnboarding: { ...workspaceCreateForm.rescueOnboarding, name: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
City
|
||||||
|
<input
|
||||||
|
value={workspaceCreateForm.rescueOnboarding.city}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceCreateForm({
|
||||||
|
...workspaceCreateForm,
|
||||||
|
rescueOnboarding: { ...workspaceCreateForm.rescueOnboarding, city: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
State
|
||||||
|
<input
|
||||||
|
value={workspaceCreateForm.rescueOnboarding.state}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceCreateForm({
|
||||||
|
...workspaceCreateForm,
|
||||||
|
rescueOnboarding: { ...workspaceCreateForm.rescueOnboarding, state: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
EIN
|
||||||
|
<input
|
||||||
|
value={workspaceCreateForm.rescueOnboarding.ein}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceCreateForm({
|
||||||
|
...workspaceCreateForm,
|
||||||
|
rescueOnboarding: { ...workspaceCreateForm.rescueOnboarding, ein: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Website
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
value={workspaceCreateForm.rescueOnboarding.website}
|
||||||
|
onChange={(event) =>
|
||||||
|
setWorkspaceCreateForm({
|
||||||
|
...workspaceCreateForm,
|
||||||
|
rescueOnboarding: { ...workspaceCreateForm.rescueOnboarding, website: event.target.value },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<article className="summary-card">
|
||||||
|
<strong>{formatBillingPlanName('rescue_free')}</strong>
|
||||||
|
<span>No billing is applied to rescue flocks.</span>
|
||||||
|
</article>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<label>
|
<label>
|
||||||
Billing contact email
|
Billing contact email
|
||||||
|
|||||||
Reference in New Issue
Block a user