Skip to content

Commit

Permalink
Merge pull request #92 from TheByteFlow/frontend
Browse files Browse the repository at this point in the history
Add sign-in and registration validation
  • Loading branch information
muhammedsirajudeen authored Feb 25, 2025
2 parents cd007e9 + 12bf8b8 commit 99c4d6d
Show file tree
Hide file tree
Showing 20 changed files with 799 additions and 111 deletions.
2 changes: 1 addition & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/Logo Light.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Inker</title>
</head>
Expand Down
8 changes: 7 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,30 @@
"preview": "vite preview"
},
"dependencies": {
"@hookform/resolvers": "^4.1.1",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/vite": "^4.0.6",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^8.5.2",
"lucide-react": "^0.475.0",
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"react-router-dom": "^7.1.5",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.1",
"tailwindcss": "^4.0.6",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
Expand Down
15 changes: 15 additions & 0 deletions frontend/public/Logo Light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions frontend/src/api/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import axios from 'axios'

export const axiosInstance = axios.create({
baseURL: 'http://localhost:3000',
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
});
39 changes: 39 additions & 0 deletions frontend/src/components/password-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react'
import { Eye, EyeOff } from 'lucide-react';
import { cn } from '@/lib/utils'
import { Button } from './ui/button'

type PasswordInputProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'type'
>

const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
({ className, disabled, ...props }, ref) => {
const [showPassword, setShowPassword] = React.useState(false)
return (
<div className={cn('relative rounded-md', className)}>
<input
type={showPassword ? 'text' : 'password'}
className='flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50'
ref={ref}
disabled={disabled}
{...props}
/>
<Button
type='button'
size='icon'
variant='ghost'
disabled={disabled}
className='absolute right-1 top-1/2 h-6 w-6 -translate-y-1/2 rounded-md text-muted-foreground'
onClick={() => setShowPassword((prev) => !prev)}
>
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
</Button>
</div>
)
}
)
PasswordInput.displayName = 'PasswordInput'

export { PasswordInput }
175 changes: 175 additions & 0 deletions frontend/src/components/ui/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import * as React from 'react'
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from 'react-hook-form'
import * as LabelPrimitive from '@radix-ui/react-label'
import { Slot } from '@radix-ui/react-slot'
import { cn } from '@/lib/utils'
import { Label } from '@/components/ui/label'

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, formState } = useFormContext()

const fieldState = getFieldState(fieldContext.name, formState)

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>')
}

const { id } = itemContext

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}

type FormItemContextValue = {
id: string
}

const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)

const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()

return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn('space-y-2', className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = 'FormItem'

const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()

return (
<Label
ref={ref}
className={cn(error && 'text-destructive', className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = 'FormLabel'

const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()

return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = 'FormControl'

const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()

return (
<p
ref={ref}
id={formDescriptionId}
className={cn('text-[0.8rem] text-muted-foreground', className)}
{...props}
/>
)
})
FormDescription.displayName = 'FormDescription'

const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children

if (!body) {
return null
}

return (
<p
ref={ref}
id={formMessageId}
className={cn('text-[0.8rem] font-medium text-destructive', className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = 'FormMessage'

export {
useFormField,

Check warning on line 167 in frontend/src/components/ui/form.tsx

View workflow job for this annotation

GitHub Actions / lint

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}
27 changes: 27 additions & 0 deletions frontend/src/components/ui/sonner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useTheme } from "next-themes"
import { Toaster as Sonner, ToasterProps } from "sonner"

const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()

return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground font-medium",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground font-medium",
},
}}
{...props}
/>
)
}

export { Toaster }
13 changes: 0 additions & 13 deletions frontend/src/components/user/auth/AuthLeftPanel.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/src/components/user/auth/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const Login: FC<LoginProps> = ({ signin, setSignin }) => {
</span>
</div>
</div>

{/* @muhammedsirajudeen: Replace this svg with icon from lucide-react */}
<div className="flex gap-2 ">
<Button variant="outline" className="w-full">
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
Expand Down
Loading

0 comments on commit 99c4d6d

Please sign in to comment.