Refactor various components for improved UI consistency and functionality; update layout styles, enhance user interactions, and introduce reset button functionality in forms.

This commit is contained in:
Marco Pedone 2026-01-16 17:11:53 +01:00
parent 1baca2997a
commit c87a7238a7
14 changed files with 150 additions and 275 deletions

View file

@ -190,7 +190,7 @@ export const AreaRiservataLayoutUserView = ({
isAdmin isAdmin
/> />
<div className="mx-4 my-3 flex-1 overflow-auto"> <div className="mx-2 my-3 flex-1 overflow-auto lg:mx-4">
<UserViewHeader /> <UserViewHeader />
{children} {children}

View file

@ -134,6 +134,7 @@ export const MultiSelect = ({
control: () => control: () =>
cn( cn(
"mt-2 w-full rounded-lg h-min-[42px] bg-card shadow-xs outline-hidden text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", "mt-2 w-full rounded-lg h-min-[42px] bg-card shadow-xs outline-hidden text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
disabled && "bg-muted opacity-50 cursor-not-allowed",
), ),
input: () => cn("*:ring-0 focus:*:ring-0"), input: () => cn("*:ring-0 focus:*:ring-0"),
menu: () => menu: () =>

View file

@ -169,21 +169,6 @@ const DocSection = () => {
const { data, updateServizioAnnunci } = useServizioAnnuncio(); const { data, updateServizioAnnunci } = useServizioAnnuncio();
const utils = api.useUtils(); const utils = api.useUtils();
// const { mutate: updateServizio } = api.servizio.UpdateConfermaDoc.useMutation(
// {
// onError: (error) => {
// toast.error(error.message);
// },
// onSuccess: async () => {
// await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
// await utils.servizio.getServizio.invalidate({
// servizioId,
// });
// toast.success("Dati salvati con successo");
// },
// },
// );
const { data: files, isLoading } = api.storage.getAll.useQuery(); const { data: files, isLoading } = api.storage.getAll.useQuery();
@ -622,6 +607,7 @@ const StabileSection = () => {
1000, 1000,
[value], [value],
); );
//TODO selettore per pack consulenza
return ( return (
<div className="flex flex-col gap-2 rounded-md border border-border bg-muted p-2 sm:flex-row"> <div className="flex flex-col gap-2 rounded-md border border-border bg-muted p-2 sm:flex-row">

View file

@ -39,7 +39,7 @@ export const AdminContratto = () => {
aria-label="Open Contratto Section" aria-label="Open Contratto Section"
className="w-fit" className="w-fit"
size="sm" size="sm"
variant={data.doc_contratto_added ? "success" : "secondary"} variant={data.doc_contratto_added ? "success" : "orange"}
> >
{data.doc_contratto_added ? <CircleCheck /> : <Wrench />} {data.doc_contratto_added ? <CircleCheck /> : <Wrench />}
Contratto Contratto

View file

@ -134,12 +134,11 @@ export const UserActions = () => {
<> <>
<div className="flex w-full flex-col flex-wrap gap-2 sm:flex-row"> <div className="flex w-full flex-col flex-wrap gap-2 sm:flex-row">
<ContattiProprietarioModal /> <ContattiProprietarioModal />
{ConfermaUtente
? !data.hasConfermaAdmin && <ConfermaInLavorazioneModal />
: ContattiSbloccati && <ConfermaAnnuncioModal />}
{ContattiSbloccati && !ConfermaUtente && <ConfermaAnnuncioModal />} {AccettatoConferma ? (
{ConfermaUtente && !data.hasConfermaAdmin && (
<ConfermaInLavorazioneModal />
)}
{AccettatoConferma && (
<Link <Link
aria-label="Conferma immobile" aria-label="Conferma immobile"
href={`/servizio/riapri-conferma/${servizioId}/${annuncioId}`} href={`/servizio/riapri-conferma/${servizioId}/${annuncioId}`}
@ -149,8 +148,8 @@ export const UserActions = () => {
Rivedi la conferma Rivedi la conferma
</Button> </Button>
</Link> </Link>
)} ) : (
{ConfermaLavorata && ConfermaLavorata &&
(data.doc_conferma_ref ? ( (data.doc_conferma_ref ? (
<Link <Link
aria-label="Conferma immobile" aria-label="Conferma immobile"
@ -181,7 +180,8 @@ export const UserActions = () => {
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
))} ))
)}
</div> </div>
{data.accettato_conferma_at != null && <PostConfermaSection />} {data.accettato_conferma_at != null && <PostConfermaSection />}

View file

@ -1,3 +1,4 @@
import { Toolbox } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { import {
AdminActions, AdminActions,
@ -10,22 +11,21 @@ import { cn, formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider"; import { useTranslation } from "~/providers/I18nProvider";
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider"; import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller"; import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
import { Button } from "../ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../ui/collapsible";
import { AnnuncioDettaglio, TipoBadge } from "./annuncio_dettaglio"; import { AnnuncioDettaglio, TipoBadge } from "./annuncio_dettaglio";
export const AnnuncioCard = ({ export const AnnuncioCard = ({ className }: { className?: string }) => {
className,
confirming,
}: {
className?: string;
confirming?: boolean;
}) => {
const { isAdmin } = useServizio(); const { isAdmin } = useServizio();
const { data } = useServizioAnnuncio(); const { data } = useServizioAnnuncio();
return ( return (
<BasicAnnuncioCard <BasicAnnuncioCard
className={className} className={className}
confirming={confirming}
data={{ data={{
...data, ...data,
modificato_il: data.modificato_il ? new Date(data.modificato_il) : null, modificato_il: data.modificato_il ? new Date(data.modificato_il) : null,
@ -33,18 +33,63 @@ export const AnnuncioCard = ({
? new Date(data.media_updated_at) ? new Date(data.media_updated_at)
: null, : null,
}} }}
interactions={isAdmin ? <AdminActions /> : <UserActions />} interactions={
isAdmin ? (
<div className="flex flex-col gap-2">
<Collapsible className="space-y-2">
<CollapsibleTrigger>
<Button variant="outline">
<Toolbox />
<span>Azioni Utente</span>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="rounded-md border border-border bg-muted p-2">
<UserActions />
</CollapsibleContent>
</Collapsible>
<AdminActions />
</div>
) : (
<UserActions />
)
}
/> />
); );
}; };
export const BasicAnnuncioCard = ({
interactions,
className,
data,
}: {
interactions?: React.ReactNode;
className?: string;
data: AnnuncioRicerca;
}) => {
return (
<Card className={cn("w-full gap-2 border-white py-3", className)}>
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
<CardTitle className="text-2xl">Annuncio {data.codice}</CardTitle>
</CardHeader>
<CardContent className="px-3">
<div
className={cn("@container flex w-full flex-col gap-4")}
id="container"
>
<AnnuncioDisplay data={data} />
<div className="flex w-full flex-col gap-4">{interactions}</div>
</div>
</CardContent>
</Card>
);
};
export const AnnuncioDisplay = ({ export const AnnuncioDisplay = ({
data, data,
confirming,
className, className,
}: { }: {
data: AnnuncioRicerca; data: AnnuncioRicerca;
confirming?: boolean;
className?: string; className?: string;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -53,7 +98,6 @@ export const AnnuncioDisplay = ({
<div <div
className={cn( className={cn(
"grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] gap-x-4 gap-y-2 rounded-md md:max-w-3xl md:border md:border-white md:bg-[#e6e9ec]/50 lg:gap-x-4 xl:gap-x-6 dark:md:border-primary dark:md:bg-card", "grid @md:grid-cols-[12rem_1fr] grid-cols-[7rem_1fr] gap-x-4 gap-y-2 rounded-md md:max-w-3xl md:border md:border-white md:bg-[#e6e9ec]/50 lg:gap-x-4 xl:gap-x-6 dark:md:border-primary dark:md:bg-card",
confirming && "xl:w-1/2",
className, className,
)} )}
> >
@ -103,42 +147,3 @@ export const AnnuncioDisplay = ({
</div> </div>
); );
}; };
export const BasicAnnuncioCard = ({
interactions,
className,
data,
confirming,
}: {
interactions?: React.ReactNode;
className?: string;
data: AnnuncioRicerca;
confirming?: boolean;
}) => {
return (
<Card className={cn("w-full gap-2 border-white py-3", className)}>
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 px-3 pb-2">
<CardTitle className="text-2xl">Annuncio {data.codice}</CardTitle>
</CardHeader>
<CardContent className="px-3">
<div
className={cn(
"@container flex w-full flex-col gap-4",
confirming && "xl:flex-row xl:gap-6",
)}
id="container"
>
<AnnuncioDisplay confirming={confirming} data={data} />
<div
className={cn(
"flex w-full flex-col gap-4",
confirming && "xl:w-1/2",
)}
>
{interactions}
</div>
</div>
</CardContent>
</Card>
);
};

View file

@ -285,10 +285,7 @@ export const ServizioContent = () => {
servizioId={servizio.servizio_id} servizioId={servizio.servizio_id}
userId={userId} userId={userId}
> >
<AnnuncioCard <AnnuncioCard className="rounded-md border-primary" />
className="rounded-md border-primary"
confirming
/>
</ServizioAnnuncioProvider> </ServizioAnnuncioProvider>
); );
})} })}

View file

@ -6,12 +6,14 @@ import {
ChevronUp, ChevronUp,
ClockAlert, ClockAlert,
Cog, Cog,
ExternalLink,
Info, Info,
Plus, Plus,
SlidersHorizontal, SlidersHorizontal,
Trash2, Trash2,
UserCircle, UserCircle,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { import {
@ -60,6 +62,7 @@ import { cn, formatCurrency } from "~/lib/utils";
import { useServizio } from "~/providers/ServizioProvider"; import { useServizio } from "~/providers/ServizioProvider";
import type { AnnunciId } from "~/schemas/public/Annunci"; import type { AnnunciId } from "~/schemas/public/Annunci";
import type { ServizioServizioId } from "~/schemas/public/Servizio"; import type { ServizioServizioId } from "~/schemas/public/Servizio";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { UsersId } from "~/schemas/public/Users"; import type { UsersId } from "~/schemas/public/Users";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { Confirm } from "../confirm"; import { Confirm } from "../confirm";
@ -131,9 +134,9 @@ const ServizioInfos = () => {
</Button> </Button>
)} )}
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent className="mb-4 flex w-full flex-col gap-8 rounded-md border bg-card p-2 sm:items-start sm:gap-4"> <CollapsibleContent className="mb-4 flex w-full flex-col gap-4 rounded-md border bg-card p-2 sm:items-start sm:gap-6">
<ServizioBasicActions /> <ServizioBasicActions />
<div className="flex w-full flex-col gap-8 sm:flex-row sm:items-start"> <div className="flex w-full flex-col gap-4 sm:flex-row sm:items-start sm:gap-6">
<div className="flex flex-2 flex-col gap-2 sm:w-1/2"> <div className="flex flex-2 flex-col gap-2 sm:w-1/2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -173,7 +176,7 @@ const ServizioBasicActions = ({ className }: { className?: string }) => {
if (isAdmin || canUserEdit) { if (isAdmin || canUserEdit) {
return ( return (
<div className={cn("flex flex-wrap items-center gap-3", className)}> <div className={cn("flex flex-wrap items-center gap-2", className)}>
<EditParametri /> <EditParametri />
<InterruzioneServizio /> <InterruzioneServizio />
</div> </div>
@ -234,6 +237,27 @@ export const ServizioPacksInfos = () => {
<span className="font-medium">{packs.saldo.message}</span> <span className="font-medium">{packs.saldo.message}</span>
)} )}
</div> </div>
{servizio.tipologia === TipologiaPosizioneEnum.Stabile && (
<div className="flex flex-1 flex-col gap-2 rounded-md bg-secondary/50 p-2">
<>
<span className="font-medium">Consulenza</span>
<span className="text-muted-foreground text-sm">
Il prezzo della consulenza varia in base alla tipologia
contrattuale.{" "}
</span>
<span className="font-semibold">
<Link
className="flex items-center gap-2 underline underline-offset-2"
href={"/prezzi#contratti"}
target="_blank"
>
<span>Vedi prezzi contratti</span>
<ExternalLink className="size-4 stroke-foreground" />
</Link>
</span>
</>
</div>
)}
</div> </div>
</div> </div>
); );
@ -390,7 +414,7 @@ const InterruzioneServizio = () => {
servizio.annunci.some((a) => a.user_confirmed_at !== null)) servizio.annunci.some((a) => a.user_confirmed_at !== null))
} }
> >
<ClockAlert /> <span>Richiedi Interruzione</span> <ClockAlert /> <span>Interruzione</span>
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>

View file

@ -1,169 +0,0 @@
"use client";
import { type Label as LabelPrimitive, Slot as SlotPrimitive } from "radix-ui";
import * as React from "react";
import {
Controller,
type ControllerProps,
type FieldPath,
type FieldValues,
FormProvider,
useFormContext,
useFormState,
} from "react-hook-form";
import { Label } from "~/components/ui/label";
import { cn } from "~/lib/utils";
const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
);
};
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState } = useFormContext();
const formState = useFormState({ name: fieldContext.name });
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}
const { id } = itemContext;
return {
formDescriptionId: `${id}-form-item-description`,
formItemId: `${id}-form-item`,
formMessageId: `${id}-form-item-message`,
id,
name: fieldContext.name,
...fieldState,
};
};
type FormItemContextValue = {
id: string;
};
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
const id = React.useId();
return (
<FormItemContext.Provider value={{ id }}>
<div
className={cn("grid gap-2", className)}
data-slot="form-item"
{...props}
/>
</FormItemContext.Provider>
);
}
function FormLabel({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
const { error, formItemId } = useFormField();
return (
<Label
className={cn("data-[error=true]:text-destructive", className)}
data-error={!!error}
data-slot="form-label"
htmlFor={formItemId}
{...props}
/>
);
}
function FormControl({
...props
}: React.ComponentProps<typeof SlotPrimitive.Slot>) {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
return (
<SlotPrimitive.Slot
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
data-slot="form-control"
id={formItemId}
{...props}
/>
);
}
function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
const { formDescriptionId } = useFormField();
return (
<p
className={cn("text-muted-foreground text-sm", className)}
data-slot="form-description"
id={formDescriptionId}
{...props}
/>
);
}
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message ?? "") : props.children;
if (!body) {
return null;
}
return (
<p
className={cn("text-destructive text-sm", className)}
data-slot="form-message"
id={formMessageId}
{...props}
>
{body}
</p>
);
}
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
};

