feat: add hosts management

This commit is contained in:
2024-11-09 10:33:07 +00:00
parent 31c43836f4
commit d931235fb3
27 changed files with 742 additions and 94 deletions
+40
View File
@@ -0,0 +1,40 @@
import React, { ComponentPropsWithoutRef } from "react";
import { Label, Text, View, XStack } from "tamagui";
type FormFieldProps = ComponentPropsWithoutRef<typeof XStack> & {
label?: string;
htmlFor?: string;
};
const FormField = ({ label, htmlFor, ...props }: FormFieldProps) => {
return (
<XStack alignItems="flex-start" {...props}>
<Label htmlFor={htmlFor} w={120} $xs={{ w: 100 }}>
{label}
</Label>
<View w="auto" flex={1}>
{props.children}
</View>
</XStack>
);
};
type ErrorMessageProps = ComponentPropsWithoutRef<typeof Text> & {
error?: unknown | null;
};
export const ErrorMessage = ({ error, ...props }: ErrorMessageProps) => {
if (!error) {
return null;
}
const message = (error as any)?.message || "Something went wrong";
return (
<Text color="red" fontSize="$3" mt="$1" {...props}>
{message}
</Text>
);
};
export default FormField;
+27
View File
@@ -0,0 +1,27 @@
import { Controller, FieldValues } from "react-hook-form";
import { FormFieldBaseProps } from "./utility";
import { Input, View } from "tamagui";
import { ComponentPropsWithoutRef } from "react";
import { ErrorMessage } from "./form";
type InputFieldProps<T extends FieldValues> = FormFieldBaseProps<T> &
ComponentPropsWithoutRef<typeof Input>;
export const InputField = <T extends FieldValues>({
form,
name,
...props
}: InputFieldProps<T>) => (
<Controller
control={form.control}
name={name}
render={({ field, fieldState }) => (
<>
<Input {...field} {...props} />
<ErrorMessage error={fieldState.error} />
</>
)}
/>
);
export default Input;
+91
View File
@@ -0,0 +1,91 @@
import { Adapt, Button, Dialog, Sheet, Text, View } from "tamagui";
import React from "react";
import { createDisclosure } from "@/lib/utils";
import Icons from "./icons";
type ModalProps = {
disclosure: ReturnType<typeof createDisclosure<any>>;
title?: string;
description?: string;
children?: React.ReactNode;
width?: number | string;
};
const Modal = ({
disclosure,
children,
title,
description,
width = 512,
}: ModalProps) => {
const { open, onOpenChange } = disclosure.use();
return (
<Dialog open={open} onOpenChange={onOpenChange} modal>
<Adapt when="sm">
<Sheet
animation="quick"
zIndex={999}
modal
dismissOnSnapToBottom
disableDrag
>
<Sheet.Frame>
<Adapt.Contents />
</Sheet.Frame>
<Sheet.Overlay
animation="quicker"
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
zIndex={0}
/>
</Sheet>
</Adapt>
<Dialog.Portal zIndex={999}>
<Dialog.Overlay
key="overlay"
animation="quickest"
opacity={0.5}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
/>
<Dialog.Content
bordered
key="content"
elevate
animateOnly={["transform", "opacity"]}
animation={["quickest", { opacity: { overshootClamping: true } }]}
enterStyle={{ x: 0, opacity: 0, scale: 0.95 }}
exitStyle={{ x: 0, opacity: 0, scale: 0.98 }}
p="$1"
width="90%"
maxWidth={width}
height="90%"
maxHeight={600}
>
<View p="$4">
<Dialog.Title>{title}</Dialog.Title>
<Dialog.Description>{description}</Dialog.Description>
<Dialog.Close asChild>
<Button
position="absolute"
top="$2"
right="$2"
size="$3"
bg="$colorTransparent"
circular
icon={<Icons name="close" size={16} />}
/>
</Dialog.Close>
</View>
{children}
</Dialog.Content>
</Dialog.Portal>
</Dialog>
);
};
export default Modal;
+27
View File
@@ -1,5 +1,9 @@
import React, { forwardRef } from "react";
import { Controller, FieldValues } from "react-hook-form";
import { Select as BaseSelect } from "tamagui";
import { FormFieldBaseProps } from "./utility";
import { ErrorMessage } from "./form";
import Icons from "./icons";
export type SelectItem = {
label: string;
@@ -50,7 +54,10 @@ const Select = forwardRef<SelectRef, SelectProps>(
key={item.value}
value={item.value}
index={idx + 1}
justifyContent="flex-start"
gap="$2"
>
{value === item.value && <Icons name="check" size={16} />}
<BaseSelect.ItemText>{item.label}</BaseSelect.ItemText>
</BaseSelect.Item>
))}
@@ -62,4 +69,24 @@ const Select = forwardRef<SelectRef, SelectProps>(
}
);
type SelectFieldProps<T extends FieldValues> = FormFieldBaseProps<T> &
SelectProps;
export const SelectField = <T extends FieldValues>({
form,
name,
...props
}: SelectFieldProps<T>) => (
<Controller
control={form.control}
name={name}
render={({ field, fieldState }) => (
<>
<Select w="auto" f={1} {...field} {...props} />
<ErrorMessage error={fieldState.error} />
</>
)}
/>
);
export default Select;
+7
View File
@@ -0,0 +1,7 @@
import { UseZFormReturn } from "@/hooks/useZForm";
import { FieldPath, FieldValues } from "react-hook-form";
export type FormFieldBaseProps<T extends FieldValues> = {
form: UseZFormReturn<T>;
name: FieldPath<T>;
};