Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🦂 Update create wave ui #205

Merged
merged 4 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ const FORM_FIELD_PARAMS = {

export const mainDetailsSchema = z.object({
imageId: z.string().optional(),
name: specificLengthStringSchema(
"Project name",
FORM_FIELD_PARAMS.name.min,
FORM_FIELD_PARAMS.name.max,
),
name: specificLengthStringSchema("Project name", FORM_FIELD_PARAMS.name),
entityName: z.string(),
duration: z.string(),
budget: positiveNumberSchema("Project budget"),
Expand Down
4 changes: 1 addition & 3 deletions src/app/waves/create/createWaveAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { insertWave } from "@/drizzle/queries/waves";

import { createWaveSchema } from "./createWaveSchema";

export async function createWaveAction(data: createWaveSchema) {
export async function createWaveAction(data: any) {
const [{ id }] = await insertWave({
name: data.waveName,
startsAt: data.duration.from,
Expand Down
10 changes: 3 additions & 7 deletions src/app/waves/create/createWaveForm.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { BackButton } from "@/components/ui/backButton";
import { PageTitle } from "@/components/ui/pageTitle";
import { Stepper } from "@/components/ui/stepper";
import { CampaignIcon } from "@/components/icons/campaignIcon";
import { ClockIcon } from "@/components/icons/clockIcon";
import { ComputerIcon } from "@/components/icons/computerIcon";

import { MainDetails } from "./steps/mainDetails";
import { Timeline } from "./steps/timeline";

const stepsConfig = [
{ name: "Main details", icon: <ComputerIcon className="h-6 w-6" /> },
{
name: "Timeline",
icon: <ClockIcon className="h-6 w-6" />,
},
{
name: "Additional info",
icon: <CampaignIcon className="h-6 w-6" />,
},
];

export function CreateWaveForm() {
Expand All @@ -26,9 +22,9 @@ export function CreateWaveForm() {
<BackButton href="/" />
<PageTitle>Create new wave</PageTitle>
</div>
<Stepper currentStep={0} stepsConfig={stepsConfig}>
<MainDetails />
<Stepper currentStep={1} stepsConfig={stepsConfig}>
<MainDetails />
<Timeline />
</Stepper>
</>
);
Expand Down
15 changes: 0 additions & 15 deletions src/app/waves/create/createWaveSchema.ts

This file was deleted.

112 changes: 48 additions & 64 deletions src/app/waves/create/steps/mainDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,117 +1,101 @@
"use client";

import { specificLengthStringSchema } from "@/constants/validationSchemas";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";

import {
addDays,
formatDate,
formatDateRange,
getStartOfDate,
} from "@/lib/dates";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Form,
FormControl,
FormCounter,
FormField,
FormFooter,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { CalendarIcon } from "@/components/icons/calendarIcon";
import { Textarea } from "@/components/ui/textarea";
import { ArrowIcon } from "@/components/icons/arrowIcon";

import { createWaveAction } from "../createWaveAction";
import { createWaveSchema } from "../createWaveSchema";
const FORM_FIELD_PARAMS = {
waveName: {
min: 3,
max: 20,
},
waveSummary: {
min: 3,
max: 160,
},
};

export function MainDetails() {
const todayDate = getStartOfDate(new Date());
export const mainDetailsSchema = z.object({
waveName: specificLengthStringSchema("Wave name", FORM_FIELD_PARAMS.waveName),
waveSummary: specificLengthStringSchema(
"Wave summary",
FORM_FIELD_PARAMS.waveSummary,
),
});

export type mainDetailsSchema = z.infer<typeof mainDetailsSchema>;

const form = useForm<createWaveSchema>({
resolver: zodResolver(createWaveSchema),
export function MainDetails() {
const form = useForm<mainDetailsSchema>({
resolver: zodResolver(mainDetailsSchema),
defaultValues: {
waveName: "",
duration: {
from: todayDate,
to: addDays(todayDate, 30),
},
waveSummary: "",
},
});

return (
<Form {...form}>
<form
className="flex w-full flex-col gap-6"
onSubmit={form.handleSubmit(async (data) => {
await createWaveAction(data);
})}
onSubmit={form.handleSubmit(async (data) => {})}
>
<FormField
control={form.control}
name="waveName"
render={({ field }) => (
<FormItem>
<FormItem aria-required>
<FormLabel>Wave name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormCounter
current={field.value.length}
limit={FORM_FIELD_PARAMS[field.name].max}
/>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="duration"
name="waveSummary"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Wave duration</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
className="pl-3 text-left font-normal"
>
{field.value.from ? (
field.value.to ? (
<>
{formatDateRange(field.value.from, field.value.to)}
</>
) : (
formatDate(field.value.from)
)
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="range"
numberOfMonths={2}
defaultMonth={field.value.from}
selected={field.value}
onSelect={field.onChange}
disabled={(date) => date < todayDate}
/>
</PopoverContent>
</Popover>
<FormItem aria-required>
<FormLabel>Wave summary</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormCounter
current={field.value.length}
limit={FORM_FIELD_PARAMS[field.name].max}
/>
<FormMessage />
</FormItem>
)}
/>
<FormFooter className="justify-end">
<Button disabled={form.formState.isSubmitting}>Create wave</Button>
<Button disabled={form.formState.isSubmitting}>
Next
<ArrowIcon direction="right" />
</Button>
</FormFooter>
</form>
</Form>
Expand Down
145 changes: 145 additions & 0 deletions src/app/waves/create/steps/timeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import {
useForm,
type ControllerProps,
type FieldPath,
type FieldValues,
} from "react-hook-form";
import { z } from "zod";

import { formatDate, getStartOfDate } from "@/lib/dates";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Form,
FormControl,
FormField,
FormFooter,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { ArrowIcon } from "@/components/icons/arrowIcon";
import { CalendarIcon } from "@/components/icons/calendarIcon";

export const timelineSchema = z.object({
openStartDate: z.date(),
denoisingStartDate: z.date(),
assesmentStartDate: z.date(),
closeDate: z.date(),
});

export type timelineSchema = z.infer<typeof timelineSchema>;

export function Timeline() {
const form = useForm<timelineSchema>({
resolver: zodResolver(timelineSchema),
});

return (
<Form {...form}>
<form
className="flex w-full flex-col gap-6"
onSubmit={form.handleSubmit(async (data) => {})}
>
<div className="grid grid-cols-2 gap-y-8 rounded-2xl border p-6">
<CalendarField
control={form.control}
name="openStartDate"
title="Open"
/>
<CalendarField
control={form.control}
name="denoisingStartDate"
title="Denoising"
/>
<CalendarField
control={form.control}
name="assesmentStartDate"
title="Assesment"
/>
<CalendarField
control={form.control}
name="closeDate"
label="Close date"
title="Close"
/>
</div>

<FormFooter>
<Button
disabled={form.formState.isSubmitting}
variant="secondary"
type="button"
>
<ArrowIcon direction="left" />
Back
</Button>

<Button disabled={form.formState.isSubmitting}>
Preview
<ArrowIcon direction="right" />
</Button>
</FormFooter>
</form>
</Form>
);
}

const CalendarField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
name,
control,
label = "Start date",
title,
}: Pick<ControllerProps<TFieldValues, TName>, "control" | "name"> & {
label?: string;
title: string;
}) => {
const todayDate = getStartOfDate(new Date());

return (
<FormField
control={control}
name={name}
render={({ field }) => (
<FormItem aria-required>
<div className="mb-4 font-bold">{title}</div>
<FormLabel>{label}</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
className="h-12 w-40 justify-start gap-2 aria-[invalid=true]:border-destructive"
>
<CalendarIcon className="h-6 w-6" />
{field.value ? formatDate(field.value) : "Pick a date"}
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent align="start">
<Calendar
mode="single"
defaultMonth={field.value}
selected={field.value}
onSelect={field.onChange}
disabled={(date) => date < todayDate}
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
);
};
Loading
Loading