mirror of
https://github.com/khairul169/home-lab.git
synced 2026-10-02 17:13:57 +07:00
feat: add authentication
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ComponentPropsWithClassName } from "@/types/components";
|
||||
import { View } from "react-native";
|
||||
import Text from "./Text";
|
||||
import { VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
const alertVariants = cva(
|
||||
"rounded-md bg-gray-100 border border-gray-300 px-3 py-2 w-full",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-gray-100 border-gray-300",
|
||||
success: "bg-green-100 border-green-300",
|
||||
error: "bg-red-100 border-red-300",
|
||||
warning: "bg-yellow-100 border-yellow-300",
|
||||
info: "bg-blue-100 border-blue-300",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const alertTextVariants = cva("text-sm", {
|
||||
variants: {
|
||||
variant: {
|
||||
default: "text-gray-700",
|
||||
success: "text-green-700",
|
||||
error: "text-red-700",
|
||||
warning: "text-yellow-700",
|
||||
info: "text-blue-700",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
});
|
||||
|
||||
type Props = Omit<ComponentPropsWithClassName<typeof View>, "children"> &
|
||||
VariantProps<typeof alertVariants> & {
|
||||
children?: string;
|
||||
textClassName?: string;
|
||||
error?: unknown;
|
||||
};
|
||||
|
||||
const Alert = ({
|
||||
className,
|
||||
textClassName,
|
||||
children,
|
||||
variant: variantName,
|
||||
error,
|
||||
}: Props) => {
|
||||
let variant = variantName;
|
||||
let message = children;
|
||||
|
||||
if (error) {
|
||||
variant = "error";
|
||||
message = (error as any)?.message || "An error occured!";
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={cn(alertVariants({ variant }), className)}>
|
||||
<Text className={[alertTextVariants({ variant }), textClassName]}>
|
||||
{message}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Alert;
|
||||
@@ -54,12 +54,13 @@ const buttonTextVariants = cva("text-center font-medium", {
|
||||
});
|
||||
|
||||
interface ButtonProps
|
||||
extends React.ComponentPropsWithoutRef<typeof Pressable>,
|
||||
extends Omit<React.ComponentPropsWithoutRef<typeof Pressable>, "children">,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
label?: string;
|
||||
labelClasses?: string;
|
||||
className?: string;
|
||||
icon?: React.ReactNode;
|
||||
children?: string;
|
||||
}
|
||||
function Button({
|
||||
label,
|
||||
@@ -68,6 +69,7 @@ function Button({
|
||||
variant,
|
||||
size,
|
||||
icon,
|
||||
children,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const textStyles = cn(
|
||||
@@ -86,7 +88,9 @@ function Button({
|
||||
>
|
||||
{icon ? <Slot.View style={textStyles}>{icon}</Slot.View> : null}
|
||||
|
||||
{label ? <Text style={textStyles}>{label}</Text> : null}
|
||||
{label || children ? (
|
||||
<Text style={textStyles}>{label || children}</Text>
|
||||
) : null}
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ScrollView, View } from "react-native";
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type ContainerProps = {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
scrollable?: boolean;
|
||||
};
|
||||
|
||||
const Container = ({
|
||||
className,
|
||||
children,
|
||||
scrollable = false,
|
||||
}: ContainerProps) => {
|
||||
const style = cn("mx-auto w-full max-w-xl", className);
|
||||
|
||||
if (scrollable) {
|
||||
return (
|
||||
<ScrollView
|
||||
contentContainerStyle={style}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
return <View style={style}>{children}</View>;
|
||||
};
|
||||
|
||||
export default Container;
|
||||
@@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
import { TextInput } from "react-native";
|
||||
import { ComponentPropsWithClassName } from "@/types/components";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Box from "./Box";
|
||||
import Text from "./Text";
|
||||
import { Controller, FieldValues, Path, UseFormReturn } from "react-hook-form";
|
||||
|
||||
type BaseInputProps = ComponentPropsWithClassName<typeof TextInput> & {
|
||||
label?: string;
|
||||
inputClassName?: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
type InputProps<T extends FieldValues> = BaseInputProps & {
|
||||
form?: UseFormReturn<T>;
|
||||
path?: Path<T>;
|
||||
};
|
||||
|
||||
const BaseInput = ({
|
||||
className,
|
||||
inputClassName,
|
||||
label,
|
||||
error,
|
||||
...props
|
||||
}: BaseInputProps) => {
|
||||
return (
|
||||
<Box className={className}>
|
||||
{label ? <Text className="text-sm mb-1">{label}</Text> : null}
|
||||
|
||||
<TextInput
|
||||
style={cn(
|
||||
"border border-gray-300 rounded-lg px-3 h-10 w-full",
|
||||
inputClassName
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
{error ? (
|
||||
<Text className="text-red-500 text-sm mt-1">{error}</Text>
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const Input = <T extends FieldValues>({
|
||||
form,
|
||||
path,
|
||||
...props
|
||||
}: InputProps<T>) => {
|
||||
if (form && path) {
|
||||
return (
|
||||
<Controller
|
||||
control={form.control}
|
||||
name={path}
|
||||
render={({ field: { ref, ...field }, fieldState }) => (
|
||||
<BaseInput {...props} {...field} error={fieldState.error?.message} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <BaseInput {...props} />;
|
||||
};
|
||||
|
||||
export default Input;
|
||||
Reference in New Issue
Block a user