Compare commits
6 Commits
main
...
bootstrap-
| Author | SHA1 | Date | |
|---|---|---|---|
| ee31eb5511 | |||
| b926019ab6 | |||
| d3a8fb56ba | |||
| ea04c954b0 | |||
| 8c12667693 | |||
| 921007ae13 |
@ -14,8 +14,11 @@
|
|||||||
"@tanstack/react-query": "^5.69.0",
|
"@tanstack/react-query": "^5.69.0",
|
||||||
"@tanstack/react-table": "^8.21.2",
|
"@tanstack/react-table": "^8.21.2",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.8.4",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"declarations": "link:@/lib/declarations",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"next": "15.2.3",
|
"next": "15.2.3",
|
||||||
"next-auth": "^4.24.11",
|
"next-auth": "^4.24.11",
|
||||||
"nextjs-toploader": "^3.8.15",
|
"nextjs-toploader": "^3.8.15",
|
||||||
@ -32,6 +35,7 @@
|
|||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@types/leaflet": "^1.9.17",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
@ -39,7 +43,6 @@
|
|||||||
"eslint-config-next": "15.2.3",
|
"eslint-config-next": "15.2.3",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"npm": "^11.2.0",
|
"npm": "^11.2.0",
|
||||||
"tailwindcss": "^4",
|
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
46
pnpm-lock.yaml
generated
46
pnpm-lock.yaml
generated
@ -23,12 +23,21 @@ importers:
|
|||||||
axios:
|
axios:
|
||||||
specifier: ^1.8.4
|
specifier: ^1.8.4
|
||||||
version: 1.8.4
|
version: 1.8.4
|
||||||
|
bootstrap:
|
||||||
|
specifier: ^5.3.3
|
||||||
|
version: 5.3.3(@popperjs/core@2.11.8)
|
||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
declarations:
|
||||||
|
specifier: link:@/lib/declarations
|
||||||
|
version: link:@/lib/declarations
|
||||||
jwt-decode:
|
jwt-decode:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
|
leaflet:
|
||||||
|
specifier: ^1.9.4
|
||||||
|
version: 1.9.4
|
||||||
next:
|
next:
|
||||||
specifier: 15.2.3
|
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)
|
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':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4
|
specifier: ^4
|
||||||
version: 4.0.15
|
version: 4.0.15
|
||||||
|
'@types/leaflet':
|
||||||
|
specifier: ^1.9.17
|
||||||
|
version: 1.9.17
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20
|
specifier: ^20
|
||||||
version: 20.17.27
|
version: 20.17.27
|
||||||
@ -93,9 +105,6 @@ importers:
|
|||||||
npm:
|
npm:
|
||||||
specifier: ^11.2.0
|
specifier: ^11.2.0
|
||||||
version: 11.2.0
|
version: 11.2.0
|
||||||
tailwindcss:
|
|
||||||
specifier: ^4
|
|
||||||
version: 4.0.15
|
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5
|
specifier: ^5
|
||||||
version: 5.8.2
|
version: 5.8.2
|
||||||
@ -1018,6 +1027,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
|
'@popperjs/core@2.11.8':
|
||||||
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
|
|
||||||
'@radix-ui/colors@3.0.0':
|
'@radix-ui/colors@3.0.0':
|
||||||
resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==}
|
resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==}
|
||||||
|
|
||||||
@ -1875,12 +1887,18 @@ packages:
|
|||||||
'@types/estree@1.0.7':
|
'@types/estree@1.0.7':
|
||||||
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
||||||
|
|
||||||
|
'@types/geojson@7946.0.16':
|
||||||
|
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||||
|
|
||||||
'@types/json-schema@7.0.15':
|
'@types/json-schema@7.0.15':
|
||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
|
||||||
'@types/json5@0.0.29':
|
'@types/json5@0.0.29':
|
||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
|
|
||||||
|
'@types/leaflet@1.9.17':
|
||||||
|
resolution: {integrity: sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==}
|
||||||
|
|
||||||
'@types/node@20.17.27':
|
'@types/node@20.17.27':
|
||||||
resolution: {integrity: sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==}
|
resolution: {integrity: sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==}
|
||||||
|
|
||||||
@ -2100,6 +2118,11 @@ packages:
|
|||||||
boolbase@1.0.0:
|
boolbase@1.0.0:
|
||||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
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:
|
brace-expansion@1.1.11:
|
||||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
|
|
||||||
@ -2845,6 +2868,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
|
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
|
||||||
|
leaflet@1.9.4:
|
||||||
|
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@ -4716,6 +4742,8 @@ snapshots:
|
|||||||
'@parcel/watcher-win32-x64': 2.5.1
|
'@parcel/watcher-win32-x64': 2.5.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@popperjs/core@2.11.8': {}
|
||||||
|
|
||||||
'@radix-ui/colors@3.0.0': {}
|
'@radix-ui/colors@3.0.0': {}
|
||||||
|
|
||||||
'@radix-ui/number@1.1.0': {}
|
'@radix-ui/number@1.1.0': {}
|
||||||
@ -5614,10 +5642,16 @@ snapshots:
|
|||||||
|
|
||||||
'@types/estree@1.0.7': {}
|
'@types/estree@1.0.7': {}
|
||||||
|
|
||||||
|
'@types/geojson@7946.0.16': {}
|
||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
'@types/json5@0.0.29': {}
|
'@types/json5@0.0.29': {}
|
||||||
|
|
||||||
|
'@types/leaflet@1.9.17':
|
||||||
|
dependencies:
|
||||||
|
'@types/geojson': 7946.0.16
|
||||||
|
|
||||||
'@types/node@20.17.27':
|
'@types/node@20.17.27':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
undici-types: 6.19.8
|
||||||
@ -5882,6 +5916,10 @@ snapshots:
|
|||||||
|
|
||||||
boolbase@1.0.0: {}
|
boolbase@1.0.0: {}
|
||||||
|
|
||||||
|
bootstrap@5.3.3(@popperjs/core@2.11.8):
|
||||||
|
dependencies:
|
||||||
|
'@popperjs/core': 2.11.8
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
@ -6765,6 +6803,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
language-subtag-registry: 0.3.23
|
language-subtag-registry: 0.3.23
|
||||||
|
|
||||||
|
leaflet@1.9.4: {}
|
||||||
|
|
||||||
levn@0.4.1:
|
levn@0.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import Header from "#/components/header";
|
import Header from "#/components/header";
|
||||||
|
|
||||||
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||||
return(
|
return (
|
||||||
<div className="flex flex-col min-h-screen">
|
<div className="flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="flex flex-1 justify-center items-center bg-blue-100">
|
<div className="flex flex-1 justify-center items-center bg-blue-100">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -7,71 +7,77 @@ import { signIn } from "next-auth/react"
|
|||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const params = useSearchParams().get("redirect_to");
|
const params = useSearchParams().get("redirect_to");
|
||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationKey: ['login'],
|
mutationKey: ['login'],
|
||||||
mutationFn: async (data: { email: string; password: string }) => {
|
mutationFn: async (data: { email: string; password: string }) => {
|
||||||
try {
|
try {
|
||||||
const result = await signIn("credentials", {
|
const result = await signIn("credentials", {
|
||||||
email: data.email,
|
email: data.email,
|
||||||
password: data.password,
|
password: data.password,
|
||||||
redirect: false,
|
redirect: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result?.error) {
|
if (result?.error) {
|
||||||
const errorMessage = result.error.includes("CredentialsSignin")
|
const errorMessage = result.error.includes("CredentialsSignin")
|
||||||
? "Email ou mot de passe incorrect"
|
? "Email ou mot de passe incorrect"
|
||||||
: result.error;
|
: result.error;
|
||||||
console.error(errorMessage)
|
console.error(errorMessage)
|
||||||
throw new Error(result.error)
|
throw new Error(result.error)
|
||||||
} else {
|
} else {
|
||||||
if (params) {
|
if (params) {
|
||||||
router.push(params);
|
router.push(params);
|
||||||
} else {
|
} else {
|
||||||
router.push('/admin/home')
|
router.push('/admin/home')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof Error && error.message.includes("Network Error")) {
|
if (error instanceof Error && error.message.includes("Network Error")) {
|
||||||
console.error("Problème de connexion au serveur");
|
console.error("Problème de connexion au serveur");
|
||||||
}
|
}
|
||||||
console.error("Autre = ", error);
|
console.error("Autre = ", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
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>
|
||||||
onError: (error: Error) => {
|
)
|
||||||
console.error(error.message)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@ -12,34 +12,34 @@ import Image from "next/image"
|
|||||||
import { Modal } from "#/components/modal"
|
import { Modal } from "#/components/modal"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|
||||||
export default function HomePage () {
|
export default function HomePage() {
|
||||||
|
|
||||||
const {data: session, status} = useSession()
|
const { data: session, status } = useSession()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
|
||||||
console.log("Session = ", session)
|
console.log("Session = ", session)
|
||||||
|
|
||||||
const { data: companies, refetch, isLoading} = useQuery({
|
const { data: companies, refetch, isLoading } = useQuery({
|
||||||
enabled: status === 'authenticated',
|
enabled: status === 'authenticated',
|
||||||
queryKey: ["companies"],
|
queryKey: ["companies"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
'https://private-docs-api.intside.co/companies', {
|
'https://private-docs-api.intside.co/companies', {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${session?.user.access_token}`
|
'Authorization': `Bearer ${session?.user.access_token}`
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(response.data) {
|
|
||||||
return response.data.data as Company[]
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.data) {
|
||||||
|
return response.data.data as Company[]
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -48,13 +48,13 @@ export default function HomePage () {
|
|||||||
try {
|
try {
|
||||||
const response = await axios.delete(
|
const response = await axios.delete(
|
||||||
`https://private-docs-api.intside.co/companies/${id}/`, {
|
`https://private-docs-api.intside.co/companies/${id}/`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${session?.user.access_token}`
|
'Authorization': `Bearer ${session?.user.access_token}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(response.status === 200 || response.status === 201) {
|
if (response.status === 200 || response.status === 201) {
|
||||||
console.log('Suppresion réussie !')
|
console.log('Suppresion réussie !')
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}
|
}
|
||||||
@ -68,140 +68,139 @@ export default function HomePage () {
|
|||||||
refetch()
|
refetch()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const columns: ColumnDef<Company>[] = [
|
const columns: ColumnDef<Company>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
header: "Organisations",
|
header: "Organisations",
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "total_users",
|
|
||||||
header: "Utilisateurs",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Administrateurs",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const value = String(row.original.owner.first_name) + " " + String(row.original.owner.last_name)
|
|
||||||
const initials = String(row.original.owner.first_name[0]) + String(row.original.owner.last_name[0])
|
|
||||||
return(
|
|
||||||
<div className="flex space-x-2 items-center">
|
|
||||||
<div className="flex items-center justify-center bg-[#DCDCFE] text-[#246BFD] w-10 h-10 rounded-full">
|
|
||||||
{initials}
|
|
||||||
</div>
|
|
||||||
<p>{value}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "owner.email",
|
|
||||||
header: "Adresse e-mail"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "status",
|
|
||||||
header: "Statut",
|
|
||||||
cell: ({ cell }) => {
|
|
||||||
const status = String(cell.getValue())
|
|
||||||
return (
|
|
||||||
<p
|
|
||||||
className={`rounded-full px-2 py-1 font-medium text-sm w-20 h-6 text-center
|
|
||||||
${
|
|
||||||
status === "active" ? "bg-[#ECF9E8] text-[#49C91E]" :
|
|
||||||
status === "inactive" ? "bg-[#E7EBF3] text-[#9FA8BC]" :
|
|
||||||
status === "pending" ? "bg-[#EAF7FC] text-[#30B2EA]" :
|
|
||||||
status === "blocked" ? "bg-[#FDEBE8] text-[#F33F19]" :
|
|
||||||
""
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
status === "active" ? "Actif" :
|
|
||||||
status === "inactive" ? "Inactif" :
|
|
||||||
status === "pending" ? "En attente" :
|
|
||||||
status === "blocked" ? "Bloquée" :
|
|
||||||
""
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "delete",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const id = String(row.original.id);
|
|
||||||
return (
|
|
||||||
<div className="relative p-2 cursor-pointer">
|
|
||||||
<Modal
|
|
||||||
open={selectedId === id}
|
|
||||||
onOpenChange={(isOpen) => {
|
|
||||||
if (!isOpen) {
|
|
||||||
setSelectedId(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
trigger={
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedId(id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
alt=""
|
|
||||||
src={icons.trash}
|
|
||||||
className="cursor-pointer responsive-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
title={
|
|
||||||
<p className="font-bold text-3xl">
|
|
||||||
Supprimer cette organisation
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
content={
|
|
||||||
<div>
|
|
||||||
<p className="text-center">
|
|
||||||
Voulez-vous vraiment supprimer l'organisation <span className="font-bold">{row.original.name}</span> ?
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex justify-between w-full pt-6 r-gap-24">
|
|
||||||
<button
|
|
||||||
className="cta modal-cta cancel"
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedId(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Annuler
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="cta modal-cta danger"
|
|
||||||
onClick={() => {
|
|
||||||
mutate(id);
|
|
||||||
setSelectedId(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Supprimer
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
]
|
accessorKey: "total_users",
|
||||||
|
header: "Utilisateurs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Administrateurs",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const value = String(row.original.owner.first_name) + " " + String(row.original.owner.last_name)
|
||||||
|
const initials = String(row.original.owner.first_name[0]) + String(row.original.owner.last_name[0])
|
||||||
|
return (
|
||||||
|
<div className="r-flex align-items-center r-gap-16">
|
||||||
|
<div className="circled-badge">
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
<p className="r-p-m-0" >{value}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "owner.email",
|
||||||
|
header: "Adresse e-mail"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Statut",
|
||||||
|
cell: ({ cell }) => {
|
||||||
|
const status = String(cell.getValue())
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
className={`custom-badge r-p-m-0 r-flex-center rounded-5
|
||||||
|
${status === "active" ? "badge-active" :
|
||||||
|
status === "inactive" ? "badge-inactive" :
|
||||||
|
status === "pending" ? "badge-pending" :
|
||||||
|
status === "blocked" ? "badge-blocked" :
|
||||||
|
""
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
status === "active" ? "Actif" :
|
||||||
|
status === "inactive" ? "Inactif" :
|
||||||
|
status === "pending" ? "En attente" :
|
||||||
|
status === "blocked" ? "Bloquée" :
|
||||||
|
""
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "delete",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const id = String(row.original.id);
|
||||||
|
return (
|
||||||
|
<div className="relative p-2 cursor-pointer">
|
||||||
|
<Modal
|
||||||
|
open={selectedId === id}
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setSelectedId(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
alt=""
|
||||||
|
src={icons.trash}
|
||||||
|
className="cursor-pointer responsive-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title={
|
||||||
|
<p className="font-bold text-3xl">
|
||||||
|
Supprimer cette organisation
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<p className="text-center">
|
||||||
|
Voulez-vous vraiment supprimer l'organisation <span className="font-bold">{row.original.name}</span> ?
|
||||||
|
</p>
|
||||||
|
|
||||||
return(
|
<div className="r-flex-between w-full pt-6 r-gap-24">
|
||||||
<div className="space-y-10">
|
<button
|
||||||
|
className="cta modal-cta cancel"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="cta modal-cta danger"
|
||||||
|
onClick={() => {
|
||||||
|
mutate(id);
|
||||||
|
setSelectedId(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="home home-table r-p-m-0">
|
||||||
<Statistics />
|
<Statistics />
|
||||||
|
|
||||||
<p className="font-bold text-xl">Dernières organisations actives</p>
|
<h3 className="fw-bold fs-5 pt-5 pb-4">Dernières organisations actives</h3>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
isDataLoading={isLoading}
|
isDataLoading={isLoading}
|
||||||
data={companies || []}
|
data={companies || []}
|
||||||
pageSize={5}
|
pageSize={5}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -24,35 +24,12 @@ export default function Dashboard({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
<div className="r-flex-between">
|
<div className="r-flex-between">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<main className="flex-grow-1 min-w-0 pt-md-1 pt-sm-5">
|
<div className="flex-grow-1">
|
||||||
<Header/>
|
<Header/>
|
||||||
<div className="main px-[44px] py-[64px] ">
|
<div className="main px-[44px] py-[64px] ">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
</div >
|
</div >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
<div className="sidebar r-m-0 d-flex flex-column pt-[16px] max-w-[90px] h-[100vh] relative ">
|
|
||||||
<div className="logo r-flex-center pt-[13px] px-[20px] ">
|
|
||||||
<icons.Logo aria-label="Logo" className="scale-95" />
|
|
||||||
</div>
|
|
||||||
<div className="nav-menu r-column-center h-max pt-[160px] r-gap-40 ">
|
|
||||||
<Link href="#" className="nav-item r-flex-center ">
|
|
||||||
<icons.HomeIcon aria-label="Home" className="nav-home scale-100" />
|
|
||||||
</Link>
|
|
||||||
<Link href="#" className="nav-item border-none r-flex-center ">
|
|
||||||
<icons.CompaniesIcon aria-label="Companies" className="scale-100 " width={24} height={24} />
|
|
||||||
</Link>
|
|
||||||
<Link href="#" className="nav-item r-flex-center ">
|
|
||||||
<icons.UserGroup aria-label="Admins" className="scale-100" width={24} height={24} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="logout absolute bottom-[60px] left-[22px]">
|
|
||||||
<icons.Logout aria-label="Logout" />
|
|
||||||
</div>
|
|
||||||
*/
|
|
||||||
@ -170,12 +170,12 @@ export default function Organizations() {
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const value = String(row.original.owner.first_name) + " " + String(row.original.owner.last_name)
|
const value = String(row.original.owner.first_name) + " " + String(row.original.owner.last_name)
|
||||||
const initials = String(row.original.owner.first_name[0]) + String(row.original.owner.last_name[0])
|
const initials = String(row.original.owner.first_name[0]) + String(row.original.owner.last_name[0])
|
||||||
return(
|
return (
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="r-flex align-items-center r-gap-16">
|
||||||
<div className="flex items-center justify-center bg-[#DCDCFE] text-[#246BFD] w-10 h-10 rounded-full">
|
<div className="circled-badge">
|
||||||
{initials}
|
{initials}
|
||||||
</div>
|
</div>
|
||||||
<p>{value}</p>
|
<p className="r-p-m-0" >{value}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -191,22 +191,21 @@ export default function Organizations() {
|
|||||||
const status = String(cell.getValue())
|
const status = String(cell.getValue())
|
||||||
return (
|
return (
|
||||||
<p
|
<p
|
||||||
className={`rounded-full px-2 py-1 font-medium text-sm w-20 h-6 text-center
|
className={`custom-badge r-p-m-0 r-flex-center rounded-5
|
||||||
${
|
${status === "active" ? "badge-active" :
|
||||||
status === "active" ? "font-medium w-[80px] h-[24px] bg-[#ECF9E8] text-[#49C91E]" :
|
status === "inactive" ? "badge-inactive" :
|
||||||
status === "inactive" ? "font-medium w-[80px] h-[24px] bg-[#E7EBF3] text-[#9FA8BC]" :
|
status === "pending" ? "badge-pending" :
|
||||||
status === "pending" ? "font-medium w-[80px] h-[24px] bg-[#EAF7FC] text-[#30B2EA]" :
|
status === "blocked" ? "badge-blocked" :
|
||||||
status === "blocked" ? "font-medium w-[80px] h-[24px] bg-[#FDEBE8] text-[#F33F19]" :
|
""
|
||||||
""
|
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
status === "active" ? "Actif" :
|
status === "active" ? "Actif" :
|
||||||
status === "inactive" ? "Inactif" :
|
status === "inactive" ? "Inactif" :
|
||||||
status === "pending" ? "En attente" :
|
status === "pending" ? "En attente" :
|
||||||
status === "blocked" ? "Bloquée" :
|
status === "blocked" ? "Bloquée" :
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
@ -216,64 +215,64 @@ export default function Organizations() {
|
|||||||
id: "delete",
|
id: "delete",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const company = row.original;
|
const company = row.original;
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-end p-2 space-x-2"
|
<div className="flex justify-end p-2 space-x-2"
|
||||||
// onClick={() => { mutate(id) }}
|
// onClick={() => { mutate(id) }}
|
||||||
>
|
>
|
||||||
|
|
||||||
{/* Modal de suppression */}
|
{/* Modal de suppression */}
|
||||||
<Modal
|
<Modal
|
||||||
open={openDeleteModal && selectedAdminId === company.id}
|
open={openDeleteModal && selectedAdminId === company.id}
|
||||||
onOpenChange={(isOpen) => {
|
onOpenChange={(isOpen) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setSelectedAdminId(null);
|
setSelectedAdminId(null);
|
||||||
setOpenDeleteModal(false);
|
setOpenDeleteModal(false);
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
|
trigger={
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedAdminId(company.id);
|
||||||
|
setOpenDeleteModal(true);
|
||||||
}}
|
}}
|
||||||
trigger={
|
>
|
||||||
<div
|
<Image
|
||||||
|
alt="Supprimer"
|
||||||
|
src={icons.trash}
|
||||||
|
className="cursor-pointer responsive-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title="Supprimer une organisation"
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<p className="text-center">
|
||||||
|
Voulez-vous vraiment supprimer l'organisation <span className="font-bold">{row.original.name}</span> ?
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex justify-between w-full pt-6 r-gap-24">
|
||||||
|
<button
|
||||||
|
className="cta modal-cta cancel"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedAdminId(company.id);
|
setOpenDeleteModal(false);
|
||||||
setOpenDeleteModal(true);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
Annuler
|
||||||
alt="Supprimer"
|
</button>
|
||||||
src={icons.trash}
|
<button
|
||||||
className="cursor-pointer responsive-icon"
|
className="cta modal-cta danger"
|
||||||
/>
|
onClick={() => {
|
||||||
</div>
|
deleteMutation.mutate(company.id);
|
||||||
}
|
}}
|
||||||
title="Supprimer une organisation"
|
>
|
||||||
content={
|
Supprimer
|
||||||
<div>
|
</button>
|
||||||
<p className="text-center">
|
</div>
|
||||||
Voulez-vous vraiment supprimer l'organisation <span className="font-bold">{row.original.name}</span> ?
|
</div>
|
||||||
</p>
|
}
|
||||||
|
/>
|
||||||
<div className="flex justify-between w-full pt-6 r-gap-24">
|
</div>
|
||||||
<button
|
)
|
||||||
className="cta modal-cta cancel"
|
|
||||||
onClick={() => {
|
|
||||||
setOpenDeleteModal(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Annuler
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="cta modal-cta danger"
|
|
||||||
onClick={() => {
|
|
||||||
deleteMutation.mutate(company.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Supprimer
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -291,8 +290,8 @@ export default function Organizations() {
|
|||||||
.map((row) => row.original.id);
|
.map((row) => row.original.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 w-full space-y-3">
|
<div className="r-flex flex-wrap flex-lg-nowrap justify-content-between r-gap-16 w-100">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="r-flex align-items-center items-center space-x-3 r-gap-16">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={
|
checked={
|
||||||
@ -306,10 +305,10 @@ export default function Organizations() {
|
|||||||
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<p className="cta cancel flex gap-2">
|
<button className="cta cancel flex gap-2">
|
||||||
Sélectionner une action
|
Sélectionner une action
|
||||||
<Image src={icons.arrowDown} alt="arrow down" />
|
<Image src={icons.arrowDown} alt="arrow down" />
|
||||||
</p>
|
</button >
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
@ -328,7 +327,7 @@ export default function Organizations() {
|
|||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between lg:justify-end space-x-3">
|
<div className="r-flex-between w-100 justify-content-lg-end r-gap-24">
|
||||||
{/* Modal d'ajout */}
|
{/* Modal d'ajout */}
|
||||||
<Modal
|
<Modal
|
||||||
title="Ajouter une organisation"
|
title="Ajouter une organisation"
|
||||||
@ -386,13 +385,13 @@ export default function Organizations() {
|
|||||||
schema={companySchema}
|
schema={companySchema}
|
||||||
child={
|
child={
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={createMutation.isPending}
|
disabled={createMutation.isPending}
|
||||||
className={`${createMutation.isPending ? "btn-auth-loading" : "btn-auth"} cta modal-cta mt-4 cursor-pointer`}
|
className={`${createMutation.isPending ? "btn-auth-loading" : "btn-auth"} cta modal-cta mt-4 cursor-pointer`}
|
||||||
>
|
>
|
||||||
{createMutation.isPending ? "En cours..." : "Ajouter une organisation"}
|
{createMutation.isPending ? "En cours..." : "Ajouter une organisation"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Urbanist'; /* Nom que vous donnerez à votre police */
|
font-family: 'Urbanist';
|
||||||
src: url('../assets/fonts/Urbanist.ttf') format('truetype'); /* Chemin vers votre fichier */
|
src: url('../assets/fonts/Urbanist.ttf') format('truetype');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
@ -14,26 +12,41 @@
|
|||||||
--secondary: #9FA8BC;
|
--secondary: #9FA8BC;
|
||||||
--danger: #F33F19;
|
--danger: #F33F19;
|
||||||
--cinder: #E7E5E4;
|
--cinder: #E7E5E4;
|
||||||
|
--lightblue: #DCDCFE;
|
||||||
--bluegray: #E9F0FF;
|
--bluegray: #E9F0FF;
|
||||||
--gray: #E7EBF3;
|
--gray: #E7EBF3;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ data-theme="dark"] {
|
[ data-theme="dark"] {
|
||||||
--background: #04060F;
|
--background: #11111a;
|
||||||
--foreground: #ffffff;
|
--foreground: #eeeeff;
|
||||||
|
/* --secondary: #000; */
|
||||||
|
--cinder: #444;
|
||||||
|
--bluegray: #246BFD55;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
/* @media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--foreground: #04060F;
|
--foreground: #2a2a2a;
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
|
--cinder: #999;
|
||||||
|
--bluegray: --primary;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: var(--background)!important;
|
||||||
color: var(--foreground);
|
color: var(--foreground)!important;
|
||||||
font-family: Urbanist, sans-serif;
|
font-family: Urbanist, sans-serif !important;
|
||||||
|
}
|
||||||
|
*{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
text-decoration: none!important;
|
||||||
|
color: inherit!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-form {
|
.input-form {
|
||||||
@ -41,7 +54,8 @@ body {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 1px solid #d1d5dc;
|
border: 1px solid #d1d5dc;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
color: black;
|
color: var(--foreground);
|
||||||
|
background-color: var(--background);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline-color: none;
|
outline-color: none;
|
||||||
@ -52,18 +66,32 @@ body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
top: -0.45rem;
|
top: -0.45rem;
|
||||||
background-color: white;
|
padding: 1px 5px;
|
||||||
padding-inline: 4px;
|
line-height: normal;
|
||||||
|
color: var(--secondary);
|
||||||
|
background-color: var(--background);
|
||||||
|
border-radius: 400px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-floating-right {
|
.btn-floating-right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 8px;
|
right: 12px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-floating-right.select{
|
||||||
|
position: relative!important;
|
||||||
|
right: 0px!important;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-floating-left {
|
.btn-floating-left {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
@ -100,7 +128,12 @@ body {
|
|||||||
transition: width 0.2s, height 0.2s;
|
transition: width 0.2s, height 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta{
|
button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cta {
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
@ -114,35 +147,38 @@ body {
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta.modal-cta{
|
.cta.modal-cta {
|
||||||
|
padding: 9px;
|
||||||
|
margin-top: 32px;
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 40px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta.cancel{
|
.cta.cancel {
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
border: 1px solid var(--gray);
|
border: 1px solid var(--gray);
|
||||||
background-color: var(--gray);
|
background-color: var(--gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta.info{
|
.cta.info {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta.danger{
|
.cta.danger {
|
||||||
border: 1px solid var(--danger);
|
border: 1px solid var(--danger);
|
||||||
background-color: var(--danger);
|
background-color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.bg-bluegray{
|
.bg-bluegray {
|
||||||
background-color: var(--bluegray);
|
background-color: var(--bluegray);
|
||||||
}
|
}
|
||||||
.bg-gray{
|
|
||||||
|
.bg-gray {
|
||||||
background-color: var(--gray);
|
background-color: var(--gray);
|
||||||
}
|
}
|
||||||
hr{
|
|
||||||
|
hr {
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +189,10 @@ input[type="checkbox"] {
|
|||||||
|
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border: 1px solid var(--secondary) !important;
|
border: 1px solid var(--secondary) !important;
|
||||||
border-radius: 6px !important;
|
border-radius: 6px !important;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +204,7 @@ input[type="checkbox"]:checked {
|
|||||||
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked::before {
|
input[type="checkbox"]:checked::before {
|
||||||
content: "✔";
|
content: "✔";
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: white;
|
color: white;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -177,17 +213,46 @@ input[type="checkbox"]:checked::before {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-input{
|
.modal-input {
|
||||||
width: 490px;
|
width: 490px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Splash Screnn */
|
||||||
|
|
||||||
|
.splash-screen {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #04060F;
|
||||||
|
background-color: #E9F0FF;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
.splash-screen p{
|
||||||
|
width: max-content;
|
||||||
|
font-size: 32px;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.splash-screen span {
|
||||||
|
/* font-family: "Inter 200"; */
|
||||||
|
font-weight: 100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Scroll Bar */
|
/* Scroll Bar */
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 7px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@ -222,4 +287,106 @@ input[type="checkbox"]:checked::before {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Login Header */
|
||||||
|
.login-header {
|
||||||
|
padding: 20px 0;
|
||||||
|
box-shadow: 0 0 24px #0000001A;
|
||||||
|
/* background-color: white; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
.splash-screen .dot{
|
||||||
|
font-size: 48px;
|
||||||
|
top: -28px;
|
||||||
|
left: 28px;
|
||||||
|
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) {
|
||||||
|
|
||||||
|
.splash-screen p{
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ import NextTopLoader from "nextjs-toploader";
|
|||||||
import "../assets/css/ruben-ui.css"
|
import "../assets/css/ruben-ui.css"
|
||||||
import { AuthProvider } from "#/components/provider/authProvider";
|
import { AuthProvider } from "#/components/provider/authProvider";
|
||||||
import { QueryClientProvide } from "#/components/provider/queryClient";
|
import { QueryClientProvide } from "#/components/provider/queryClient";
|
||||||
|
import SplashScreen from "#/components/splashScreen";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Private Docs",
|
title: "Private Docs",
|
||||||
@ -15,18 +16,21 @@ export default function RootLayout({
|
|||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
<link rel="favicon.svg" 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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<QueryClientProvide>
|
<QueryClientProvide>
|
||||||
<NextTopLoader color="#246BFD" shadow="0" />
|
<SplashScreen name="White" label="developer" timer={2000} />
|
||||||
{children}
|
<NextTopLoader color="#246BFD" shadow="0" />
|
||||||
|
{children}
|
||||||
</QueryClientProvide>
|
</QueryClientProvide>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,10 +1,28 @@
|
|||||||
|
/* Header */
|
||||||
|
.sidebar-holder, .sidebar{
|
||||||
|
min-width: 78px;
|
||||||
|
max-width: 90px;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
.header{
|
||||||
|
padding: 20px 44px;
|
||||||
|
height: max-content;
|
||||||
|
align-items: center;
|
||||||
|
/* border: solid; */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Sidebar */
|
/* Sidebar */
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
padding-top: 25px;
|
||||||
border-right: 1px solid var(--cinder);
|
border-right: 1px solid var(--cinder);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-menu{
|
||||||
|
padding-top: 160px;
|
||||||
|
}
|
||||||
.nav-item .nav-home {
|
.nav-item .nav-home {
|
||||||
margin-bottom: -10px;
|
margin-bottom: -10px;
|
||||||
}
|
}
|
||||||
@ -29,33 +47,62 @@
|
|||||||
margin-top: -11px;
|
margin-top: -11px;
|
||||||
margin-bottom: -11px;
|
margin-bottom: -11px;
|
||||||
}
|
}
|
||||||
|
.logout{
|
||||||
|
bottom: 40px;
|
||||||
|
left: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Border */
|
/* Border */
|
||||||
.icon-border {
|
.icon-border {
|
||||||
border: 1px solid var(--cinder);
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
height: max-content;
|
||||||
|
border: 1px solid var(--bluegray);
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle{
|
||||||
|
align-items: center!important;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
width: max-content;
|
width: max-content;
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
|
border: none!important;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 0 24px #0000001A;
|
box-shadow: 0 0 24px #0000001A!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item a {
|
.dropdown-menu{
|
||||||
width: max-content;
|
right: 0px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r-dropdown-item a {
|
||||||
|
width: 100%;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
margin: 0!important;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
.r-dropdown-item a:hover {
|
||||||
|
background-color: var(--cinder);
|
||||||
|
}
|
||||||
|
.dropdown-toggle::after{
|
||||||
|
content: unset!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transition-300{
|
||||||
|
transition: transform .3s ;
|
||||||
|
}
|
||||||
|
|
||||||
/* Main */
|
/* Main */
|
||||||
|
.main{
|
||||||
|
padding: 64px 44px;
|
||||||
|
}
|
||||||
.p-container {
|
.p-container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
border: 1px solid var(--gray);
|
border: 1px solid var(--gray);
|
||||||
@ -95,14 +142,189 @@
|
|||||||
width: 284px;
|
width: 284px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-desktop{
|
||||||
|
display: flex!important;
|
||||||
|
}
|
||||||
|
.mobile{
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
/* Home Page */
|
||||||
.admin-infos {
|
.stats{
|
||||||
/* justify-content: space-between; */
|
width: 22%;
|
||||||
/* gap: 30px; */
|
max-width: 400px;
|
||||||
|
height: 108px;
|
||||||
|
max-height: 108px;
|
||||||
|
padding: 24px 27px;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-wrap: nowrap!important;
|
||||||
|
border: 1px solid!important;
|
||||||
|
border-color: var(--primary)!important;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
.sqarre-bg{
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--bluegray);
|
||||||
|
}
|
||||||
|
|
||||||
|
th{
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 8px;
|
||||||
|
height: 40px!important;
|
||||||
|
}
|
||||||
|
th:first-of-type{
|
||||||
|
border-top-left-radius: 8px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
th:last-of-type{
|
||||||
|
border-top-right-radius: 8px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
height: 56px!important;
|
||||||
|
max-height: 56px!important;
|
||||||
|
overflow: hidden;
|
||||||
|
border-bottom: 1px solid var(--bluegray)!important;
|
||||||
|
}
|
||||||
|
tbody tr:hover{
|
||||||
|
background-color: var(--bluegray);
|
||||||
|
}
|
||||||
|
tbody tr td:first-child {
|
||||||
|
padding-left: 16px;
|
||||||
|
width: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr td:last-child {
|
||||||
|
width: 56px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circled-badge{
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500!important;
|
||||||
|
border-radius: 40px;
|
||||||
|
color: var(--primary);
|
||||||
|
background-color: var(--lightblue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-badge{
|
||||||
|
width: 80px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
.badge-active{
|
||||||
|
color: #49C91E;
|
||||||
|
background-color: #ECF9E8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-inactive{
|
||||||
|
color: #9FA8BC;
|
||||||
|
background-color: #E7EBF3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pending{
|
||||||
|
color: #30B2EA;
|
||||||
|
background-color: #EAF7FC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-blocked{
|
||||||
|
color: #F33F19;
|
||||||
|
background-color: #FDEBE8;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.disabled{
|
||||||
|
opacity: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home .pagination {
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.search-input{
|
||||||
|
padding: 10px 40px;
|
||||||
|
width: max-content;
|
||||||
|
width: 300px;
|
||||||
|
min-width: max-content;
|
||||||
|
border: 1px solid var(--secondary);
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 1070px) {
|
||||||
|
|
||||||
|
.stats-container{
|
||||||
|
justify-content: space-evenly;
|
||||||
}
|
}
|
||||||
.admin-card {
|
|
||||||
/* max-width: 100%; */
|
.stats{
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
|
||||||
|
.header, .main{
|
||||||
|
padding-left: 22px;
|
||||||
|
padding-right: 22px;
|
||||||
|
}
|
||||||
|
.header-desktop{
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
.mobile{
|
||||||
|
display: flex!important;
|
||||||
|
}
|
||||||
|
.mobile-flex{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.mobile-none{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .name{
|
||||||
|
padding-left: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar{
|
||||||
|
transition: all ease-in-out .4s;
|
||||||
|
}
|
||||||
|
.hamburger{
|
||||||
|
position: absolute;
|
||||||
|
top: 43px;
|
||||||
|
left: 22px;
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
.hamburger.shifted{
|
||||||
|
left: 100px;
|
||||||
|
}
|
||||||
|
.hamburger-icon{
|
||||||
|
color: var(--primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.stats-container{
|
||||||
|
/* flex-direction: column; */
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.stats{
|
||||||
|
width: 300px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
src/assets/icons/archive.svg
Normal file
8
src/assets/icons/archive.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.0001 29.3333H20.0001C26.6667 29.3333 29.3334 26.6667 29.3334 20V12C29.3334 5.33334 26.6667 2.66667 20.0001 2.66667H12.0001C5.33341 2.66667 2.66675 5.33334 2.66675 12V20C2.66675 26.6667 5.33341 29.3333 12.0001 29.3333Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 10.3333V19.3333C24 17.8667 22.8 16.6667 21.3333 16.6667H10.6667C9.2 16.6667 8 17.8667 8 19.3333V10.3333C8 8.86667 9.2 7.66667 10.6667 7.66667H21.3333C22.8 7.66667 24 8.86667 24 10.3333Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M25.3333 21H24" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.00008 21H6.66675" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 18.6667V14.6667C24 13.2 22.8 12 21.3333 12H10.6667C9.2 12 8 13.2 8 14.6667V18.6667" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 19.3333V21H19.3333C19.3333 22.84 17.84 24.3333 16 24.3333C14.16 24.3333 12.6667 22.84 12.6667 21H8V19.3333C8 17.8667 9.2 16.6667 10.6667 16.6667H21.3333C22.8 16.6667 24 17.8667 24 19.3333Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
9
src/assets/icons/buildingsBlue.svg
Normal file
9
src/assets/icons/buildingsBlue.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.3333 29.3333H6.66663C3.99996 29.3333 2.66663 28 2.66663 25.3333V14.6667C2.66663 12 3.99996 10.6667 6.66663 10.6667H13.3333V25.3333C13.3333 28 14.6666 29.3333 17.3333 29.3333Z" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.4799 5.33333C13.3733 5.73333 13.3333 6.17333 13.3333 6.66666V10.6667H6.66663V7.99999C6.66663 6.53333 7.86663 5.33333 9.33329 5.33333H13.4799Z" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M18.6666 10.6667V17.3333" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 10.6667V17.3333" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M22.6666 22.6667H20C19.2666 22.6667 18.6666 23.2667 18.6666 24V29.3333H24V24C24 23.2667 23.4 22.6667 22.6666 22.6667Z" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8 17.3333V22.6667" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.3334 25.3333V6.66667C13.3334 4.00001 14.6667 2.66667 17.3334 2.66667H25.3334C28 2.66667 29.3334 4.00001 29.3334 6.66667V25.3333C29.3334 28 28 29.3333 25.3334 29.3333H17.3334C14.6667 29.3333 13.3334 28 13.3334 25.3333Z" stroke="#246BFD" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
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 |
@ -22,6 +22,7 @@ import menuIcon from "./Menu Dots.svg"
|
|||||||
import messagesIcon from "./message.svg"
|
import messagesIcon from "./message.svg"
|
||||||
import homeIcon from "./NavItem.svg"
|
import homeIcon from "./NavItem.svg"
|
||||||
import companiesIcon from "./buildings.svg"
|
import companiesIcon from "./buildings.svg"
|
||||||
|
import companiesIconBlue from "./buildingsBlue.svg"
|
||||||
import arrowLeft from "./arrowLeft.svg"
|
import arrowLeft from "./arrowLeft.svg"
|
||||||
import arrowRight from "./arrowRight.svg"
|
import arrowRight from "./arrowRight.svg"
|
||||||
import filesIcon from "./ph_files.svg"
|
import filesIcon from "./ph_files.svg"
|
||||||
@ -43,6 +44,8 @@ import trash from "./trash.svg"
|
|||||||
import mailIcon from "./sms.svg"
|
import mailIcon from "./sms.svg"
|
||||||
import personalCard from "./personalcard.svg"
|
import personalCard from "./personalcard.svg"
|
||||||
import arrowDown from "#/assets/icons/chevron-down.svg"
|
import arrowDown from "#/assets/icons/chevron-down.svg"
|
||||||
|
import peopleIcon from "#/assets/icons/people.svg"
|
||||||
|
import docsArchive from "#/assets/icons/archive.svg"
|
||||||
|
|
||||||
|
|
||||||
export const icons = {
|
export const icons = {
|
||||||
@ -57,13 +60,16 @@ export const icons = {
|
|||||||
eyeIcon,
|
eyeIcon,
|
||||||
eyeSlashIcon,
|
eyeSlashIcon,
|
||||||
checkboxchedIcon,
|
checkboxchedIcon,
|
||||||
|
peopleIcon,
|
||||||
crossIcon,
|
crossIcon,
|
||||||
addBlueIcon,
|
addBlueIcon,
|
||||||
|
docsArchive,
|
||||||
archivesIcon,
|
archivesIcon,
|
||||||
notificationsIcon,
|
notificationsIcon,
|
||||||
timerIcon,
|
timerIcon,
|
||||||
logo,
|
logo,
|
||||||
companiesIcon,
|
companiesIcon,
|
||||||
|
companiesIconBlue,
|
||||||
logout,
|
logout,
|
||||||
logoutRed,
|
logoutRed,
|
||||||
maximizeIcon,
|
maximizeIcon,
|
||||||
@ -92,92 +98,4 @@ export const icons = {
|
|||||||
personalCard,
|
personalCard,
|
||||||
arrowDown
|
arrowDown
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
import AddIcon from "./add.svg";
|
|
||||||
import CalendarIcon from "./Calendar Mark.svg";
|
|
||||||
import DocummentTextIcon from "./document-text.svg";
|
|
||||||
import FolderIcon from "./Document.svg";
|
|
||||||
import EditIcon from "./edit-2.svg";
|
|
||||||
import GridIcon from "./element-3-white.svg";
|
|
||||||
import GridBlueIcon from "./element-3.svg";
|
|
||||||
import ProfilePicture from "./Ellipse 2.svg";
|
|
||||||
import EyeIcon from "./eye.svg";
|
|
||||||
import EyeSlashIcon from "./eye-slash.svg";
|
|
||||||
import CheckboxchedIcon from "./checked.svg";
|
|
||||||
import CrossIcon from "./cross.svg";
|
|
||||||
import AddBlueIcon from "./icon-add.svg";
|
|
||||||
import ArchivesIcon from "./archives.svg";
|
|
||||||
import NotificationsIcon from "./notifications.svg";
|
|
||||||
import TimerIcon from "./Line Duotone.svg";
|
|
||||||
import Logo from "./logo.svg";
|
|
||||||
import LogoutRed from "./logout-red.svg";
|
|
||||||
import Logout from "./logout.svg";
|
|
||||||
import MaximizeIcon from "./maximize-3.svg";
|
|
||||||
import MenuIcon from "./Menu Dots.svg";
|
|
||||||
import MessagesIcon from "./message.svg";
|
|
||||||
import HomeIcon from "./NavItem.svg";
|
|
||||||
import CompaniesIcon from "./buildings.svg";
|
|
||||||
import ArrowLeft from "./arrowLeft.svg";
|
|
||||||
import ArrowRight from "./arrowLeft.svg";
|
|
||||||
import FilesIcon from "./ph_files.svg";
|
|
||||||
import PdfIcon from "./prime_file-pdf.svg";
|
|
||||||
import WordIcon from "./prime_file-word.svg";
|
|
||||||
import UserIcon from "./profile.svg";
|
|
||||||
import UserGroup from "./profile-2user.svg";
|
|
||||||
import UserGroupBlue from "./profile-2user-blue.svg";
|
|
||||||
import Rectanagle from "./Rectangle.svg";
|
|
||||||
import SearchIcon from "./Search.svg";
|
|
||||||
import SettingsIcon from "./setting-2.svg";
|
|
||||||
import FilterIcon from "./setting-3.svg";
|
|
||||||
import ShareIcon from "./share.svg";
|
|
||||||
import StarIcon from "./star.svg";
|
|
||||||
import ArrowUp from "./Vector.svg";
|
|
||||||
|
|
||||||
export const icons = {
|
|
||||||
AddIcon,
|
|
||||||
CalendarIcon,
|
|
||||||
DocummentTextIcon,
|
|
||||||
FolderIcon,
|
|
||||||
EditIcon,
|
|
||||||
GridIcon,
|
|
||||||
GridBlueIcon,
|
|
||||||
ProfilePicture,
|
|
||||||
EyeIcon,
|
|
||||||
EyeSlashIcon,
|
|
||||||
CheckboxchedIcon,
|
|
||||||
CrossIcon,
|
|
||||||
AddBlueIcon,
|
|
||||||
ArchivesIcon,<svg width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:var(--theme-toggle-moon-icon-display)"><path d="M2.89998 0.499976C2.89998 0.279062 2.72089 0.0999756 2.49998 0.0999756C2.27906 0.0999756 2.09998 0.279062 2.09998 0.499976V1.09998H1.49998C1.27906 1.09998 1.09998 1.27906 1.09998 1.49998C1.09998 1.72089 1.27906 1.89998 1.49998 1.89998H2.09998V2.49998C2.09998 2.72089 2.27906 2.89998 2.49998 2.89998C2.72089 2.89998 2.89998 2.72089 2.89998 2.49998V1.89998H3.49998C3.72089 1.89998 3.89998 1.72089 3.89998 1.49998C3.89998 1.27906 3.72089 1.09998 3.49998 1.09998H2.89998V0.499976ZM5.89998 3.49998C5.89998 3.27906 5.72089 3.09998 5.49998 3.09998C5.27906 3.09998 5.09998 3.27906 5.09998 3.49998V4.09998H4.49998C4.27906 4.09998 4.09998 4.27906 4.09998 4.49998C4.09998 4.72089 4.27906 4.89998 4.49998 4.89998H5.09998V5.49998C5.09998 5.72089 5.27906 5.89998 5.49998 5.89998C5.72089 5.89998 5.89998 5.72089 5.89998 5.49998V4.89998H6.49998C6.72089 4.89998 6.89998 4.72089 6.89998 4.49998C6.89998 4.27906 6.72089 4.09998 6.49998 4.09998H5.89998V3.49998ZM1.89998 6.49998C1.89998 6.27906 1.72089 6.09998 1.49998 6.09998C1.27906 6.09998 1.09998 6.27906 1.09998 6.49998V7.09998H0.499976C0.279062 7.09998 0.0999756 7.27906 0.0999756 7.49998C0.0999756 7.72089 0.279062 7.89998 0.499976 7.89998H1.09998V8.49998C1.09998 8.72089 1.27906 8.89997 1.49998 8.89997C1.72089 8.89997 1.89998 8.72089 1.89998 8.49998V7.89998H2.49998C2.72089 7.89998 2.89998 7.72089 2.89998 7.49998C2.89998 7.27906 2.72089 7.09998 2.49998 7.09998H1.89998V6.49998ZM8.54406 0.98184L8.24618 0.941586C8.03275 0.917676 7.90692 1.1655 8.02936 1.34194C8.17013 1.54479 8.29981 1.75592 8.41754 1.97445C8.91878 2.90485 9.20322 3.96932 9.20322 5.10022C9.20322 8.37201 6.82247 11.0878 3.69887 11.6097C3.45736 11.65 3.20988 11.6772 2.96008 11.6906C2.74563 11.702 2.62729 11.9535 2.77721 12.1072C2.84551 12.1773 2.91535 12.2458 2.98667 12.3128L3.05883 12.3795L3.31883 12.6045L3.50684 12.7532L3.62796 12.8433L3.81491 12.9742L3.99079 13.089C4.11175 13.1651 4.23536 13.2375 4.36157 13.3059L4.62496 13.4412L4.88553 13.5607L5.18837 13.6828L5.43169 13.7686C5.56564 13.8128 5.70149 13.8529 5.83857 13.8885C5.94262 13.9155 6.04767 13.9401 6.15405 13.9622C6.27993 13.9883 6.40713 14.0109 6.53544 14.0298L6.85241 14.0685L7.11934 14.0892C7.24637 14.0965 7.37436 14.1002 7.50322 14.1002C11.1483 14.1002 14.1032 11.1453 14.1032 7.50023C14.1032 7.25044 14.0893 7.00389 14.0623 6.76131L14.0255 6.48407C13.991 6.26083 13.9453 6.04129 13.8891 5.82642C13.8213 5.56709 13.7382 5.31398 13.6409 5.06881L13.5279 4.80132L13.4507 4.63542L13.3766 4.48666C13.2178 4.17773 13.0353 3.88295 12.8312 3.60423L12.6782 3.40352L12.4793 3.16432L12.3157 2.98361L12.1961 2.85951L12.0355 2.70246L11.8134 2.50184L11.4925 2.24191L11.2483 2.06498L10.9562 1.87446L10.6346 1.68894L10.3073 1.52378L10.1938 1.47176L9.95488 1.3706L9.67791 1.2669L9.42566 1.1846L9.10075 1.09489L8.83599 1.03486L8.54406 0.98184ZM10.4032 5.30023C10.4032 4.27588 10.2002 3.29829 9.83244 2.40604C11.7623 3.28995 13.1032 5.23862 13.1032 7.50023C13.1032 10.593 10.596 13.1002 7.50322 13.1002C6.63646 13.1002 5.81597 12.9036 5.08355 12.5522C6.5419 12.0941 7.81081 11.2082 8.74322 10.0416C8.87963 10.2284 9.10028 10.3497 9.34928 10.3497C9.76349 10.3497 10.0993 10.0139 10.0993 9.59971C10.0993 9.24256 9.84965 8.94373 9.51535 8.86816C9.57741 8.75165 9.63653 8.63334 9.6926 8.51332C9.88358 8.63163 10.1088 8.69993 10.35 8.69993C11.0403 8.69993 11.6 8.14028 11.6 7.44993C11.6 6.75976 11.0406 6.20024 10.3505 6.19993C10.3853 5.90487 10.4032 5.60464 10.4032 5.30023Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
|
||||||
NotificationsIcon,
|
|
||||||
TimerIcon,
|
|
||||||
Logo,
|
|
||||||
CompaniesIcon,
|
|
||||||
Logout,
|
|
||||||
LogoutRed,
|
|
||||||
MaximizeIcon,
|
|
||||||
MenuIcon,
|
|
||||||
MessagesIcon,
|
|
||||||
HomeIcon,
|
|
||||||
ArrowLeft,
|
|
||||||
ArrowRight,
|
|
||||||
FilesIcon,
|
|
||||||
PdfIcon,
|
|
||||||
WordIcon,
|
|
||||||
UserIcon,
|
|
||||||
UserGroup,
|
|
||||||
UserGroupBlue,
|
|
||||||
Rectanagle,
|
|
||||||
SearchIcon,
|
|
||||||
SettingsIcon,
|
|
||||||
FilterIcon,
|
|
||||||
ShareIcon,
|
|
||||||
StarIcon,
|
|
||||||
ArrowUp
|
|
||||||
};
|
|
||||||
|
|
||||||
*/
|
|
||||||
8
src/assets/icons/people.svg
Normal file
8
src/assets/icons/people.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M23.9999 9.54668C23.9199 9.53334 23.8266 9.53334 23.7466 9.54668C21.9066 9.48001 20.4399 7.97334 20.4399 6.10667C20.4399 4.20001 21.9733 2.66667 23.8799 2.66667C25.7866 2.66667 27.3199 4.21334 27.3199 6.10667C27.3066 7.97334 25.8399 9.48001 23.9999 9.54668Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M22.6266 19.2534C24.4532 19.56 26.4666 19.24 27.8799 18.2934C29.7599 17.04 29.7599 14.9867 27.8799 13.7334C26.4532 12.7867 24.4132 12.4667 22.5865 12.7867" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7.96002 9.54668C8.04002 9.53334 8.13335 9.53334 8.21335 9.54668C10.0533 9.48001 11.52 7.97334 11.52 6.10667C11.52 4.20001 9.98668 2.66667 8.08002 2.66667C6.17335 2.66667 4.64001 4.21334 4.64001 6.10667C4.65335 7.97334 6.12002 9.48001 7.96002 9.54668Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.33337 19.2534C7.50671 19.56 5.49338 19.24 4.08004 18.2934C2.20004 17.04 2.20004 14.9867 4.08004 13.7334C5.50671 12.7867 7.54671 12.4667 9.37337 12.7867" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M15.9999 19.5067C15.9199 19.4933 15.8266 19.4933 15.7466 19.5067C13.9066 19.44 12.4399 17.9333 12.4399 16.0667C12.4399 14.16 13.9733 12.6267 15.8799 12.6267C17.7866 12.6267 19.3199 14.1733 19.3199 16.0667C19.3066 17.9333 17.8399 19.4533 15.9999 19.5067Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.12 23.7067C10.24 24.96 10.24 27.0133 12.12 28.2667C14.2533 29.6933 17.7466 29.6933 19.88 28.2667C21.76 27.0133 21.76 24.96 19.88 23.7067C17.76 22.2933 14.2533 22.2933 12.12 23.7067Z" stroke="#246BFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -12,34 +12,53 @@ export default function AdminHeader() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<nav className="header r-flex-between px-[44px] py-[20px] ">
|
<nav className="header r-flex-between align-items-center ">
|
||||||
<p className="name text-[26px]">Bienvenue, <span>Ken B.</span> </p>
|
<p className="r-m-0 name fs-4 fw-medium">Bienvenue, <span>Ken B.</span> </p>
|
||||||
<div className="r-flex-between justify-center items-center r-gap-12">
|
<div className="r-flex-between justify-content-center align-items-center r-gap-12">
|
||||||
<Theme />
|
<div className="header-desktop">
|
||||||
{/* <ProfilePicture /> */}
|
<Theme />
|
||||||
|
</div>
|
||||||
<button type="button" className="icon-border">
|
<button type="button" className="icon-border header-desktop">
|
||||||
<Image src={icons.notificationsIcon} alt="Notifications" />
|
<Image src={icons.notificationsIcon} alt="Notifications" />
|
||||||
</button>
|
</button>
|
||||||
<div className="relative">
|
|
||||||
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
<div className="dropdown">
|
||||||
|
<div className="r-flex-between dropdown-toggle justify-center items-center r-gap-12 cursor-pointer" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<button type="button" className="icon-borderd mobile scale-120" >
|
||||||
|
<Image src={icons.arrowLeft} alt="options" className={`transition-300 transition-transform duration-300 ${open ? " " : ""}`} />
|
||||||
|
</button>
|
||||||
|
<Image src={icons.profilePicture} alt="ProfilePicture" />
|
||||||
|
<button className="IconButton header-desktop" aria-label="Customise options">
|
||||||
|
<Image src={icons.arrowUp} alt="arrowUp" className={`transition-300 transition-transform duration-300 ${open ? "" : ""}`} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul className="dropdown-menu">
|
||||||
|
<li className="r-dropdown-item text-[14px]">
|
||||||
|
<Link href="#" className="d-flex align-items-center ">
|
||||||
|
<Image src={icons.userIcon} alt="Profil" />
|
||||||
|
Profil
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className="r-dropdown-item text-[14px]">
|
||||||
|
<Link href="#" className="d-flex align-items-center r-danger ">
|
||||||
|
<Image src={icons.logoutRed} alt="Deconnexion" />
|
||||||
|
Déconnexion
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="position-relative">
|
||||||
|
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<div className="r-flex-between justify-center items-center r-gap-12 cursor-pointer ">
|
|
||||||
<Image src={icons.profilePicture} alt="ProfilePicture" />
|
|
||||||
<button className="IconButton" aria-label="Customise options">
|
|
||||||
<Image src={icons.arrowUp} alt="arrowUp" className={`transition-transform duration-300 ${open ? "scale-y-[-1]" : ""}`}/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content className="DropdownMenuContent dropdown-menu r-flex-column r-secondary" sideOffset={0} side="bottom" align="end" >
|
<DropdownMenu.Content className="DropdownMenuContent dropdown-menu r-flex-column r-secondary" sideOffset={0} side="bottom" align="end" >
|
||||||
<DropdownMenu.Item className="DropdownMenuItem dropdown-item text-[14px]">
|
<DropdownMenu.Item className="DropdownMenuItem dropdown-item text-[14px]">
|
||||||
<Link href="#" className="d-flex items-start ">
|
|
||||||
<Image src={icons.userIcon} alt="Profil" />
|
|
||||||
Profil
|
|
||||||
</Link>
|
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
{/* <DropdownMenu.Item className="DropdownMenuItem dropdown-item flex items-center content-center text-[14px] mobile">
|
||||||
|
<Theme />
|
||||||
|
</DropdownMenu.Item> */}
|
||||||
<DropdownMenu.Item className="DropdownMenuItem dropdown-item text-[14px]">
|
<DropdownMenu.Item className="DropdownMenuItem dropdown-item text-[14px]">
|
||||||
<Link href="#" className="d-flex items-start r-danger ">
|
<Link href="#" className="d-flex items-start r-danger ">
|
||||||
<Image src={icons.logoutRed} alt="Deconnexion" />
|
<Image src={icons.logoutRed} alt="Deconnexion" />
|
||||||
|
|||||||
@ -7,14 +7,15 @@ interface ItemProps {
|
|||||||
label: string;
|
label: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isNavHome?: boolean;
|
isNavHome?: boolean;
|
||||||
|
classname?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavItem({ link, iconSrc, label, isActive, isNavHome }: ItemProps) {
|
export default function NavItem({ link, iconSrc, label, isActive, isNavHome, classname }: ItemProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Link href={link} className={`nav-item r-flex-center ${isActive ? "active" : ""}`} >
|
<Link href={link} className={`nav-item r-flex-center ${isActive ? "active" : ""} ${classname}`} >
|
||||||
<Image src={iconSrc} alt={label} className={`scale-100 ${isNavHome ? "nav-home" : ""}`} />
|
<Image src={iconSrc} alt={label} className={`scale-100 ${isNavHome ? "nav-home" : ""}`} />
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -4,31 +4,44 @@ import { icons } from "#/assets/icons"
|
|||||||
import NavItem from "./navItem";
|
import NavItem from "./navItem";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
export default function Sidebar() {
|
export default function Sidebar() {
|
||||||
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
const [mobileVisible, setmobileVisible] = useState(false)
|
||||||
|
|
||||||
|
const handleMobileVisible = ()=> {
|
||||||
|
setmobileVisible(!mobileVisible)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="sidebar-holder min-w-[78px] max-w-[90px] h-[100vh] ">
|
<div className={` ${ mobileVisible? "mobile-flex" : "mobile-none" } sidebar-holder position-relative `}>
|
||||||
<div className="sidebar r-m-0 d-flex flex-column pt-[25px] max-w-[90px] h-[100vh] ">
|
<div className="sidebar r-m-0 d-flex flex-column ">
|
||||||
<div className="logo r-flex-center px-[19px] ">
|
<div className="logo r-flex-center px-[19px] ">
|
||||||
<Image src={icons.logo} alt="Logo" className="scale-95" />
|
<Image src={icons.logo} alt="Logo" className="scale-95" />
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-menu r-column-center h-max pt-[160px] r-gap-40 ">
|
<div className="nav-menu r-column-center h-max pt-[160px] r-gap-40 ">
|
||||||
<NavItem link="/admin/home" iconSrc={icons.homeIcon} label="Home" isNavHome={true} isActive={(pathname === "/admin/") || (pathname === "/admin/home")} />
|
<NavItem link="/admin/home" iconSrc={icons.homeIcon} label="Home" isNavHome={true} isActive={(pathname === "/admin/") || (pathname === "/admin/home")} />
|
||||||
<NavItem link="/admin/organizations" iconSrc={icons.companiesIcon} label="Organizations" isActive={pathname.startsWith("/admin/organizations")} />
|
<NavItem link="/admin/organizations" iconSrc={icons.companiesIcon} label="Organizations" isActive={pathname.startsWith("/admin/organizations")} classname="disabled" />
|
||||||
<NavItem link="/admin/admins" iconSrc={icons.userGroup} label="Admins" isActive={pathname.startsWith("/admin/admins")} />
|
<NavItem link="/admin/admins" iconSrc={icons.userGroup} label="Admins" isActive={pathname.startsWith("/admin/admins")} classname="disabled" />
|
||||||
</div>
|
</div>
|
||||||
<div className="logout absolute bottom-[40px] left-[28px]">
|
<div className="logout position-absolute bottom-[40px] left-[28px]">
|
||||||
<Link href="/login" className="cursor-pointer">
|
<Link href="/login" className="cursor-pointer">
|
||||||
<Image src={icons.logout} alt="Logout" />
|
<Image src={icons.logout} alt="Logout" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div onClick={() => handleMobileVisible()} className={`${mobileVisible ? "shifted" : ""} hamburger mobile`}>
|
||||||
|
{/* <Image src={icons.logo} alt="Logo" className="scale-95" /> */}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-list hamburger-icon" viewBox="0 0 16 16">
|
||||||
|
<path fillRule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -8,7 +8,7 @@ export default function Theme() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const savedTheme = localStorage.getItem("theme") ||
|
const savedTheme = localStorage.getItem("privateDocsTheme") ||
|
||||||
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
||||||
|
|
||||||
setTheme(savedTheme);
|
setTheme(savedTheme);
|
||||||
@ -19,7 +19,7 @@ export default function Theme() {
|
|||||||
const handleTheme = () => {
|
const handleTheme = () => {
|
||||||
const newTheme = theme === "light" ? "dark" : "light";
|
const newTheme = theme === "light" ? "dark" : "light";
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
localStorage.setItem("theme", newTheme);
|
localStorage.setItem("privateDocsTheme", newTheme);
|
||||||
document.body.setAttribute("data-theme", newTheme);
|
document.body.setAttribute("data-theme", newTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export default function Theme() {
|
|||||||
const handleChange = (e: MediaQueryListEvent) => {
|
const handleChange = (e: MediaQueryListEvent) => {
|
||||||
const newTheme = e.matches ? "dark" : "light";
|
const newTheme = e.matches ? "dark" : "light";
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
localStorage.setItem("theme", newTheme);
|
localStorage.setItem("privateDocsTheme", newTheme);
|
||||||
document.body.setAttribute("data-theme", newTheme);
|
document.body.setAttribute("data-theme", newTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default function FloatingLabelInput({
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case 'select':
|
case 'select':
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="position-relative w-full">
|
||||||
<select
|
<select
|
||||||
className="input-form modal-input focus:ring-2 focus:ring-blue-500 outline-none"
|
className="input-form modal-input focus:ring-2 focus:ring-blue-500 outline-none"
|
||||||
name={name}
|
name={name}
|
||||||
@ -40,13 +40,13 @@ export default function FloatingLabelInput({
|
|||||||
<option key={index} value={option.value}>{option.label}</option>
|
<option key={index} value={option.value}>{option.label}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{button && <div className="btn-floating-right">{button}</div>}
|
{button && <div className="btn-floating-right select">{button}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
case 'password':
|
case 'password':
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="position-relative w-full">
|
||||||
<input
|
<input
|
||||||
name={name}
|
name={name}
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
@ -66,14 +66,14 @@ export default function FloatingLabelInput({
|
|||||||
src={icons.eyeSlashIcon}
|
src={icons.eyeSlashIcon}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
alt=''
|
alt='show password'
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
src={icons.eyeIcon}
|
src={icons.eyeIcon}
|
||||||
width={20}
|
width={20}
|
||||||
height={20}
|
height={20}
|
||||||
alt=''
|
alt='hide password'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
@ -84,14 +84,14 @@ export default function FloatingLabelInput({
|
|||||||
|
|
||||||
case 'search':
|
case 'search':
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="position-relative w-full">
|
||||||
<div className='btn-floating-left'>
|
<div className='btn-floating-left'>
|
||||||
<Image alt='' src={icons.searchIcon} />
|
<Image alt='' src={icons.searchIcon} />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className="focus:ring-2 focus:ring-blue-500 outline-none px-10 py-2 w-full text-black border border-[#d1d5dc] rounded-full"
|
className="search-input focus:ring-2 focus:ring-blue-500 outline-none px-10 py-2 w-full ctext-black border border-[#d1d5dc] rounded-full"
|
||||||
name={name}
|
name={name}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
@ -102,7 +102,7 @@ export default function FloatingLabelInput({
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full">
|
<div className="position-relative w-full">
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -118,7 +118,7 @@ export default function FloatingLabelInput({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="position-relative">
|
||||||
<label
|
<label
|
||||||
htmlFor={name}
|
htmlFor={name}
|
||||||
className="input-label text-gray-400 text-sm"
|
className="input-label text-gray-400 text-sm"
|
||||||
|
|||||||
@ -5,72 +5,71 @@ import { FormProps } from "#/types"
|
|||||||
import { FormEvent, useState } from "react"
|
import { FormEvent, useState } from "react"
|
||||||
|
|
||||||
export default function Form({
|
export default function Form({
|
||||||
fields,
|
fields,
|
||||||
submit,
|
submit,
|
||||||
className,
|
className,
|
||||||
child,
|
child,
|
||||||
title,
|
title,
|
||||||
schema,
|
schema,
|
||||||
formClassName
|
formClassName
|
||||||
} : FormProps) {
|
}: FormProps) {
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const formData = new FormData(e.currentTarget)
|
const formData = new FormData(e.currentTarget)
|
||||||
const data = Object.fromEntries(formData)
|
const data = Object.fromEntries(formData)
|
||||||
|
|
||||||
console.log("FORM DATA = ", data)
|
console.log("FORM DATA = ", data)
|
||||||
const result = schema.safeParse(data);
|
const result = schema.safeParse(data);
|
||||||
console.log("ZOD = ", result.error?.format())
|
console.log("ZOD = ", result.error?.format())
|
||||||
|
|
||||||
if(!result.success) {
|
if (!result.success) {
|
||||||
const formatedErrors = result.error.format() as Record<string, { _errors?: string[] }>;
|
const formatedErrors = result.error.format() as Record<string, { _errors?: string[] }>;
|
||||||
|
|
||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {};
|
||||||
Object.keys(formatedErrors).forEach((field) => {
|
Object.keys(formatedErrors).forEach((field) => {
|
||||||
if (field !== "_errors" && formatedErrors[field]._errors?.length) {
|
if (field !== "_errors" && formatedErrors[field]._errors?.length) {
|
||||||
newErrors[field] = formatedErrors[field]._errors[0];
|
newErrors[field] = formatedErrors[field]._errors[0];
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setErrors(newErrors)
|
|
||||||
} else {
|
|
||||||
setErrors({})
|
|
||||||
submit(result.data)
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setErrors(newErrors)
|
||||||
|
} else {
|
||||||
|
setErrors({})
|
||||||
|
submit(result.data)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className={formClassName}>
|
<form onSubmit={handleSubmit} className={(formClassName) + " form-container"}>
|
||||||
<div className="flex justify-center text-black">
|
<div className="form-title d-flex justify-content-center text-black mb-3 ">
|
||||||
<p className="text-3xl font-bold">{title}</p>
|
<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>
|
||||||
|
))
|
||||||
<div className={`space-y-8 my-2 ${className}`}>
|
}
|
||||||
|
</div>
|
||||||
{
|
{child}
|
||||||
fields.map((item, index) => (
|
</form>
|
||||||
<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>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@ -2,16 +2,17 @@ import { icons } from "#/assets/icons";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return(
|
return (
|
||||||
<div className="w-full bg-white shadow-md py-4">
|
<div className="d-flex justify-content-center login-header ">
|
||||||
<div className="container mx-auto text-center flex items-center justify-center gap-2">
|
<div className="d-flex justify-content-center align-items-center gap-2">
|
||||||
<Image
|
<Image
|
||||||
src={icons.logo}
|
src={icons.logo}
|
||||||
alt="Private Docs"
|
alt="Private Docs"
|
||||||
className="text-red-500 h-auto"
|
className="text-red-500 h-auto"
|
||||||
/>
|
height={30}
|
||||||
<p className="text-2xl font-bold text-black">Private Docs</p>
|
/>
|
||||||
</div>
|
<p className="r-p-m-0 word">Pr<span className="dot">•</span>ıvate Docs</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
63
src/components/splashScreen.tsx
Normal file
63
src/components/splashScreen.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"use client"
|
||||||
|
import Image from "next/image";
|
||||||
|
import { icons } from "#/assets/icons";
|
||||||
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string
|
||||||
|
label: string
|
||||||
|
timer: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SplashScreen({ name, label, timer }: Props) {
|
||||||
|
|
||||||
|
const [showSplash, setShowSplash] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => setShowSplash(false), timer);
|
||||||
|
|
||||||
|
if (typeof window !== undefined) {
|
||||||
|
|
||||||
|
if (!sessionStorage.getItem('sessionInitialized')) {
|
||||||
|
setShowSplash(true);
|
||||||
|
// Set session
|
||||||
|
} else {
|
||||||
|
setShowSplash(false);
|
||||||
|
//console.log('Session already exists');
|
||||||
|
}
|
||||||
|
const handleUnload = () => sessionStorage.removeItem('sessionInitialized')
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", handleUnload);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
handleUnload
|
||||||
|
} // Clean up the timeout on unmount
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [timer]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showSplash &&
|
||||||
|
<div className="splash-screen w-[100vw] h-[100vh] ">
|
||||||
|
<div className="d-flex justify-content-center align-items-center gap-3">
|
||||||
|
<Image
|
||||||
|
src={icons.logo}
|
||||||
|
alt="Private Docs"
|
||||||
|
className="text-red-500 h-auto"
|
||||||
|
height={48}
|
||||||
|
/>
|
||||||
|
<p className="r-p-m-0 word">Pr<span className="dot">•</span>ıvate Docs</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -7,64 +7,64 @@ import { Stats, StatsType, } from "#/types";
|
|||||||
|
|
||||||
export default function Statistics() {
|
export default function Statistics() {
|
||||||
|
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
|
|
||||||
const { data: stats, isLoading} = useQuery({
|
const { data: stats, isLoading } = useQuery({
|
||||||
enabled: status === 'authenticated',
|
enabled: status === 'authenticated',
|
||||||
queryKey: ["stats", session?.user.access_token],
|
queryKey: ["stats", session?.user.access_token],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
'https://private-docs-api.intside.co/statistics', {
|
'https://private-docs-api.intside.co/statistics', {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${session?.user.access_token}`
|
'Authorization': `Bearer ${session?.user.access_token}`
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(response.data) {
|
|
||||||
return response.data as Stats
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
const statsData: StatsType[] = [
|
if (response.data) {
|
||||||
{ id: 1, title: 'Organisations', value: stats?.companies, icon: icons.companiesIcon, color: 'blue' },
|
return response.data as Stats
|
||||||
{ id: 2, title: 'Utilisateurs', value: stats?.users, icon: icons.userIcon, color: 'blue' },
|
}
|
||||||
{ id: 3, title: 'Documents', value: stats?.documents, icon: icons.docummentTextIcon, color: 'blue' },
|
} catch (error) {
|
||||||
{ id: 4, title: 'Stockage', value: stats?.documents_size, icon: icons.archivesIcon, color: 'blue' }
|
console.error(error)
|
||||||
];
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const statsData: StatsType[] = [
|
||||||
|
{ id: 1, title: 'Organisations', value: stats?.companies, icon: icons.companiesIconBlue, color: 'blue' },
|
||||||
|
{ id: 2, title: 'Utilisateurs', value: stats?.users, icon: icons.peopleIcon, color: 'blue' },
|
||||||
|
{ id: 3, title: 'Documents', value: stats?.documents, icon: icons.docsArchive, color: 'blue' },
|
||||||
|
{ id: 4, title: 'Stockage', value: (stats?.documents_size + " GB"), icon: icons.maximizeIcon, color: 'blue' }
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return(
|
return (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="stats-container r-flex-between r-gap-24 flex-wrap overflow-hidden ">
|
||||||
{statsData.map(({ id, title, value, icon }) => (
|
{statsData.map(({ id, title, value, icon }) => (
|
||||||
<div key={id} className="w-full">
|
<div key={id} className="stats flex items-center rounded-xl r-gap-12">
|
||||||
<div className="flex items-center rounded-xl border-2 border-blue-500 p-4 space-x-3">
|
{/* <div className=""> */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-center rounded-lg bg-[#E9F0FF] bg-opacity-25 p-2"
|
className="sqarre-bg d-flex items-center justify-center rounded-lg bg-[#E9F0FF] bg-opacity-25 p-2"
|
||||||
style={{ width: '54px', height: '54px' }}
|
style={{ width: '54px', height: '54px' }}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
alt={title}
|
alt={title}
|
||||||
src={icon}
|
src={icon}
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
className="text-blue-500"
|
className="text-blue-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<p className="text-sm text-gray-500 mb-0">{title}</p>
|
<p className="text-sm fw-semibold text-secondary mb-0">{title}</p>
|
||||||
<p className="font-bold text-2xl mb-0">{ status === "loading" && isLoading ? "Chargement..." : value}</p>
|
<p className="fw-bold fs-5 mb-0">{status === "loading" && isLoading ? "Chargement..." : value}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/* </div> */}
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@ -1,214 +1,209 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
getPaginationRowModel,
|
getPaginationRowModel,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
Table as TableType,
|
Table as TableType,
|
||||||
} from "@tanstack/react-table"
|
} from "@tanstack/react-table"
|
||||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { icons } from "#/assets/icons";
|
import { icons } from "#/assets/icons";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
interface DataTableProps<TData, TValue> {
|
interface DataTableProps<TData, TValue> {
|
||||||
columns: ColumnDef<TData, TValue>[]
|
columns: ColumnDef<TData, TValue>[]
|
||||||
data: TData[],
|
data: TData[],
|
||||||
pageSize?: number,
|
pageSize?: number,
|
||||||
header?: ReactNode | ((table: TableType<TData>) => ReactNode),
|
header?: ReactNode | ((table: TableType<TData>) => ReactNode),
|
||||||
isDataLoading?: boolean
|
isDataLoading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Table<TData, TValue>({
|
export default function Table<TData, TValue>({
|
||||||
columns,
|
columns,
|
||||||
|
data,
|
||||||
|
pageSize = 10,
|
||||||
|
header,
|
||||||
|
isDataLoading = true
|
||||||
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
const [rowSelection, setRowSelection] = useState({})
|
||||||
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
pageSize = 10,
|
columns,
|
||||||
header,
|
getCoreRowModel: getCoreRowModel(),
|
||||||
isDataLoading = true
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
}: DataTableProps<TData, TValue>) {
|
|
||||||
const [rowSelection, setRowSelection] = useState({})
|
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
const table = useReactTable({
|
state: {
|
||||||
data,
|
rowSelection,
|
||||||
columns,
|
columnFilters,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
},
|
||||||
getPaginationRowModel: getPaginationRowModel(),
|
initialState: {
|
||||||
|
pagination: {
|
||||||
state: {
|
pageSize: pageSize,
|
||||||
rowSelection,
|
}
|
||||||
columnFilters,
|
},
|
||||||
},
|
enableRowSelection: true,
|
||||||
initialState: {
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
pagination: {
|
onRowSelectionChange: setRowSelection,
|
||||||
pageSize: pageSize,
|
onColumnFiltersChange: setColumnFilters,
|
||||||
}
|
})
|
||||||
},
|
|
||||||
enableRowSelection: true,
|
|
||||||
getFilteredRowModel: getFilteredRowModel(),
|
|
||||||
onRowSelectionChange: setRowSelection,
|
|
||||||
onColumnFiltersChange: setColumnFilters,
|
|
||||||
})
|
|
||||||
|
|
||||||
const headerCheckboxRef = useRef<HTMLInputElement>(null);
|
const headerCheckboxRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (headerCheckboxRef.current) {
|
if (headerCheckboxRef.current) {
|
||||||
headerCheckboxRef.current.indeterminate =
|
headerCheckboxRef.current.indeterminate =
|
||||||
table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected();
|
table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected();
|
||||||
}
|
|
||||||
|
|
||||||
const selectedRows = table.getSelectedRowModel().rows;
|
|
||||||
const filteredSelectedRows = table
|
|
||||||
.getRowModel()
|
|
||||||
.rows.filter((row) => row.getIsSelected())
|
|
||||||
.map((row) => row.original);
|
|
||||||
|
|
||||||
console.log("SELECTED ALL = ", selectedRows);
|
|
||||||
console.log("SELECTED = ", filteredSelectedRows);
|
|
||||||
}, [
|
|
||||||
table
|
|
||||||
]);
|
|
||||||
|
|
||||||
const totalPages = table.getPageCount()
|
|
||||||
const currentPage = table.getState().pagination.pageIndex + 1
|
|
||||||
|
|
||||||
const getPageNumbers = () => {
|
|
||||||
const pages = []
|
|
||||||
for (let i = 1; i <= totalPages; i++) {
|
|
||||||
pages.push(i)
|
|
||||||
}
|
|
||||||
return pages
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = () => {
|
const selectedRows = table.getSelectedRowModel().rows;
|
||||||
if(!header) return null
|
const filteredSelectedRows = table
|
||||||
|
.getRowModel()
|
||||||
|
.rows.filter((row) => row.getIsSelected())
|
||||||
|
.map((row) => row.original);
|
||||||
|
|
||||||
return(
|
console.log("SELECTED ALL = ", selectedRows);
|
||||||
<div className="mb-4">
|
console.log("SELECTED = ", filteredSelectedRows);
|
||||||
{typeof header === 'function'
|
}, [
|
||||||
? header(table)
|
table
|
||||||
: header}
|
]);
|
||||||
</div>
|
|
||||||
)
|
const totalPages = table.getPageCount()
|
||||||
|
const currentPage = table.getState().pagination.pageIndex + 1
|
||||||
|
|
||||||
|
const getPageNumbers = () => {
|
||||||
|
const pages = []
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
pages.push(i)
|
||||||
}
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
return(
|
const render = () => {
|
||||||
<div className="w-full">
|
if (!header) return null
|
||||||
{render()}
|
|
||||||
|
|
||||||
<div className="rounded-lg border border-gray-200 w-auto">
|
return (
|
||||||
<div className="overflow-x-auto">
|
<div className="mb-4">
|
||||||
<table className="w-full overflow-x-auto rounded-lg">
|
{typeof header === 'function'
|
||||||
<thead className="h-10">
|
? header(table)
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
: header}
|
||||||
<tr key={headerGroup.id} className="rounded-lg">
|
</div>
|
||||||
{header
|
|
||||||
?
|
|
||||||
<th className="bg-[#E9F0FF] p-3 text-start first:rounded-tl-lg">
|
|
||||||
|
|
||||||
</th>
|
|
||||||
:
|
|
||||||
<th className="bg-[#E9F0FF] p-3 text-start first:rounded-tl-lg">
|
|
||||||
<input
|
|
||||||
ref={headerCheckboxRef}
|
|
||||||
checked={!!table.getIsAllPageRowsSelected()}
|
|
||||||
onChange={(e) => table.toggleAllPageRowsSelected(e.target.checked)}
|
|
||||||
type="checkbox" name="" id=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
}
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return(
|
|
||||||
<th key={header.id} className="bg-[#E9F0FF] p-3 text-start last:rounded-tr-lg">
|
|
||||||
{flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</th>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{table.getRowModel().rows.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<tr key={row.id} className={clsx('hover:bg-gray-100 border-t border-gray-200', { 'bg-gray-300': row.getIsSelected()})}>
|
|
||||||
<td className="p-3 text-start">
|
|
||||||
<input
|
|
||||||
checked={row.getIsSelected()}
|
|
||||||
onChange={(e) => row.toggleSelected(e.target.checked)}
|
|
||||||
type="checkbox" name="" id=""
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<td key={cell.id} className="p-3 text-start">
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
: isDataLoading ?
|
|
||||||
(
|
|
||||||
<tr>
|
|
||||||
<td colSpan={columns.length} className="h-20 text-center">
|
|
||||||
Chargement...
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<tr>
|
|
||||||
<td colSpan={columns.length} className="h-20 text-center">
|
|
||||||
Aucun résultats
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-end space-x-2 py-4">
|
|
||||||
<button
|
|
||||||
className="hover:bg-gray-300 cursor-pointer px-3 py-1 rounded w-9 h-9"
|
|
||||||
onClick={() => table.previousPage()}
|
|
||||||
disabled={!table.getCanPreviousPage()}
|
|
||||||
>
|
|
||||||
<Image alt="" src={icons.arrowLeft} className="hover:text-blue-400"/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className="flex space-x-1">
|
|
||||||
{getPageNumbers().map((pageNumber) => (
|
|
||||||
<button
|
|
||||||
key={pageNumber}
|
|
||||||
className={clsx(
|
|
||||||
"px-3 py-1 rounded w-9 h-9",
|
|
||||||
pageNumber === currentPage
|
|
||||||
? "bg-[#E9F0FF] text-blue-400"
|
|
||||||
: "hover:bg-gray-300 cursor-pointer"
|
|
||||||
)}
|
|
||||||
onClick={() => table.setPageIndex(pageNumber - 1)}
|
|
||||||
>
|
|
||||||
{pageNumber}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="w-9 h-9 hover:bg-gray-300 cursor-pointer hover:text-black px-3 py-1 rounded"
|
|
||||||
onClick={() => table.nextPage()}
|
|
||||||
disabled={!table.getCanNextPage()}
|
|
||||||
>
|
|
||||||
<Image alt="" src={icons.arrowRight} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
{render()}
|
||||||
|
|
||||||
|
<div className="rounded-top">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-100 overflow-x-auto rounded-1c ">
|
||||||
|
<thead className="table-head bg-bluegray">
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id} className="rounded-lg">
|
||||||
|
{header
|
||||||
|
?
|
||||||
|
<th className=" r-p-m-0 text-start ">
|
||||||
|
|
||||||
|
</th>
|
||||||
|
:
|
||||||
|
<th className=" r-p-m-0 text-start ">
|
||||||
|
|
||||||
|
</th>
|
||||||
|
}
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<th key={header.id} className="r-p-m-0 text-start">
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{table.getRowModel().rows.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<tr key={row.id} className={clsx('hover:bg-gray-100 border-t border-gray-200', { 'bg-gray-300': row.getIsSelected() })}>
|
||||||
|
<td className="r-p-m-0 text-start">
|
||||||
|
<input
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onChange={(e) => row.toggleSelected(e.target.checked)}
|
||||||
|
type="checkbox" name="" id=""
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<td key={cell.id} className="r-p-m-0 text-start">
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)
|
||||||
|
: isDataLoading ?
|
||||||
|
(
|
||||||
|
<tr>
|
||||||
|
<td colSpan={columns.length} className="h-20 text-center">
|
||||||
|
Chargement...
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={columns.length} className="h-20 text-center">
|
||||||
|
Aucun résultats
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pagination r-flex-center justify-content-end r-gap-2 py-4">
|
||||||
|
<button
|
||||||
|
className="hover:bg-gray-300 cursor-pointer px-3 py-1 rounded w-9 h-9"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
<Image alt="" src={icons.arrowLeft} className="hover:text-blue-400" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="r-flex r-gap-1">
|
||||||
|
{getPageNumbers().map((pageNumber) => (
|
||||||
|
<button
|
||||||
|
key={pageNumber}
|
||||||
|
className={clsx(
|
||||||
|
"px-3 py-1 rounded w-9 h-9",
|
||||||
|
pageNumber === currentPage
|
||||||
|
? "bg-bluegray text-blue-400"
|
||||||
|
: "hover:bg-gray-300 cursor-pointer"
|
||||||
|
)}
|
||||||
|
onClick={() => table.setPageIndex(pageNumber - 1)}
|
||||||
|
>
|
||||||
|
{pageNumber}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="w-9 h-9 px-3 py-1 rounded"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
<Image alt="" src={icons.arrowRight} />
|
||||||
|
</button>
|
||||||
|
</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}</>;
|
||||||
|
}
|
||||||
@ -30,7 +30,7 @@ export interface FormProps{
|
|||||||
export interface StatsType {
|
export interface StatsType {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
value: number | undefined;
|
value: number | string | undefined;
|
||||||
icon: string;
|
icon: string;
|
||||||
color: string;
|
color: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,6 @@
|
|||||||
"@/*": ["./src/*"]
|
"@/*": ["./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"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user