refactor: restyle login page with UI components
Improves the login page's visual appearance by using Card and Input components.
This commit is contained in:
parent
a56b6829a2
commit
8ab50b2c5e
6 changed files with 142 additions and 45 deletions
|
@ -1,48 +1,28 @@
|
|||
import { Input } from '@/components/ui/input';
|
||||
|
||||
export default function LabeledInput({
|
||||
type,
|
||||
width,
|
||||
label,
|
||||
placeholder,
|
||||
value,
|
||||
}: {
|
||||
type: 'text' | 'email' | 'password';
|
||||
width?: number;
|
||||
label?: string;
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
}) {
|
||||
const randomId = Math.random().toString(36).substring(2, 15);
|
||||
|
||||
if (!label) {
|
||||
return (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<input
|
||||
className='border border-gray-300 rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-blue-500'
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
style={{
|
||||
width: width ? `${width}px` : '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<label htmlFor={randomId}>{label}</label>
|
||||
return (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<label htmlFor={randomId}>{label}</label>
|
||||
|
||||
<input
|
||||
className='border border-gray-300 rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-blue-500'
|
||||
id={randomId}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
style={{
|
||||
width: width ? `${width}px` : '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Input
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
id={randomId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
92
src/components/ui/card.tsx
Normal file
92
src/components/ui/card.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card'
|
||||
className={cn(
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-header'
|
||||
className={cn(
|
||||
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-title'
|
||||
className={cn('leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-description'
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-action'
|
||||
className={cn(
|
||||
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-content'
|
||||
className={cn('px-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-footer'
|
||||
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
};
|
21
src/components/ui/input.tsx
Normal file
21
src/components/ui/input.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot='input'
|
||||
className={cn(
|
||||
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Input };
|
|
@ -3,7 +3,7 @@ import { Button } from '@/components/ui/button';
|
|||
|
||||
export default function LoginForm() {
|
||||
return (
|
||||
<form className='flex flex-col gap-4 w-7/8'>
|
||||
<form className='flex flex-col gap-4 w-full'>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail'
|
||||
|
|
|
@ -11,7 +11,7 @@ export default function SSOLogin({
|
|||
}) {
|
||||
return (
|
||||
<form
|
||||
className='flex flex-col items-center gap-4 w-7/8'
|
||||
className='flex flex-col items-center gap-4 w-full'
|
||||
action={async () => {
|
||||
'use server';
|
||||
await signIn(provider);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue