Midday / apps /dashboard /src /components /forms /create-team-form.tsx
Jules
Final deployment with all fixes and verified content
c09f67c
"use client";
import { uniqueCurrencies } from "@midday/location/currencies";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@midday/ui/form";
import { Input } from "@midday/ui/input";
import { SubmitButton } from "@midday/ui/submit-button";
import { getDefaultFiscalYearStartMonth } from "@midday/utils";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { use, useEffect, useRef, useState } from "react";
import { z } from "zod/v3";
import { revalidateAfterTeamChange } from "@/actions/revalidate-action";
import { SelectCurrency } from "@/components/select-currency";
import { useZodForm } from "@/hooks/use-zod-form";
import { useTRPC } from "@/trpc/client";
import { CountrySelector } from "../country-selector";
import { SelectFiscalMonth } from "../select-fiscal-month";
const formSchema = z.object({
name: z.string().min(2, {
message: "Team name must be at least 2 characters.",
}),
countryCode: z.string(),
baseCurrency: z.string(),
fiscalYearStartMonth: z.number().int().min(1).max(12).nullable().optional(),
});
type FormValues = z.infer<typeof formSchema>;
type Props = {
defaultCurrencyPromise: Promise<string>;
defaultCountryCodePromise: Promise<string>;
};
export function CreateTeamForm({
defaultCurrencyPromise,
defaultCountryCodePromise,
}: Props) {
const currency = use(defaultCurrencyPromise);
const countryCode = use(defaultCountryCodePromise);
const trpc = useTRPC();
const queryClient = useQueryClient();
const [isLoading, setIsLoading] = useState(false);
const isSubmittedRef = useRef(false);
const createTeamMutation = useMutation(
trpc.team.create.mutationOptions({
onSuccess: async () => {
// Lock the form permanently - user will be redirected
setIsLoading(true);
isSubmittedRef.current = true;
await queryClient.invalidateQueries();
await revalidateAfterTeamChange();
},
onError: () => {
setIsLoading(false);
isSubmittedRef.current = false;
},
}),
);
const form = useZodForm(formSchema, {
defaultValues: {
name: "",
baseCurrency: currency,
countryCode: countryCode ?? "",
fiscalYearStartMonth: getDefaultFiscalYearStartMonth(countryCode),
},
});
// Update fiscal year when country changes
const selectedCountryCode = form.watch("countryCode");
useEffect(() => {
const defaultFiscalYear =
getDefaultFiscalYearStartMonth(selectedCountryCode);
if (defaultFiscalYear !== form.getValues("fiscalYearStartMonth")) {
form.setValue("fiscalYearStartMonth", defaultFiscalYear);
}
}, [selectedCountryCode, form]);
// Computed loading state that can never be reset unexpectedly
const isFormLocked = isLoading || isSubmittedRef.current;
function onSubmit(values: FormValues) {
if (isFormLocked) {
return;
}
setIsLoading(true);
isSubmittedRef.current = true;
createTeamMutation.mutate({
name: values.name,
baseCurrency: values.baseCurrency,
countryCode: values.countryCode,
fiscalYearStartMonth: values.fiscalYearStartMonth,
switchTeam: true,
});
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem className="mt-4 w-full">
<FormLabel className="text-xs text-[#666] font-normal">
Company name
</FormLabel>
<FormControl>
<Input
autoFocus
placeholder="Ex: Acme Marketing or Acme Co"
autoComplete="off"
autoCapitalize="none"
autoCorrect="off"
spellCheck="false"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="countryCode"
render={({ field }) => (
<FormItem className="mt-4 w-full">
<FormLabel className="text-xs text-[#666] font-normal">
Country
</FormLabel>
<FormControl className="w-full">
<CountrySelector
defaultValue={field.value ?? ""}
onSelect={(code, name) => {
field.onChange(name);
form.setValue("countryCode", code);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="baseCurrency"
render={({ field }) => (
<FormItem className="mt-4 border-b border-border pb-4">
<FormLabel className="text-xs text-[#666] font-normal">
Base currency
</FormLabel>
<FormControl>
<SelectCurrency currencies={uniqueCurrencies} {...field} />
</FormControl>
<FormDescription>
If you have multiple accounts in different currencies, this will
be the default currency for your company. You can change it
later.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="fiscalYearStartMonth"
render={({ field }) => (
<FormItem className="mt-4 border-b border-border pb-4">
<FormLabel className="text-xs text-[#666] font-normal">
Fiscal year starts
</FormLabel>
<FormControl>
<SelectFiscalMonth {...field} />
</FormControl>
<FormDescription>
When does your company's fiscal year begin? This determines
default date ranges for reports. You can change it later.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<SubmitButton
className="mt-6 w-full"
type="submit"
isSubmitting={isFormLocked}
>
Create
</SubmitButton>
</form>
</Form>
);
}