style: login page Okay
This commit is contained in:
parent
921007ae13
commit
8c12667693
@ -14,8 +14,11 @@
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"axios": "^1.8.4",
|
||||
"bootstrap": "^5.3.3",
|
||||
"clsx": "^2.1.1",
|
||||
"declarations": "link:@/lib/declarations",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"next": "15.2.3",
|
||||
"next-auth": "^4.24.11",
|
||||
"nextjs-toploader": "^3.8.15",
|
||||
@ -32,6 +35,7 @@
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/leaflet": "^1.9.17",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
@ -39,7 +43,6 @@
|
||||
"eslint-config-next": "15.2.3",
|
||||
"install": "^0.13.0",
|
||||
"npm": "^11.2.0",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
46
pnpm-lock.yaml
generated
46
pnpm-lock.yaml
generated
@ -23,12 +23,21 @@ importers:
|
||||
axios:
|
||||
specifier: ^1.8.4
|
||||
version: 1.8.4
|
||||
bootstrap:
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3(@popperjs/core@2.11.8)
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
declarations:
|
||||
specifier: link:@/lib/declarations
|
||||
version: link:@/lib/declarations
|
||||
jwt-decode:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
leaflet:
|
||||
specifier: ^1.9.4
|
||||
version: 1.9.4
|
||||
next:
|
||||
specifier: 15.2.3
|
||||
version: 15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.0)
|
||||
@ -72,6 +81,9 @@ importers:
|
||||
'@tailwindcss/postcss':
|
||||
specifier: ^4
|
||||
version: 4.0.15
|
||||
'@types/leaflet':
|
||||
specifier: ^1.9.17
|
||||
version: 1.9.17
|
||||
'@types/node':
|
||||
specifier: ^20
|
||||
version: 20.17.27
|
||||
@ -93,9 +105,6 @@ importers:
|
||||
npm:
|
||||
specifier: ^11.2.0
|
||||
version: 11.2.0
|
||||
tailwindcss:
|
||||
specifier: ^4
|
||||
version: 4.0.15
|
||||
typescript:
|
||||
specifier: ^5
|
||||
version: 5.8.2
|
||||
@ -1018,6 +1027,9 @@ packages:
|
||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@popperjs/core@2.11.8':
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
|
||||
'@radix-ui/colors@3.0.0':
|
||||
resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==}
|
||||
|
||||
@ -1875,12 +1887,18 @@ packages:
|
||||
'@types/estree@1.0.7':
|
||||
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
||||
|
||||
'@types/geojson@7946.0.16':
|
||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
'@types/json5@0.0.29':
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
|
||||
'@types/leaflet@1.9.17':
|
||||
resolution: {integrity: sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==}
|
||||
|
||||
'@types/node@20.17.27':
|
||||
resolution: {integrity: sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==}
|
||||
|
||||
@ -2100,6 +2118,11 @@ packages:
|
||||
boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
|
||||
bootstrap@5.3.3:
|
||||
resolution: {integrity: sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==}
|
||||
peerDependencies:
|
||||
'@popperjs/core': ^2.11.8
|
||||
|
||||
brace-expansion@1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
|
||||
@ -2845,6 +2868,9 @@ packages:
|
||||
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
leaflet@1.9.4:
|
||||
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
||||
|
||||
levn@0.4.1:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@ -4716,6 +4742,8 @@ snapshots:
|
||||
'@parcel/watcher-win32-x64': 2.5.1
|
||||
optional: true
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
'@radix-ui/colors@3.0.0': {}
|
||||
|
||||
'@radix-ui/number@1.1.0': {}
|
||||
@ -5614,10 +5642,16 @@ snapshots:
|
||||
|
||||
'@types/estree@1.0.7': {}
|
||||
|
||||
'@types/geojson@7946.0.16': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/json5@0.0.29': {}
|
||||
|
||||
'@types/leaflet@1.9.17':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/node@20.17.27':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
@ -5882,6 +5916,10 @@ snapshots:
|
||||
|
||||
boolbase@1.0.0: {}
|
||||
|
||||
bootstrap@5.3.3(@popperjs/core@2.11.8):
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.8
|
||||
|
||||
brace-expansion@1.1.11:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
@ -6765,6 +6803,8 @@ snapshots:
|
||||
dependencies:
|
||||
language-subtag-registry: 0.3.23
|
||||
|
||||
leaflet@1.9.4: {}
|
||||
|
||||
levn@0.4.1:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
@ -7,71 +7,77 @@ import { signIn } from "next-auth/react"
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
|
||||
export default function LoginPage() {
|
||||
const router = useRouter()
|
||||
const params = useSearchParams().get("redirect_to");
|
||||
const router = useRouter()
|
||||
const params = useSearchParams().get("redirect_to");
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationKey: ['login'],
|
||||
mutationFn: async (data: { email: string; password: string }) => {
|
||||
try {
|
||||
const result = await signIn("credentials", {
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
redirect: false,
|
||||
})
|
||||
|
||||
if (result?.error) {
|
||||
const errorMessage = result.error.includes("CredentialsSignin")
|
||||
? "Email ou mot de passe incorrect"
|
||||
: result.error;
|
||||
console.error(errorMessage)
|
||||
throw new Error(result.error)
|
||||
} else {
|
||||
if (params) {
|
||||
router.push(params);
|
||||
} else {
|
||||
router.push('/admin/home')
|
||||
}
|
||||
}
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error && error.message.includes("Network Error")) {
|
||||
console.error("Problème de connexion au serveur");
|
||||
}
|
||||
console.error("Autre = ", error);
|
||||
}
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationKey: ['login'],
|
||||
mutationFn: async (data: { email: string; password: string }) => {
|
||||
try {
|
||||
const result = await signIn("credentials", {
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
redirect: false,
|
||||
})
|
||||
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error.message)
|
||||
},
|
||||
})
|
||||
if (result?.error) {
|
||||
const errorMessage = result.error.includes("CredentialsSignin")
|
||||
? "Email ou mot de passe incorrect"
|
||||
: result.error;
|
||||
console.error(errorMessage)
|
||||
throw new Error(result.error)
|
||||
} else {
|
||||
if (params) {
|
||||
router.push(params);
|
||||
} else {
|
||||
router.push('/admin/home')
|
||||
}
|
||||
}
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error && error.message.includes("Network Error")) {
|
||||
console.error("Problème de connexion au serveur");
|
||||
}
|
||||
console.error("Autre = ", error);
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<Form
|
||||
title="Connexion"
|
||||
formClassName="bg-white p-10 shadow-2xl w-3/4 lg:w-lg"
|
||||
fields={[
|
||||
{
|
||||
label: "Email",
|
||||
name: "email",
|
||||
type: "email",
|
||||
placeholder: "Entrer votre email"
|
||||
},
|
||||
{
|
||||
label: "Password",
|
||||
name: "password",
|
||||
type: "password",
|
||||
placeholder: "Enter votre mot de passe",
|
||||
showPasswordToggle: true
|
||||
}
|
||||
]}
|
||||
submit={mutation.mutate}
|
||||
schema={loginSchema}
|
||||
child={<button disabled={mutation.isPending} type="submit" className={`${mutation.isPending ? "btn-auth-loading" : "btn-auth"} mt-4`}>{mutation.isPending ? "Chargement..." : "Connexion"}</button>}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error(error.message)
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column bg-bluegray h-100 align-items-center">
|
||||
<div className="login-main d-flex justify-content-center align-items-center position-relative">
|
||||
<Form
|
||||
title="Connexion"
|
||||
formClassName="bg-white p-10 shadow-2xl w-3/4 lg:w-lg"
|
||||
fields={[
|
||||
{
|
||||
label: "Email",
|
||||
name: "email",
|
||||
type: "email",
|
||||
placeholder: "Entrer votre email"
|
||||
},
|
||||
{
|
||||
label: "Password",
|
||||
name: "password",
|
||||
type: "password",
|
||||
placeholder: "Enter votre mot de passe",
|
||||
showPasswordToggle: true
|
||||
}
|
||||
]}
|
||||
submit={mutation.mutate}
|
||||
schema={loginSchema}
|
||||
child={<div className="d-flex justify-content-center w-100"> <button disabled={mutation.isPending} type="submit" className={`${mutation.isPending ? "btn-auth-loading" : "btn-auth"} cta modal-cta`}>{mutation.isPending ? "Chargement..." : "Connexion"}</button></div>}
|
||||
/>
|
||||
</div>
|
||||
<div className="version">
|
||||
<p className="fw-bold" >Version 0.0.1</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@font-face {
|
||||
font-family: 'Urbanist'; /* Nom que vous donnerez à votre police */
|
||||
src: url('../assets/fonts/Urbanist.ttf') format('truetype'); /* Chemin vers votre fichier */
|
||||
font-family: 'Urbanist';
|
||||
src: url('../assets/fonts/Urbanist.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@ -29,15 +27,18 @@
|
||||
--background: #ffffff;
|
||||
--cinder: #999;
|
||||
--bluegray: --primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Urbanist, sans-serif;
|
||||
font-family: Urbanist, sans-serif !important;
|
||||
}
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.input-form {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
@ -54,14 +55,22 @@ body {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: -0.45rem;
|
||||
background-color: white;
|
||||
padding-inline: 4px;
|
||||
padding: 1px 5px;
|
||||
line-height: normal;
|
||||
color: var(--secondary);
|
||||
background-color: var(--background);
|
||||
border-radius: 400px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.form-error {
|
||||
font-size: 14px;
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.btn-floating-right {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
@ -102,7 +111,12 @@ body {
|
||||
transition: width 0.2s, height 0.2s;
|
||||
}
|
||||
|
||||
.cta{
|
||||
button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cta {
|
||||
padding: 10px 24px;
|
||||
width: max-content;
|
||||
height: max-content;
|
||||
@ -116,35 +130,38 @@ body {
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.cta.modal-cta{
|
||||
.cta.modal-cta {
|
||||
padding: 9px;
|
||||
margin-top: 32px;
|
||||
width: 240px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.cta.cancel{
|
||||
.cta.cancel {
|
||||
color: var(--secondary);
|
||||
border: 1px solid var(--gray);
|
||||
background-color: var(--gray);
|
||||
}
|
||||
|
||||
.cta.info{
|
||||
.cta.info {
|
||||
color: var(--primary);
|
||||
background-color: var(--background);
|
||||
}
|
||||
|
||||
.cta.danger{
|
||||
.cta.danger {
|
||||
border: 1px solid var(--danger);
|
||||
background-color: var(--danger);
|
||||
}
|
||||
|
||||
|
||||
.bg-bluegray{
|
||||
.bg-bluegray {
|
||||
background-color: var(--bluegray);
|
||||
}
|
||||
.bg-gray{
|
||||
|
||||
.bg-gray {
|
||||
background-color: var(--gray);
|
||||
}
|
||||
hr{
|
||||
|
||||
hr {
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
@ -155,10 +172,10 @@ input[type="checkbox"] {
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid var(--secondary) !important;
|
||||
border-radius: 6px !important;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--secondary) !important;
|
||||
border-radius: 6px !important;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -170,7 +187,7 @@ input[type="checkbox"]:checked {
|
||||
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
content: "✔";
|
||||
content: "✔";
|
||||
font-size: 18px;
|
||||
color: white;
|
||||
position: absolute;
|
||||
@ -179,7 +196,7 @@ input[type="checkbox"]:checked::before {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.modal-input{
|
||||
.modal-input {
|
||||
width: 490px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@ -189,7 +206,7 @@ input[type="checkbox"]:checked::before {
|
||||
/* Scroll Bar */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 7px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
@ -224,4 +241,95 @@ input[type="checkbox"]:checked::before {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Login Header */
|
||||
.login-header {
|
||||
padding: 20px 0;
|
||||
box-shadow: 0 0 24px #0000001A;
|
||||
}
|
||||
|
||||
.word {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.dot {
|
||||
/* font-size: 14px; */
|
||||
color: blue;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 22px;
|
||||
animation: bounce 1s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
}
|
||||
|
||||
.login-main {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
.form-container {
|
||||
padding: 48px 64px;
|
||||
margin: auto;
|
||||
width: max-content;
|
||||
max-width: 624px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 24px #0000001A;
|
||||
}
|
||||
|
||||
.form-fields {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
|
||||
.login-main{
|
||||
width: 100%;
|
||||
}
|
||||
.form-container {
|
||||
padding: 24px 32px;
|
||||
margin: 0 4%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.form-fields {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.modal-input {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
.form-title{
|
||||
margin-bottom: 2px!important;
|
||||
}
|
||||
|
||||
.cta.modal-cta{
|
||||
margin: 0!important;
|
||||
}
|
||||
.input-form{
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,8 @@ export default function RootLayout({
|
||||
<head>
|
||||
<link rel="icon" href="/favicon.svg" />
|
||||
<link rel="favicon.svg" href="/favicon.svg" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossOrigin="anonymous"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossOrigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<AuthProvider>
|
||||
|
||||
16
src/assets/icons/final-index.tsx
Normal file
16
src/assets/icons/final-index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
|
||||
//import { GeneralHtmlAttr } from "@/lib/declarations";
|
||||
//export type GeneralHtmlAttr = React.HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
import L from "leaflet";
|
||||
|
||||
export const HomeIcon = (props) => {
|
||||
return (
|
||||
<div {...props}>
|
||||
<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.25 11L10.205 2.045C10.3094 1.94056 10.4333 1.85772 10.5697 1.80119C10.7061 1.74467 10.8523 1.71558 11 1.71558C11.1477 1.71558 11.2939 1.74467 11.4303 1.80119C11.5667 1.85772 11.6906 1.94056 11.795 2.045L20.75 11M3.5 8.75V18.875C3.5 19.496 4.004 20 4.625 20H8.75V15.125C8.75 14.504 9.254 14 9.875 14H12.125C12.746 14 13.25 14.504 13.25 15.125V20H17.375C17.996 20 18.5 19.496 18.5 18.875V8.75M7.25 20H15.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
src/assets/icons/full-logo.svg
Normal file
9
src/assets/icons/full-logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 462 KiB |
@ -30,7 +30,7 @@ export default function FloatingLabelInput({
|
||||
switch(type) {
|
||||
case 'select':
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="position-relative w-full">
|
||||
<select
|
||||
className="input-form modal-input focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
name={name}
|
||||
@ -46,7 +46,7 @@ export default function FloatingLabelInput({
|
||||
|
||||
case 'password':
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="position-relative w-full">
|
||||
<input
|
||||
name={name}
|
||||
type={showPassword ? "text" : "password"}
|
||||
@ -66,14 +66,14 @@ export default function FloatingLabelInput({
|
||||
src={icons.eyeSlashIcon}
|
||||
width={20}
|
||||
height={20}
|
||||
alt=''
|
||||
alt='show password'
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={icons.eyeIcon}
|
||||
width={20}
|
||||
height={20}
|
||||
alt=''
|
||||
alt='hide password'
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
@ -84,7 +84,7 @@ export default function FloatingLabelInput({
|
||||
|
||||
case 'search':
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="position-relative w-full">
|
||||
<div className='btn-floating-left'>
|
||||
<Image alt='' src={icons.searchIcon} />
|
||||
</div>
|
||||
@ -102,7 +102,7 @@ export default function FloatingLabelInput({
|
||||
|
||||
default:
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="position-relative w-full">
|
||||
<input
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
@ -118,7 +118,7 @@ export default function FloatingLabelInput({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="position-relative">
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="input-label text-gray-400 text-sm"
|
||||
|
||||
@ -5,72 +5,71 @@ import { FormProps } from "#/types"
|
||||
import { FormEvent, useState } from "react"
|
||||
|
||||
export default function Form({
|
||||
fields,
|
||||
submit,
|
||||
className,
|
||||
child,
|
||||
title,
|
||||
schema,
|
||||
formClassName
|
||||
} : FormProps) {
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
fields,
|
||||
submit,
|
||||
className,
|
||||
child,
|
||||
title,
|
||||
schema,
|
||||
formClassName
|
||||
}: FormProps) {
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
|
||||
const formData = new FormData(e.currentTarget)
|
||||
const data = Object.fromEntries(formData)
|
||||
const formData = new FormData(e.currentTarget)
|
||||
const data = Object.fromEntries(formData)
|
||||
|
||||
console.log("FORM DATA = ", data)
|
||||
const result = schema.safeParse(data);
|
||||
console.log("ZOD = ", result.error?.format())
|
||||
console.log("FORM DATA = ", data)
|
||||
const result = schema.safeParse(data);
|
||||
console.log("ZOD = ", result.error?.format())
|
||||
|
||||
if(!result.success) {
|
||||
const formatedErrors = result.error.format() as Record<string, { _errors?: string[] }>;
|
||||
if (!result.success) {
|
||||
const formatedErrors = result.error.format() as Record<string, { _errors?: string[] }>;
|
||||
|
||||
const newErrors: Record<string, string> = {};
|
||||
Object.keys(formatedErrors).forEach((field) => {
|
||||
if (field !== "_errors" && formatedErrors[field]._errors?.length) {
|
||||
newErrors[field] = formatedErrors[field]._errors[0];
|
||||
}
|
||||
});
|
||||
|
||||
setErrors(newErrors)
|
||||
} else {
|
||||
setErrors({})
|
||||
submit(result.data)
|
||||
const newErrors: Record<string, string> = {};
|
||||
Object.keys(formatedErrors).forEach((field) => {
|
||||
if (field !== "_errors" && formatedErrors[field]._errors?.length) {
|
||||
newErrors[field] = formatedErrors[field]._errors[0];
|
||||
}
|
||||
});
|
||||
|
||||
setErrors(newErrors)
|
||||
} else {
|
||||
setErrors({})
|
||||
submit(result.data)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className={formClassName}>
|
||||
<div className="flex justify-center text-black">
|
||||
<p className="text-3xl font-bold">{title}</p>
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className={(formClassName) + " form-container"}>
|
||||
<div className="form-title d-flex justify-content-center text-black mb-3 ">
|
||||
<p className="fs-3 fw-bold">{title}</p>
|
||||
</div>
|
||||
|
||||
<div className={`d-flex flex-column form-fields ${className}`}>
|
||||
|
||||
{
|
||||
fields.map((item, index) => (
|
||||
<div key={index}>
|
||||
<FloatingLabelInput
|
||||
label={item.label}
|
||||
name={item.name}
|
||||
type={item.type}
|
||||
button={item.button}
|
||||
defaultValue={item.defaultValue}
|
||||
options={item.options}
|
||||
placeholder={item.placeholder}
|
||||
showPasswordToggle={item.showPasswordToggle}
|
||||
/>
|
||||
|
||||
<span className="form-error">{errors[item.name]}</span>
|
||||
</div>
|
||||
|
||||
<div className={`space-y-8 my-2 ${className}`}>
|
||||
|
||||
{
|
||||
fields.map((item, index) => (
|
||||
<div key={index}>
|
||||
<FloatingLabelInput
|
||||
label={item.label}
|
||||
name={item.name}
|
||||
type={item.type}
|
||||
button={item.button}
|
||||
defaultValue={item.defaultValue}
|
||||
options={item.options}
|
||||
placeholder={item.placeholder}
|
||||
showPasswordToggle={item.showPasswordToggle}
|
||||
/>
|
||||
|
||||
<span className="text-red-500 text-xs mt-1">{errors[item.name]}</span>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{child}
|
||||
</form>
|
||||
|
||||
)
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{child}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@ -2,16 +2,18 @@ import { icons } from "#/assets/icons";
|
||||
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 gap-2">
|
||||
<Image
|
||||
src={icons.logo}
|
||||
alt="Private Docs"
|
||||
className="text-red-500 h-auto"
|
||||
/>
|
||||
<p className="text-2xl font-bold text-black">Private Docs</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="d-flex justify-content-center login-header ">
|
||||
<div className="d-flex justify-content-center align-items-center gap-2">
|
||||
<Image
|
||||
src={icons.logo}
|
||||
alt="Private Docs"
|
||||
className="text-red-500 h-auto"
|
||||
height={30}
|
||||
/>
|
||||
{/* <p className="text-2xl font-bold text-black">Private Docs</p> */}
|
||||
<p className="r-p-m-0 word">Pr<span className="dot">•</span>ıvate Docs</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
9
src/lib/bootstrapLoader.tsx
Normal file
9
src/lib/bootstrapLoader.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
@ -23,6 +23,6 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["svgr.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": ["svgr.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/assets/icons/final-index.js"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user