feat: init default components and feat: implementating login page

This commit is contained in:
Orace.A 2025-03-24 23:53:55 +01:00
parent a2b76c9e69
commit 09680eac63
8 changed files with 233 additions and 0 deletions

12
src/app/(auth)/layout.tsx Normal file
View File

@ -0,0 +1,12 @@
import Header from "#/components/header";
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return(
<div className="flex flex-col min-h-screen">
<Header />
<div className="flex flex-1 justify-center items-center">
{children}
</div>
</div>
)
}

View File

@ -0,0 +1,27 @@
import Form from "#/components/form/form"
export default function LoginPage() {
return(
<div>
<Form
className="bg-white p-2 w-[28rem]"
fields={[
{
label: "Email",
name: "email",
type: "email",
placeholder: "Entrer votre email"
},
{
label: "Password",
name: "password",
type: "password",
placeholder: "Enter votre mot de passe"
}
]}
submit={undefined}
child={<button type="submit">Login</button>}
/>
</div>
)
}

View File

@ -1,5 +1,6 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
@ -24,3 +25,30 @@ body {
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
.input-form {
width: 100%;
padding: 12px;
border: 1px solid #d1d5dc;
border-radius: 9999px;
&:focus {
outline-color: none;
}
}
.input-label {
position: absolute;
left: 12px;
top: -0.45rem;
background-color: white;
padding-inline: 4px;
z-index: 1;
}
.btn-floating {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
}

View File

@ -0,0 +1,94 @@
"use client";
import { FloatingLabelInputProps } from '#/types';
import React, { useState } from 'react';
export default function FloatingLabelInput({
label,
placeholder,
type,
options,
button,
showPasswordToggle = false,
name,
defaultValue
}: FloatingLabelInputProps) {
const [showPassword, setShowPassword] = useState(false);
const renderInput = () => {
switch(type) {
case 'select':
return (
<div className="relative w-full">
<select
className="input-form focus:ring-2 focus:ring-blue-500"
required
name={name}
defaultValue={defaultValue}
>
{options?.map((option, index) => (
<option key={index} value={option}>{option}</option>
))}
</select>
{button && <div className="btn-floating">{button}</div>}
</div>
);
case 'password':
return (
<div className="relative w-full">
<input
name={name}
type={showPassword ? "text" : "password"}
placeholder={placeholder}
className="input-form focus:ring-2 focus:ring-blue-500 pr-10"
defaultValue={defaultValue}
required
/>
{/* {showPasswordToggle && (
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="btn-floating text-gray-500 hover:text-gray-700 focus:outline-none"
>
{showPassword ? (
<EyeClosedIcon size={20} />
) : (
<EyeOpenIcon size={20} />
)}
</button>
)} */}
</div>
);
default:
return (
<div className="relative w-full">
<input
type={type}
placeholder={placeholder}
className="input-form focus:ring-2 focus:ring-blue-500"
required
name={name}
defaultValue={defaultValue}
/>
{button && <div className="absolute right-0 top-1/2 transform -translate-y-1/2">{button}</div>}
</div>
);
}
};
return (
<div className="relative mb-9">
<label
htmlFor={name}
className="input-label text-gray-400 text-sm"
>
{label}
</label>
{renderInput()}
</div>
);
}

View File

@ -0,0 +1,31 @@
import FloatingLabelInput from "../floatingLabelInput"
import { FormProps } from "#/types"
export default function Form({
fields,
submit,
className,
child
} : FormProps) {
return (
<form className={className} onSubmit={submit}>
{
fields.map((item, index) => (
<FloatingLabelInput
key={index}
label={item.label}
name={item.name}
type={item.type}
button={item.button}
defaultValue={item.defaultValue}
options={item.options}
placeholder={item.placeholder}
showPasswordToggle={item.showPasswordToggle}
/>
))
}
{child}
</form>
)
}

17
src/components/header.tsx Normal file
View File

@ -0,0 +1,17 @@
import Image from "next/image";
export default function Header() {
return(
<div className="w-full bg-white shadow-md py-4">
<div className="container mx-auto text-center flex items-center justify-center">
<Image
src="/file.svg"
alt="Private Docs"
width={100}
height={100}
className="text-red-500 h-auto"
/>
</div>
</div>
)
}

View File

@ -0,0 +1,5 @@
export default function Table() {
return(
<></>
)
}

19
src/types/index.ts Normal file
View File

@ -0,0 +1,19 @@
import { FormEventHandler, ReactNode } from "react";
export interface FloatingLabelInputProps {
label: string;
placeholder?: string;
type: 'text' | 'password' | 'select' | 'email' | 'number';
options?: string[];
button?: React.ReactNode;
showPasswordToggle?: boolean;
name: string;
defaultValue?: string;
}
export interface FormProps {
fields: FloatingLabelInputProps[],
submit: FormEventHandler<HTMLFormElement> | undefined,
className: string,
child: ReactNode
}