View file

@ -27,7 +27,7 @@ function Slider({
return ( return (
<SliderPrimitive.Root <SliderPrimitive.Root
className={cn( className={cn(
"relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col data-[disabled]:opacity-50", "relative flex w-full touch-none select-none items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col data-disabled:opacity-50",
className, className,
)} )}
data-slot="slider" data-slot="slider"
@ -39,7 +39,7 @@ function Slider({
> >
<SliderPrimitive.Track <SliderPrimitive.Track
className={cn( className={cn(
"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5", "relative grow cursor-pointer overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-1.5",
)} )}
data-slot="slider-track" data-slot="slider-track"
> >
@ -54,7 +54,7 @@ function Slider({
{Array.from({ length: _values.length }, (_, index) => ( {Array.from({ length: _values.length }, (_, index) => (
<SliderPrimitive.Thumb <SliderPrimitive.Thumb
className={cn( className={cn(
"block size-4 shrink-0 rounded-full border border-primary bg-background shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:outline-hidden focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50", "block size-4 shrink-0 cursor-pointer rounded-full border border-primary bg-background shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:outline-hidden focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50",
thumbClassName, thumbClassName,
)} )}
data-slot="slider-thumb" data-slot="slider-thumb"

View file

@ -30,6 +30,7 @@ import { useZodForm } from "~/lib/zodForm";
import { useTranslation } from "~/providers/I18nProvider"; import { useTranslation } from "~/providers/I18nProvider";
import type { Servizio } from "~/schemas/public/Servizio"; import type { Servizio } from "~/schemas/public/Servizio";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import { FieldResetButton } from "./comps";
const Schema = z const Schema = z
.object({ .object({
@ -228,13 +229,9 @@ export const FormNewServizio = ({
{t.parametri.dalmese_label}... {t.parametri.dalmese_label}...
</FormLabel> </FormLabel>
{field.value !== null && !initialData && ( {field.value !== null && !initialData && (
<button <FieldResetButton
className="text-sm"
onClick={() => form.setValue("entromese", null)} onClick={() => form.setValue("entromese", null)}
type="button" />
>
Reset
</button>
)} )}
<FormMessage /> <FormMessage />
@ -253,6 +250,7 @@ export const FormNewServizio = ({
elementId="select-mesi" elementId="select-mesi"
elementName="entro_mese" elementName="entro_mese"
isMulti={false} isMulti={false}
key={field.value}
onValueChange={(value) => { onValueChange={(value) => {
field.onChange( field.onChange(
value.value !== "" ? parseInt(value.value) : null, value.value !== "" ? parseInt(value.value) : null,
@ -274,17 +272,19 @@ export const FormNewServizio = ({
render={({ field }) => ( render={({ field }) => (
<FormItem className="w-full"> <FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2"> <div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="permanenza"> <FormLabel
className={cn(
tipologia !== TipologiaPosizioneEnum.Transitorio &&
"opacity-50",
)}
htmlFor="permanenza"
>
{t.parametri.permanenza_label} {t.parametri.permanenza_label}
</FormLabel> </FormLabel>
{field.value !== null && !initialData && ( {field.value !== null && !initialData && (
<button <FieldResetButton
className="text-sm"
onClick={() => form.setValue("permanenza", null)} onClick={() => form.setValue("permanenza", null)}
type="button" />
>
Reset
</button>
)} )}
<FormMessage /> <FormMessage />
</div> </div>
@ -303,6 +303,7 @@ export const FormNewServizio = ({
elementId="permanenza" elementId="permanenza"
elementName="permanenza" elementName="permanenza"
isMulti={false} isMulti={false}
key={field.value}
onValueChange={(value) => { onValueChange={(value) => {
field.onChange( field.onChange(
value.value !== "" ? parseInt(value.value) : null, value.value !== "" ? parseInt(value.value) : null,
@ -314,7 +315,11 @@ export const FormNewServizio = ({
placeholder="Seleziona..." placeholder="Seleziona..."
/> />
</FormControl> </FormControl>
<FormDescription>{t.parametri.permanenza_desc}</FormDescription> {tipologia === TipologiaPosizioneEnum.Transitorio && (
<FormDescription>
{t.parametri.permanenza_desc}
</FormDescription>
)}
</FormItem> </FormItem>
)} )}
/> />

View file

@ -639,7 +639,13 @@ export const FormNewServizioAcquisto = ({
render={({ field }) => ( render={({ field }) => (
<FormItem className="w-full"> <FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2"> <div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="permanenza"> <FormLabel
className={cn(
tipologia !== TipologiaPosizioneEnum.Transitorio &&
"opacity-50",
)}
htmlFor="permanenza"
>
{t.parametri.permanenza_label} {t.parametri.permanenza_label}
</FormLabel> </FormLabel>
<FormMessage /> <FormMessage />
@ -673,9 +679,11 @@ export const FormNewServizioAcquisto = ({
placeholder="Seleziona..." placeholder="Seleziona..."
/> />
</FormControl> </FormControl>
{tipologia === TipologiaPosizioneEnum.Transitorio && (
<FormDescription> <FormDescription>
{t.parametri.permanenza_desc} {t.parametri.permanenza_desc}
</FormDescription> </FormDescription>
)}
</FormItem> </FormItem>
)} )}
/> />

View file

@ -255,7 +255,7 @@ export const ProfileFormAnagrafica = ({
<FormControl> <FormControl>
<Button <Button
className={cn( className={cn(
"h-[42px] w-full pl-3 text-left font-medium", "h-10.5 w-full pl-3 text-left font-medium",
!field.value && "text-muted-foreground", !field.value && "text-muted-foreground",
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent", "rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
)} )}

View file

@ -0,0 +1,18 @@
import { RefreshCcw } from "lucide-react";
import { cn } from "~/lib/utils";
export const FieldResetButton = (
props: React.ButtonHTMLAttributes<HTMLButtonElement>,
) => {
const { className, ...rest } = props;
return (
<button
{...rest}
className={cn("flex items-center gap-2 text-sm leading-none", className)}
type="button"
>
Reset
<RefreshCcw className="size-3.5" />
</button>
);
};