diff --git a/.env.sample b/.env.sample index be13e163..a01289e7 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,5 @@ NEXT_PUBLIC_API_TOKEN="" JWT_SECRET="" NODE_ENV="" -NEXT_PUBLIC_API_URL="" \ No newline at end of file +NEXT_PUBLIC_API_URL="" +NEXT_PUBLIC_JWT_SECRET="" \ No newline at end of file diff --git a/backend/src/entities/user.entity.ts b/backend/src/entities/user.entity.ts index 011acf60..03e85cea 100644 --- a/backend/src/entities/user.entity.ts +++ b/backend/src/entities/user.entity.ts @@ -30,4 +30,4 @@ export class User { @Field() isPasswordChange: boolean; -} \ No newline at end of file +} diff --git a/backend/src/index.ts b/backend/src/index.ts index 88831c21..b5585b88 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -29,7 +29,7 @@ import { loadedLogos, loadLogos } from './lib/logoLoader'; const prisma = new PrismaClient(); export interface JwtPayload { - userId: number; + id: number; } export interface MyContext { @@ -37,9 +37,18 @@ export interface MyContext { res: express.Response; apiKey: string | undefined; cookies: Cookies; + token : string | undefined | null; user: User | null; } +// export interface JwtPayload { +// userId: number; +// email?: string; +// role?: string; +// iat?: number; +// exp?: number; +// } + const app = express(); const httpServer = http.createServer(app); @@ -170,13 +179,13 @@ async function main() { expressMiddleware(server, { context: async ({ req, res }) => { const cookies = new Cookies(req, res); - // console.log("cookies:", cookies.get("jwt")); + let user: User | null = null; - const token = cookies.get("jwt"); - // console.log("Token du cookie:", token ? "Présent" : "Absent"); + const token = cookies.get("token"); if (token && process.env.JWT_SECRET) { + // console.log("token ---->", token) try { const { payload } = await jwtVerify( token, @@ -186,7 +195,7 @@ async function main() { // console.log("Payload du token décodé:", payload); const prismaUser = await prisma.user.findUnique({ - where: { id: payload.userId } + where: { id: payload.id } }); if (prismaUser) { @@ -202,7 +211,7 @@ async function main() { } catch (err) { console.error("Erreur de vérification JWT:", err); // Log l'erreur complète - cookies.set("jwt", "", { expires: new Date(0), httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const }); + cookies.set("token", "", { expires: new Date(0), httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const }); } } @@ -219,7 +228,7 @@ async function main() { await checkApiKey(apiKey); } - return { req, res, apiKey, cookies, user }; + return { req, res, apiKey, cookies, token, user }; }, }) ); diff --git a/backend/src/lib/generateSecurePassword.ts b/backend/src/lib/generateSecurePassword.ts index 20e48af5..ce82ee36 100644 --- a/backend/src/lib/generateSecurePassword.ts +++ b/backend/src/lib/generateSecurePassword.ts @@ -2,7 +2,8 @@ export function generateSecurePassword(): string { const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const lowercase = "abcdefghijklmnopqrstuvwxyz"; const numbers = "0123456789"; - const symbols = "!@#$%^&*()_+[]{}|;:,.<>?"; + // const symbols = "!@#$%^&*()_+[]{}|;:,.<>?"; + const symbols = "!@#$%^*-_=+"; const all = uppercase + lowercase + numbers + symbols; diff --git a/backend/src/resolvers/user.resolver.ts b/backend/src/resolvers/user.resolver.ts index 66805839..34e5a754 100644 --- a/backend/src/resolvers/user.resolver.ts +++ b/backend/src/resolvers/user.resolver.ts @@ -10,10 +10,12 @@ import argon2 from "argon2"; import { structureMessageCreatedAccountHTML, structureMessageCreatedAccountTEXT } from "../mail/structureMail.service"; import { Response } from "../entities/response.types"; import { emailRegex, passwordRegex, checkRegex } from "../regex"; -import jwt from "jsonwebtoken"; +import { jwtVerify, SignJWT } from "jose"; +import { TextEncoder } from "util"; import { MyContext } from ".."; // const prisma = new PrismaClient(); +const secret = new TextEncoder().encode(process.env.JWT_SECRET); @Resolver(() => User) export class UserResolver { @@ -164,15 +166,21 @@ export class UserResolver { } const tokenPayload = { - userId: user.id, + id: user.id, + email: user.email, + role: user.role, }; if (!process.env.JWT_SECRET) { return { code: 500, message: "Please check your JWT configuration !" }; } - const token = jwt.sign(tokenPayload, process.env.JWT_SECRET , { expiresIn: "7d" }); - + const token = await new SignJWT(tokenPayload) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime("7d") + .sign(secret); + const cookieOptions = { httpOnly: true, secure: process.env.NODE_ENV === 'production', @@ -181,7 +189,7 @@ export class UserResolver { path: '/', }; - ctx.cookies.set("jwt", token, cookieOptions); + ctx.cookies.set("token", token, cookieOptions); return { code: 200, @@ -197,6 +205,28 @@ export class UserResolver { } } + @Query(() => User, { nullable: true }) + async me(@Ctx() { req, token }: MyContext): Promise { + // console.log("refresh FRONTcdodddof") + // console.log("req.cookies", req.cookies) + // console.log(cookies) + // const token = req.cookies.token; + console.log("token", token) + if (!token) return null; + + try { + const payload = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET)); + const user = await this.db.user.findUnique({ where: { id: payload.payload.id as number } }); + if (!user) return null; + return { + ...user, + role: user.role as UserRole, + }; + } catch { + return null; + } + } + @Mutation(() => Response) async logout( @Ctx() ctx: MyContext @@ -208,7 +238,7 @@ export class UserResolver { return { code: 401, message: "Authentication required." }; } - ctx.cookies.set("jwt", "", { + ctx.cookies.set("token", "", { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const, diff --git a/backend/src/types/graphql.ts b/backend/src/types/graphql.ts index d9e2e384..b3611283 100644 --- a/backend/src/types/graphql.ts +++ b/backend/src/types/graphql.ts @@ -413,6 +413,7 @@ export type Query = { generateCaptcha: CaptchaResponse; getGlobalStats: GlobalStatsResponse; listBackupFiles: BackupFilesResponse; + me?: Maybe; projectById: ProjectResponse; projectList: ProjectsResponse; skillList: CategoryResponse; diff --git a/docker-compose.yml b/docker-compose.yml index 8c5868fe..593248ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,8 @@ services: args: NEXT_PUBLIC_API_TOKEN: ${NEXT_PUBLIC_API_TOKEN} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} + NEXT_PUBLIC_JWT_SECRET: ${NEXT_PUBLIC_JWT_SECRET} + JWT_SECRET: ${JWT_SECRET} ports: - 3000:3000 volumes: @@ -32,6 +34,8 @@ services: - NEXT_PUBLIC_IMAGE_URL=http://localhost:8000 - NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN} - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} + - NEXT_PUBLIC_JWT_SECRET=${NEXT_PUBLIC_JWT_SECRET} + - JWT_SECRET=${JWT_SECRET} - API_URL=${API_URL} - NODE_ENV=development env_file: diff --git a/frontend/.env.sample b/frontend/.env.sample index f139ba9e..a1028186 100644 --- a/frontend/.env.sample +++ b/frontend/.env.sample @@ -1,4 +1,6 @@ isProduction="true and false" NEXT_DISABLE_HMR="true and false" NEXT_PUBLIC_API_TOKEN="" -NEXT_PUBLIC_API_URL="" \ No newline at end of file +NEXT_PUBLIC_API_URL="" +NEXT_PUBLIC_JWT_SECRET="" +JWT_SECRET="" \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index b6879ac4..66e4a195 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -17,10 +17,16 @@ ENV NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN} ARG NEXT_PUBLIC_API_URL ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} -# CMD npm run dev +ARG NEXT_PUBLIC_JWT_SECRET +ENV NEXT_PUBLIC_JWT_SECRET=${NEXT_PUBLIC_JWT_SECRET} -RUN npm run build -CMD npm run start +ARG JWT_SECRET +ENV JWT_SECRET=${JWT_SECRET} + +CMD npm run dev + +# RUN npm run build +# CMD npm run start # RUN if [ "$NODE_ENV" = "production" ]; then npm run build; fi # CMD if [ "$NODE_ENV" = "production" ]; then npm run start; else npm run dev; fi \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 53638893..be444e55 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -29,6 +29,7 @@ "clsx": "^2.1.1", "framer-motion": "^11.2.5", "graphql": "^16.9.0", + "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "next": "^14.2.14", "react": "^18.3.1", @@ -40,6 +41,7 @@ "tsparticles": "^3.4.0" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.10", "@types/lodash": "^4.17.4", "@types/node": "^20", "@types/react": "^18", @@ -3865,12 +3867,30 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==", "dev": true }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.12.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", @@ -4737,6 +4757,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4802,9 +4828,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001653", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", - "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", + "version": "1.0.30001723", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", + "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", "funding": [ { "type": "opencollective", @@ -4818,7 +4844,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", @@ -5483,6 +5510,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.13", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", @@ -7631,6 +7667,28 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -7646,6 +7704,27 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7785,12 +7864,54 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9325,7 +9446,6 @@ "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, "bin": { "semver": "bin/semver.js" }, diff --git a/frontend/package.json b/frontend/package.json index e724873f..e967c3dd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,6 +8,7 @@ "start": "next start", "generate": "dotenv -e .env -- graphql-codegen --config codegen.yml --watch", "generatesave": "graphql-codegen --watch", + "build:css": "tailwindcss -i ./src/styles/globals.css -o ./src/styles/output.css --minify", "lint": "next lint" }, "dependencies": { @@ -32,6 +33,7 @@ "clsx": "^2.1.1", "framer-motion": "^11.2.5", "graphql": "^16.9.0", + "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "next": "^14.2.14", "react": "^18.3.1", @@ -43,6 +45,7 @@ "tsparticles": "^3.4.0" }, "devDependencies": { + "@types/jsonwebtoken": "^9.0.10", "@types/lodash": "^4.17.4", "@types/node": "^20", "@types/react": "^18", diff --git a/frontend/src/components/AuthFormLayout/AuthFormLayout.tsx b/frontend/src/components/AuthFormLayout/AuthFormLayout.tsx new file mode 100644 index 00000000..0f98af76 --- /dev/null +++ b/frontend/src/components/AuthFormLayout/AuthFormLayout.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from "react"; +import { Box } from "@mui/material"; + +interface AuthFormLayoutProps { + title: string; + children: ReactNode; +} + +const AuthFormLayout = ({ title, children } : AuthFormLayoutProps) : React.ReactElement => { + return ( +
+ +

+ {title} +

+
+ {children} +
+
+
+ ); +} + +export default AuthFormLayout; \ No newline at end of file diff --git a/frontend/src/components/Button/BurgerButton.tsx b/frontend/src/components/Button/BurgerButton.tsx new file mode 100644 index 00000000..b4c550e5 --- /dev/null +++ b/frontend/src/components/Button/BurgerButton.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +type BurgerButtonProps = { + open: boolean; + toggleMenu: () => void; + className?: string; +}; + +const BurgerButton: React.FC = ({ open, toggleMenu, className }) => ( + +); + +export default BurgerButton; \ No newline at end of file diff --git a/frontend/src/components/Button/Button.tsx b/frontend/src/components/Button/Button.tsx index 923b0b27..733aede2 100644 --- a/frontend/src/components/Button/Button.tsx +++ b/frontend/src/components/Button/Button.tsx @@ -1,7 +1,7 @@ import { Button } from "@mui/material"; type Props = { - onClick?: () => void; + onClick?: (e: React.MouseEvent) => void; text: string; disable?: boolean; disableHover?: boolean; diff --git a/frontend/src/components/Button/ButtonLinkNavBar.tsx b/frontend/src/components/Button/ButtonLinkNavBar.tsx new file mode 100644 index 00000000..815afecf --- /dev/null +++ b/frontend/src/components/Button/ButtonLinkNavBar.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { usePathname } from "next/navigation"; + +type ButtonLinkNavBarProps = { + children: React.ReactNode; + sectionRef: React.RefObject; + handleScrollToSection: ( + event: React.MouseEvent, + sectionRef: React.RefObject + ) => void; + className?: string; +}; + +const ButtonLinkNavBar: React.FC = ({ + children, + sectionRef, + handleScrollToSection, + className = "", +}) => { + const pathname = usePathname(); + + if (pathname !== "/") return null; + + return ( + + ); +}; + +export default ButtonLinkNavBar; \ No newline at end of file diff --git a/frontend/src/components/Careers/Careers.tsx b/frontend/src/components/Careers/Careers.tsx index 2b425bed..a7e26f23 100644 --- a/frontend/src/components/Careers/Careers.tsx +++ b/frontend/src/components/Careers/Careers.tsx @@ -64,17 +64,15 @@ const Careers: React.FC = (): React.ReactElement => { }, [dataEducations, dataExperiences]); return ( -
-
    +
    +
      {combinedData?.map((item, index) => ( -
    1. -
      -
      -

      +

    2. + +
      +

      {item.startDate} - {item.endDate}

      -
      -

      {item.type}

      {(item as EducationType)?.title} {(item as ExperienceType)?.job} @@ -90,7 +88,7 @@ const Careers: React.FC = (): React.ReactElement => {

    3. ))}
    -
    +
); }; diff --git a/frontend/src/components/CustomSelect/CustomSelect.tsx b/frontend/src/components/CustomSelect/CustomSelect.tsx new file mode 100644 index 00000000..efe67f4d --- /dev/null +++ b/frontend/src/components/CustomSelect/CustomSelect.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import { FormControl, InputLabel, Select, MenuItem, SelectChangeEvent } from "@mui/material"; + +interface CustomSelectProps { + id: string; + label: string; + name: string; + value: string; + onChange: (event: SelectChangeEvent) => void; + options: { value: string; label: string }[]; +} + +const CustomSelect: React.FC = ({ + id, + label, + name, + value, + onChange, + options, +}) => { + return ( + + + {label} + + + + ); +}; + +export default CustomSelect; \ No newline at end of file diff --git a/frontend/src/components/InputField/InputField.tsx b/frontend/src/components/InputField/InputField.tsx index f4eeb5e1..4a84ce46 100644 --- a/frontend/src/components/InputField/InputField.tsx +++ b/frontend/src/components/InputField/InputField.tsx @@ -9,6 +9,7 @@ interface InputFieldProps { onChange: (e: ChangeEvent) => void; multiline?: boolean; rows?: number; + name?: string; } const InputField: React.FC = ({ @@ -19,6 +20,7 @@ const InputField: React.FC = ({ onChange, multiline = false, rows, + name }) => { return ( = ({ onChange={onChange} multiline={multiline} rows={rows} + name={name} className="bg-white border border-gray-300 rounded-md text-text" sx={{ "& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline": diff --git a/frontend/src/components/ModalCustom/ModalCustom.tsx b/frontend/src/components/ModalCustom/ModalCustom.tsx new file mode 100644 index 00000000..c7bfa0af --- /dev/null +++ b/frontend/src/components/ModalCustom/ModalCustom.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import Modal from "@mui/material/Modal"; +import Box from "@mui/material/Box"; +import { useTheme } from "@mui/material/styles"; + +interface ModalCustomProps { + open: boolean; + onClose: () => void; + children: React.ReactNode; + width?: number | string; + className?: string; +} + +const ModalCustom: React.FC = ({ + open, + onClose, + children, + width = 400, + className = "", +}) => { + const theme = useTheme(); + + return ( + + + {children} + + + ); +}; + +export default ModalCustom; \ No newline at end of file diff --git a/frontend/src/components/NavBar/NavBar.tsx b/frontend/src/components/NavBar/NavBar.tsx index f2ace089..3ccae14c 100644 --- a/frontend/src/components/NavBar/NavBar.tsx +++ b/frontend/src/components/NavBar/NavBar.tsx @@ -3,14 +3,22 @@ import { useTheme } from "@/context/Theme/ThemeContext"; import { useLang } from "@/context/Lang/LangContext"; import { useSectionRefs } from "@/context/SectionRefs/SectionRefsContext"; import ColorLensIcon from "@mui/icons-material/ColorLens"; -import Box from "@mui/material/Box"; import Button from "@/components/Button/Button"; -import Modal from "@mui/material/Modal"; import { useChoiceView } from "@/context/ChoiceView/ChoiceViewContext"; import ToggleButton from "../Button/ToggleButton"; import ChoiceViewButton from "../Button/ChoiceViewButton"; +import ButtonLinkNavBar from "../Button/ButtonLinkNavBar"; +import BurgerButton from "../Button/BurgerButton"; +import { usePathname } from "next/navigation"; +import Link from "next/link"; +import ModalCustom from "../ModalCustom/ModalCustom"; const Navbar: React.FC = (): React.ReactElement => { + + const pathname = usePathname(); + + console.log("pathname", pathname); + const { lang, setLang, translations } = useLang(); const { aboutMeRef, @@ -24,27 +32,16 @@ const Navbar: React.FC = (): React.ReactElement => { const { toggleTheme } = useTheme(); const { selectedView } = useChoiceView(); - const [menuOpen, setMenuOpen]: [ - boolean, - React.Dispatch> - ] = useState(false); - const [open, setOpen]: [ - boolean, - React.Dispatch> - ] = React.useState(false); - const [isCheckedLang, setIsCheckedLang]: [ - boolean, - React.Dispatch> - ] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [open, setOpen] = useState(false); + const [isCheckedLang, setIsCheckedLang] = useState(false); - const handleOpen: () => void = (): void => setOpen(true); - const handleClose: () => void = (): void => setOpen(false); + const handleOpen = (): void => setOpen(true); + const handleClose = (): void => setOpen(false); - const toggleMenu: () => void = (): void => setMenuOpen(!menuOpen); + const toggleMenu = (): void => setMenuOpen(!menuOpen); - const handleChangeColorTheme: (newTheme: string) => void = ( - newTheme: string - ): void => { + const handleChangeColorTheme = (newTheme: string): void => { toggleTheme(newTheme); handleClose(); setMenuOpen(false); @@ -54,21 +51,23 @@ const Navbar: React.FC = (): React.ReactElement => { setIsCheckedLang(translations.file === "en"); }, [translations]); - const toggleCheckedLang: () => void = (): void => { + const toggleCheckedLang = (): void => { setIsCheckedLang(!isCheckedLang); setLang(lang === "fr" ? "en" : "fr"); }; - const handleScrollToSection: ( - event: React.MouseEvent, - sectionRef: React.RefObject - ) => void = ( + const handleScrollToSection = ( event: React.MouseEvent, sectionRef: React.RefObject ): void => { event.preventDefault(); if (sectionRef?.current) { - sectionRef.current.scrollIntoView({ behavior: "smooth" }); + const yOffset = -80; + const y = + sectionRef.current.getBoundingClientRect().top + + window.pageYOffset + + yOffset; + window.scrollTo({ top: y, behavior: "smooth" }); } setMenuOpen(false); }; @@ -77,71 +76,82 @@ const Navbar: React.FC = (): React.ReactElement => { ); }; -export default Navbar; +export default Navbar; \ No newline at end of file diff --git a/frontend/src/context/UserContext/UserContext.tsx b/frontend/src/context/UserContext/UserContext.tsx new file mode 100644 index 00000000..2ccebd51 --- /dev/null +++ b/frontend/src/context/UserContext/UserContext.tsx @@ -0,0 +1,48 @@ +import { + createContext, + useContext, + useEffect, + useState, + ReactNode, +} from "react"; +import { + GetMeQuery, + useGetMeQuery, +} from "@/types/graphql"; + +interface UserContextType { + user: GetMeQuery["me"] | null; + loading: boolean; + error: Error | null; + refetch: () => void; +} + +const UserContext = createContext(undefined); + +export const UserProvider = ({ children }: { children: ReactNode }) => { + const { data, loading, error, refetch } = useGetMeQuery(); + const [user, setUser] = useState(null); + + useEffect(() => { + console.log("data", data) + if (data?.me) { + setUser(data.me); + } else { + setUser(null); + } + }, [data]); + + return ( + + {children} + + ); +}; + +export const useUser = (): UserContextType => { + const context = useContext(UserContext); + if (!context) { + throw new Error("useUser must be used within a UserProvider"); + } + return context; +}; \ No newline at end of file diff --git a/frontend/src/lang/en.tsx b/frontend/src/lang/en.tsx index 6bb8616e..ad46ee98 100644 --- a/frontend/src/lang/en.tsx +++ b/frontend/src/lang/en.tsx @@ -3,6 +3,8 @@ import Lang from "./typeLang"; const en: Lang = { file: "en", titleHTML: "Alexandre Renard - Fullstack Developer | Portfolio", + titleHTMLNotFound: "Alexandre Renard - Fullstack Developer | 404", + titleHTMLUnauthorizedAccess : "Unauthorized Access", descHTML: "Junior developer with a lifelong passion for computer programming. Explore my portfolio to discover more about my skills!", welcome: "Welcome to my portfolio !", @@ -86,6 +88,33 @@ const en: Lang = { messageInfoCategoryCarCaptcha: "cats", messageInfoFirstCaptcha: "Select all the images containing", messageInfoLastCaptcha: "to prove you are not a robot.", + messagePageNotFoundH1: "Oops, page not found!", + messagePageNotFoundP: "The page you are looking for does not exist or has been moved.", + messagePageNotFoundButtom: "Return to home", + messagePageUnauthorizedH1 : "Oops, you're not authorized!", + messagePageUnauthorizedP : "It looks like you're trying to access a page or feature you dont have permission for.", + messagePageUnauthorizedButtom : "Return to home", + messagePageLoginTitle: "Login", + messagePageLoginInputEmail: "E-mail", + messagePageLoginInputPassword: "Password", + messagePageLoginInputButtom: "Log in", + messagePageLoginMessageSuccess : "Authentication successful. You are now logged in.", + messagePageLoginMessageErrorEmailOrPasswordIncorrect : "Incorrect email or password.", + messagePageLoginMessageErrorServer : "Server error: Please try again later.", + messagePageLoginMessageErrorUnexpected : "An unexpected error has occurred.", + messagePageChoicePasswordTitle: "Choose a password", + messagePageChoicePasswordOld: "Current password", + messagePageChoicePasswordNew: "New password", + messagePageChoicePasswordButton: "Validate", + messagePageForgotPasswordTitle: "Forgot Password", + messagePageForgotPasswordEmail: "E-mail", + messagePageForgotPasswordButton: "Send", + messagePageRegisterTitle: "Register", + messagePageRegisterEmail: "Email", + messagePageRegisterFirstName: "First Name", + messagePageRegisterLastName: "Last Name", + messagePageRegisterRole: "Role", + messagePageRegisterButtom: "Register", }; -export default en; +export default en; \ No newline at end of file diff --git a/frontend/src/lang/fr.tsx b/frontend/src/lang/fr.tsx index 02ade0da..ae6b151c 100644 --- a/frontend/src/lang/fr.tsx +++ b/frontend/src/lang/fr.tsx @@ -3,6 +3,8 @@ import Lang from "./typeLang"; const fr: Lang = { file: "fr", titleHTML: "Alexandre Renard - Développeur Fullstack | Portfolio", + titleHTMLNotFound: "Alexandre Renard - Développeur Fullstack | 404", + titleHTMLUnauthorizedAccess : "Accès non autorisé", descHTML: "Développeur junior passionné par l'informatique depuis l'enfance. Explorez mon Portfolio pour découvrir mes compétences !", welcome: "Bienvenue sur mon portfolio !", @@ -86,6 +88,33 @@ const fr: Lang = { messageInfoCategoryCarCaptcha: "voitures", messageInfoFirstCaptcha: "Sélectionnez toutes les images contenant des", messageInfoLastCaptcha: "pour prouver que vous n'êtes pas un robot.", + messagePageNotFoundH1: "Oups, page introuvable !", + messagePageNotFoundP: "La page que tu cherches n’existe pas ou a été déplacée.", + messagePageNotFoundButtom: "Retour à l’accueil", + messagePageUnauthorizedH1 : "Oups, vous n'avez pas l'autorisation !", + messagePageUnauthorizedP : "Il semble que vous essayez d'accéder à une page ou une fonctionnalité pour laquelle vous n'avez pas les droits nécessaires.", + messagePageUnauthorizedButtom : "Retour à l’accueil", + messagePageLoginTitle : "Connexion", + messagePageLoginInputEmail : "Email", + messagePageLoginInputPassword : "Mot de passe", + messagePageLoginInputButtom : "Se connecter", + messagePageLoginMessageSuccess : "Authentification réussie. Vous êtes maintenant connecté.", + messagePageLoginMessageErrorEmailOrPasswordIncorrect : "Email ou mot de passe incorrect.", + messagePageLoginMessageErrorServer : "Erreur serveur : veuillez réessayer plus tard.", + messagePageLoginMessageErrorUnexpected : "Une erreur inattendue est survenue.", + messagePageChoicePasswordTitle : "Choisir un mot de passe", + messagePageChoicePasswordOld : "Mot de passe actuel", + messagePageChoicePasswordNew : "Nouveau mot de passe", + messagePageChoicePasswordButton : "Valider", + messagePageForgotPasswordTitle : "Mot de passe oublié", + messagePageForgotPasswordEmail : "Email", + messagePageForgotPasswordButton : "Envoyer", + messagePageRegisterTitle : "Inscription", + messagePageRegisterEmail : "Email", + messagePageRegisterFirstName : "Prénom", + messagePageRegisterLastName : "Nom", + messagePageRegisterRole : "Rôle", + messagePageRegisterButtom : "S'inscrire", }; -export default fr; +export default fr; \ No newline at end of file diff --git a/frontend/src/lang/typeLang.tsx b/frontend/src/lang/typeLang.tsx index 5ac87d80..5d75230e 100644 --- a/frontend/src/lang/typeLang.tsx +++ b/frontend/src/lang/typeLang.tsx @@ -1,6 +1,8 @@ type Lang = { file: string; titleHTML: string; + titleHTMLNotFound : string; + titleHTMLUnauthorizedAccess : string; descHTML: string; welcome: string; theme1: string; @@ -69,6 +71,33 @@ type Lang = { messageInfoCategoryCarCaptcha: string; messageInfoFirstCaptcha: string; messageInfoLastCaptcha: string; + messagePageNotFoundH1 : string; + messagePageNotFoundP : string; + messagePageNotFoundButtom : string; + messagePageUnauthorizedH1 : string; + messagePageUnauthorizedP : string; + messagePageUnauthorizedButtom : string; + messagePageLoginTitle : string; + messagePageLoginInputEmail : string; + messagePageLoginInputPassword : string; + messagePageLoginInputButtom : string; + messagePageLoginMessageSuccess : string; + messagePageLoginMessageErrorEmailOrPasswordIncorrect : string; + messagePageLoginMessageErrorServer : string; + messagePageLoginMessageErrorUnexpected : string; + messagePageChoicePasswordTitle : string; + messagePageChoicePasswordOld : string; + messagePageChoicePasswordNew : string; + messagePageChoicePasswordButton : string; + messagePageForgotPasswordTitle : string; + messagePageForgotPasswordEmail : string; + messagePageForgotPasswordButton : string; + messagePageRegisterTitle : string; + messagePageRegisterEmail : string; + messagePageRegisterFirstName : string; + messagePageRegisterLastName : string; + messagePageRegisterRole : string; + messagePageRegisterButtom : string; }; -export default Lang; +export default Lang; \ No newline at end of file diff --git a/frontend/src/middleware.ts b/frontend/src/middleware.ts index 0b484512..94190315 100644 --- a/frontend/src/middleware.ts +++ b/frontend/src/middleware.ts @@ -1,32 +1,40 @@ import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; +import { jwtVerify } from "jose"; -function middleware (request: NextRequest): NextResponse { - return checkPath(request); -} +const SECRET_KEY = new TextEncoder().encode(process.env.JWT_SECRET); -const checkPath = (request: NextRequest) : NextResponse => { - // let response: NextResponse = NextResponse.next(); - - // if (request.nextUrl.pathname.startsWith("/")) { - // response = NextResponse.next(); - // } +export const config = { + matcher: ["/admin/:path*"], +}; - // if (!request.nextUrl.pathname.startsWith("/")) { - // response = NextResponse.redirect(new URL("/404", request.url)); - // } +export default async function middleware(request: NextRequest) { + const response = NextResponse.next(); + const token = request.cookies.get("token")?.value; - // return response; + if ( + request.nextUrl.pathname.startsWith("/admin/auth/login") || + request.nextUrl.pathname.startsWith("/admin/auth/forgotpassword") + ) { + return response; + } - if (request.nextUrl.pathname.startsWith("/") || request.nextUrl.pathname.startsWith("/404")) { - return NextResponse.next(); - } + if (!token) { + response.cookies.delete("token"); + return NextResponse.redirect(new URL("/admin/auth/login", request.url)); + } - return NextResponse.redirect(new URL("/404", request.url)); -} + try { + const { payload } = await jwtVerify(token, SECRET_KEY); -export const config = { - matcher: "/:path", -}; + if (typeof payload.role !== "string" || payload.role !== "admin") { + return NextResponse.redirect(new URL("/400", request.url)); + } -export default middleware; \ No newline at end of file + return response; + } catch (err) { + console.error("JWT error:", err); + response.cookies.delete("token"); + return NextResponse.redirect(new URL("/admin/auth/login", request.url)); + } +} \ No newline at end of file diff --git a/frontend/src/pages/400.tsx b/frontend/src/pages/400.tsx new file mode 100644 index 00000000..ffe6c2fe --- /dev/null +++ b/frontend/src/pages/400.tsx @@ -0,0 +1,53 @@ +import Link from "next/link"; +import { useLang } from "@/context/Lang/LangContext"; +import Head from "next/head"; +import { useEffect } from "react"; +import { Router, useRouter } from "next/router"; + +const Custom404 = (): React.ReactElement => { + const { translations } = useLang(); + + const router = useRouter(); + + useEffect(() => { + router.push("/400"); + }, []); + + return ( + <> + + {translations.titleHTMLUnauthorizedAccess} + +
+
+ + + 400 + + +

+ {translations.messagePageUnauthorizedH1} +

+

+ {translations.messagePageUnauthorizedP} +

+ + + +
+
+ + ); +}; + +export default Custom404; diff --git a/frontend/src/pages/404.tsx b/frontend/src/pages/404.tsx index 35e58387..8ae3b5a1 100644 --- a/frontend/src/pages/404.tsx +++ b/frontend/src/pages/404.tsx @@ -1,14 +1,53 @@ +import Link from "next/link"; +import { useLang } from "@/context/Lang/LangContext"; +import Head from "next/head"; import { useEffect } from "react"; -import { useRouter } from "next/router"; +import { Router, useRouter } from "next/router"; const Custom404 = (): React.ReactElement => { + const { translations } = useLang(); + const router = useRouter(); useEffect(() => { - router.push("/"); + router.push("/404"); }, []); - - return <>; + + return ( + <> + + {translations.titleHTMLNotFound} + +
+
+ + + 404 + + +

+ {translations.messagePageNotFoundH1} +

+

+ {translations.messagePageNotFoundP} +

+ + + +
+
+ + ); }; export default Custom404; diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 808e7a74..f2b9e213 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -18,6 +18,7 @@ import { import { API_URL } from "@/config"; import { setContext } from "@apollo/client/link/context"; import LoadingCustom from "@/components/Loading/LoadingCustom"; +import { UserProvider } from "@/context/UserContext/UserContext"; const App = ({ Component, pageProps }: AppProps): React.ReactElement => { const [client, setClient] = useState | null>(null); @@ -33,6 +34,7 @@ const App = ({ Component, pageProps }: AppProps): React.ReactElement => { const httpLink = new HttpLink({ uri: `${API_URL}`, + credentials: "include", }); const authLink = setContext((_, { headers }) => { @@ -60,17 +62,19 @@ const App = ({ Component, pageProps }: AppProps): React.ReactElement => { return ( - - - - - - - - - - - + + + + + + + + + + + + + ); diff --git a/frontend/src/pages/admin/auth/ChoicePassword.tsx b/frontend/src/pages/admin/auth/ChoicePassword.tsx new file mode 100644 index 00000000..c02d9fba --- /dev/null +++ b/frontend/src/pages/admin/auth/ChoicePassword.tsx @@ -0,0 +1,65 @@ +import { useState, ChangeEvent, FormEvent } from "react"; +import AuthFormLayout from "@/components/AuthFormLayout/AuthFormLayout"; +import ButtonCustom from "@/components/Button/Button"; +import InputField from "@/components/InputField/InputField"; +import { useLang } from "@/context/Lang/LangContext"; + +type ChoicePasswordFormState = { + password: string; + newPassword: string; +}; + +const ChoicePasswordPage = (): React.ReactElement => { + const { translations } = useLang(); + + const [form, setForm] = useState({ + password: "", + newPassword: "", + }); + + const handleChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setForm((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + console.log("Changement de mot de passe !", form); + }; + + return ( + +
+ + +
+ +
+ +
+ ); +}; + +export default ChoicePasswordPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/auth/forgotpassword.tsx b/frontend/src/pages/admin/auth/forgotpassword.tsx new file mode 100644 index 00000000..de6ac4f9 --- /dev/null +++ b/frontend/src/pages/admin/auth/forgotpassword.tsx @@ -0,0 +1,55 @@ +import { useState, ChangeEvent, FormEvent } from "react"; +import AuthFormLayout from "@/components/AuthFormLayout/AuthFormLayout"; +import ButtonCustom from "@/components/Button/Button"; +import InputField from "@/components/InputField/InputField"; +import { useLang } from "@/context/Lang/LangContext"; + +type ForgotPasswordFormState = { + email: string; +}; + +const ForgotPasswordPage = (): React.ReactElement => { + const { translations } = useLang(); + + const [form, setForm] = useState({ + email: "", + }); + + const handleChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setForm((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + console.log("Demande de réinitialisation envoyée !", form); + }; + + return ( + +
+ +
+ +
+ +
+ ); +}; + +export default ForgotPasswordPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/auth/login.tsx b/frontend/src/pages/admin/auth/login.tsx new file mode 100644 index 00000000..d9964962 --- /dev/null +++ b/frontend/src/pages/admin/auth/login.tsx @@ -0,0 +1,113 @@ +import { useState, ChangeEvent, FormEvent } from "react"; +import AuthFormLayout from "@/components/AuthFormLayout/AuthFormLayout"; +import ButtonCustom from "@/components/Button/Button"; +import { useLang } from "@/context/Lang/LangContext"; +import InputField from "@/components/InputField/InputField"; +import { useMutation } from "@apollo/client"; +import { + MutationDocument, + MutationMutation, + MutationMutationVariables, +} from "@/types/graphql"; +import CustomToast from "@/components/ToastCustom/CustomToast"; + +type LoginFormState = { + email: string; + password: string; +}; + +const LoginPage = (): React.ReactElement => { + + const { showAlert } = CustomToast(); + const { translations } = useLang(); + + const [form, setForm] = useState({ + email: "", + password: "", + }); + + const [login, { data, loading, error }] = useMutation< + MutationMutation, + MutationMutationVariables + >(MutationDocument); + + const handleChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setForm((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleLogin = async (e: FormEvent) => { + e.preventDefault(); + + try { + const res = await login({ + variables: { + data: { + email: form.email, + password: form.password, + }, + }, + }); + + const response = res.data?.login; + + if (response?.code === 200) { + // console.log("✅ Connexion réussie :", response.message); + showAlert("success", translations.messagePageLoginMessageSuccess); + } else if (response?.code === 401) { + // console.warn("❌ Identifiants invalides :", response.message); + showAlert("error", translations.messagePageLoginMessageErrorServer); + } else if (response?.code === 500) { + // console.error("❌ Erreur serveur :", response.message); + showAlert("error", translations.messagePageLoginMessageErrorUnexpected); + } else { + // console.warn("⚠️ Autre erreur :", response?.message); + showAlert("error", translations.messagePageLoginMessageErrorServer); + } + } catch (err) { + console.error("Erreur Apollo :", err); + showAlert("error", "Erreur serveur : veuillez réessayer plus tard."); + } + }; + + return ( + +
+ + + +
+ +
+ +
+ ); +}; + +export default LoginPage; \ No newline at end of file diff --git a/frontend/src/pages/admin/auth/register.tsx b/frontend/src/pages/admin/auth/register.tsx new file mode 100644 index 00000000..b952f64c --- /dev/null +++ b/frontend/src/pages/admin/auth/register.tsx @@ -0,0 +1,95 @@ +import { useState, ChangeEvent, FormEvent } from "react"; +import AuthFormLayout from "@/components/AuthFormLayout/AuthFormLayout"; +import ButtonCustom from "@/components/Button/Button"; +import InputField from "@/components/InputField/InputField"; +import { useLang } from "@/context/Lang/LangContext"; +import CustomSelect from "@/components/CustomSelect/CustomSelect"; + +type RegisterFormState = { + email: string; + prenom: string; + nom: string; + role: "admin" | "editor" | "view"; +}; + +const RegisterPage = (): React.ReactElement => { + const { translations } = useLang(); + + const [form, setForm] = useState({ + email: "", + prenom: "", + nom: "", + role: "view", + }); + + const handleChange = ( + e: ChangeEvent + ) => { + const { name, value } = e.target; + setForm((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleRoleChange = (event: any) => { + setForm((prev) => ({ + ...prev, + role: event.target.value as RegisterFormState["role"], + })); + }; + + const handleRegister = (e: FormEvent) => { + e.preventDefault(); + console.log("Register cliqué !", form); + }; + + return ( + +
+ + + + +
+ +
+ +
+ ); +}; + +export default RegisterPage; diff --git a/frontend/src/pages/admin/index.tsx b/frontend/src/pages/admin/index.tsx new file mode 100644 index 00000000..f244bf0f --- /dev/null +++ b/frontend/src/pages/admin/index.tsx @@ -0,0 +1,48 @@ +import { useUser } from "@/context/UserContext/UserContext"; +import { useEffect } from "react"; +import { useRouter } from "next/router"; + +const AdminDashboard = () => { + const { user, loading, error } = useUser(); + const router = useRouter(); + + useEffect(() => { + console.log("user avant if", user); + if (!loading) { + if (!user) { + console.log("user !user", user); + // router.push("/admin/auth/login"); + } else if (user.role !== "admin") { + console.log("user !== role", user); + // router.push("/400"); + } + } + }, [user, loading, router]); + +// if (loading) { +// return

Chargement...

; +// } + +// if (error) { +// return

Erreur : {error.message}

; +// } + +// if (!user || user.role !== "admin") { +// return null; +// } + + return ( +
+

Tableau de bord Admin

+

Bienvenue, {user?.firstname} {user?.lastname} !

+

Email : {user?.email}

+

Rôle : {user?.role}

+

Rôle : {user?.role}

+

Rôle : {user?.role}

+

Rôle : {user?.role}

+

Rôle : {user?.role}

+
+ ); +}; + +export default AdminDashboard; \ No newline at end of file diff --git a/frontend/src/requetes/mutations/login.mutations.ts b/frontend/src/requetes/mutations/login.mutations.ts new file mode 100644 index 00000000..f8de76e3 --- /dev/null +++ b/frontend/src/requetes/mutations/login.mutations.ts @@ -0,0 +1,11 @@ +import { gql } from "@apollo/client"; + +export const LOGIN = gql` +mutation Mutation($data: LoginInput!) { + login(data: $data) { + token + message + code + } +} +`; \ No newline at end of file diff --git a/frontend/src/requetes/queries/users.queries.ts b/frontend/src/requetes/queries/users.queries.ts new file mode 100644 index 00000000..d3853105 --- /dev/null +++ b/frontend/src/requetes/queries/users.queries.ts @@ -0,0 +1,14 @@ +import { gql } from "@apollo/client"; + +export const GET_ME = gql` + query GetMe { + me { + role + lastname + isPasswordChange + id + firstname + email + } + } +`; \ No newline at end of file diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index 70722ae8..a024238d 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -1,1405 +1,32 @@ -/* -! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.fixed { - position: fixed; -} - -.absolute { - position: absolute; -} - -.relative { - position: relative; -} - -.inset-0 { - inset: 0px; -} - -.inset-y-0 { - top: 0px; - bottom: 0px; -} - -.left-0 { - left: 0px; -} - -.left-1\/2 { - left: 50%; -} - -.right-0 { - right: 0px; -} - -.top-0 { - top: 0px; -} - -.top-1\/2 { - top: 50%; -} - -.z-10 { - z-index: 10; -} - -.z-20 { - z-index: 20; -} - -.z-40 { - z-index: 40; -} - -.z-50 { - z-index: 50; -} - -.m-1 { - margin: 0.25rem; -} - -.m-2 { - margin: 0.5rem; -} - -.m-3 { - margin: 0.75rem; -} - -.m-4 { - margin: 1rem; -} - -.m-5 { - margin: 1.25rem; -} - -.m-\[3\%\] { - margin: 3%; -} - -.mx-4 { - margin-left: 1rem; - margin-right: 1rem; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.-ms-\[5px\] { - margin-inline-start: -5px; -} - -.mb-1 { - margin-bottom: 0.25rem; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-6 { - margin-bottom: 1.5rem; -} - -.mb-\[0\.25rem\] { - margin-bottom: 0.25rem; -} - -.mb-\[5\%\] { - margin-bottom: 5%; -} - -.me-3 { - margin-inline-end: 0.75rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-14 { - margin-left: 3.5rem; -} - -.ml-2 { - margin-left: 0.5rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-6 { - margin-left: 1.5rem; -} - -.mr-2 { - margin-right: 0.5rem; -} - -.mr-3 { - margin-right: 0.75rem; -} - -.mr-3\.5 { - margin-right: 0.875rem; -} - -.mr-4 { - margin-right: 1rem; -} - -.ms-4 { - margin-inline-start: 1rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mt-5 { - margin-top: 1.25rem; -} - -.mt-8 { - margin-top: 2rem; -} - -.mt-\[-2\%\] { - margin-top: -2%; -} - -.mt-\[1\%\] { - margin-top: 1%; -} - -.mt-\[4\%\] { - margin-top: 4%; -} - -.mt-\[5\%\] { - margin-top: 5%; -} - -.mt-\[10\%\] { - margin-top: 10%; -} - -.mt-\[50\%\] { - margin-top: 50%; -} - -.mt-3 { - margin-top: 0.75rem; -} - -.mt-10 { - margin-top: 2.5rem; -} - -.block { - display: block; -} - -.inline-block { - display: inline-block; -} - -.flex { - display: flex; -} - -.hidden { - display: none; -} - -.h-1\/2 { - height: 50%; -} - -.h-40 { - height: 10rem; -} - -.h-52 { - height: 13rem; -} - -.h-6 { - height: 1.5rem; -} - -.h-\[100\%\] { - height: 100%; -} - -.h-\[170px\] { - height: 170px; -} - -.h-\[460px\] { - height: 460px; -} - -.h-\[9px\] { - height: 9px; -} - -.h-\[calc\(100vh-xpx\)\] { - height: calc(100vh - xpx); -} - -.h-auto { - height: auto; -} - -.h-full { - height: 100%; -} - -.h-screen { - height: 100vh; -} - -.max-h-0 { - max-height: 0px; -} - -.w-12 { - width: 3rem; -} - -.w-40 { - width: 10rem; -} - -.w-6 { - width: 1.5rem; -} - -.w-64 { - width: 16rem; -} - -.w-\[100\%\] { - width: 100%; -} - -.w-\[100vh\] { - width: 100vh; -} - -.w-\[25px\] { - width: 25px; -} - -.w-\[350px\] { - width: 350px; -} - -.w-\[9px\] { - width: 9px; -} - -.w-full { - width: 100%; -} - -.max-w-7xl { - max-width: 80rem; -} - -.max-w-\[160px\] { - max-width: 160px; -} - -.max-w-\[210px\] { - max-width: 210px; -} - -.max-w-\[310px\] { - max-width: 310px; -} - -.max-w-md { - max-width: 28rem; -} - -.flex-1 { - flex: 1 1 0%; -} - -.flex-shrink-0 { - flex-shrink: 0; -} - -.flex-grow { - flex-grow: 1; -} - -.-translate-x-1\/2 { - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.-translate-y-1\/2 { - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.translate-x-full { - --tw-translate-x: 100%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.rotate-180 { - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.cursor-not-allowed { - cursor: not-allowed; -} - -.cursor-pointer { - cursor: pointer; -} - -.resize { - resize: both; -} - -.flex-row { - flex-direction: row; -} - -.flex-col { - flex-direction: column; -} - -.flex-col-reverse { - flex-direction: column-reverse; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.flex-wrap-reverse { - flex-wrap: wrap-reverse; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-around { - justify-content: space-around; -} - -.space-x-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.75rem * var(--tw-space-x-reverse)); - margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1.25rem * var(--tw-space-x-reverse)); - margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-6 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); -} - -.overflow-hidden { - overflow: hidden; -} - -.overflow-x-auto { - overflow-x: auto; -} - -.overflow-y-auto { - overflow-y: auto; -} - -.overflow-y-hidden { - overflow-y: hidden; -} - -.whitespace-nowrap { - white-space: nowrap; -} - -.rounded-full { - border-radius: 9999px; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.rounded-md { - border-radius: 0.375rem; -} - -.border { - border-width: 1px; -} - -.border-4 { - border-width: 4px; -} - -.border-s { - border-inline-start-width: 1px; -} - -.border-none { - border-style: none; -} - -.border-gray-300 { - --tw-border-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-border-opacity)); -} - -.border-primary { - border-color: var(--primary-color); -} - -.border-secondary { - border-color: var(--secondary-color); -} - -.bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); -} - -.bg-body { - background-color: var(--body-color); -} - -.bg-footer { - background-color: var(--footer-color); -} - -.bg-gray-300 { - --tw-bg-opacity: 1; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)); -} - -.bg-primary { - background-color: var(--primary-color); -} - -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.p-0 { - padding: 0px; -} - -.p-1 { - padding: 0.25rem; -} - -.p-4 { - padding: 1rem; -} - -.p-5 { - padding: 1.25rem; -} - -.p-6 { - padding: 1.5rem; -} - -.p-8 { - padding: 2rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.py-6 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.pb-2 { - padding-bottom: 0.5rem; -} - -.pb-5 { - padding-bottom: 1.25rem; -} - -.pb-\[0\.25rem\] { - padding-bottom: 0.25rem; -} - -.pt-0 { - padding-top: 0px; -} - -.pt-0\.5 { - padding-top: 0.125rem; -} - -.pt-2 { - padding-top: 0.5rem; -} - -.pt-3 { - padding-top: 0.75rem; -} - -.text-center { - text-align: center; -} - -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - -.text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - -.font-bold { - font-weight: 700; -} - -.font-semibold { - font-weight: 600; -} - -.leading-normal { - line-height: 1.5; -} - -.text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); -} - -.text-primary { - color: var(--primary-color); -} - -.text-secondary { - color: var(--secondary-color); -} - -.text-text { - color: var(--text-color); -} - -.text-text200 { - color: var(--text200-color); -} - -.text-text300 { - color: var(--text300-color); -} - -.text-textButton { - color: var(--textButton-color); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.opacity-0 { - opacity: 0; -} - -.opacity-100 { - opacity: 1; -} - -.opacity-50 { - opacity: 0.5; -} - -.opacity-\[50\%\] { - opacity: 50%; -} - -.shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-inner { - --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.outline { - outline-style: solid; -} - -.blur { - --tw-blur: blur(8px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-transform { - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-300 { - transition-duration: 300ms; -} - -.duration-500 { - transition-duration: 500ms; -} - -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} +@tailwind base; +@tailwind components; +@tailwind utilities; :root { - --primary-color: #B45852; - --secondary-color: #DFBB5F; - --scrollHandle-color: #19252E; - --scrollHandleHover-color: #162028; - --body-color: #01031B; - --grey-color: #7F7F7F; - --placeholder-color: #A0AEC0; - --text-color: #ffffff; - --text100-color: #030303; - --text200-color: #B2BDCC; - --text300-color: #64748b; - --textButton-color: white; - --success-color: #1C8036; - --error-color: #AA2020; - --warn-color: #EBCC2A; - --info-color: #3B89FF; - --footer-color: #050F1A; + --primary-color: #B45852; + --secondary-color: #DFBB5F; + --scrollHandle-color: #19252E; + --scrollHandleHover-color: #162028; + --body-color: #01031B; + --grey-color: #7F7F7F; + --placeholder-color: #A0AEC0; + --text-color: #ffffff; + --text100-color: #030303; + --text200-color: #B2BDCC; + --text300-color: #64748b; + --textButton-color: white; + --success-color: #1C8036; + --error-color: #AA2020; + --warn-color: #EBCC2A; + --info-color: #3B89FF; + --footer-color: #050F1A; } body { background-color: var(--body-color); } + /* .custom-scrollbar::-webkit-scrollbar { display: none; color: red; @@ -1411,63 +38,57 @@ body { } */ /* Cacher la barre de défilement par défaut */ - .custom-scrollbar { - overflow-y: hidden; - /* Désactiver le défilement vertical */ + overflow-y: hidden; /* Désactiver le défilement vertical */ } .custom-scrollbar:hover { - overflow-y: auto; - /* Activer le défilement vertical au survol */ + overflow-y: auto; /* Activer le défilement vertical au survol */ } .custom-scrollbar::-webkit-scrollbar { - width: 0; - /* Masquer la barre de défilement */ + width: 0; /* Masquer la barre de défilement */ height: 0; } .custom-scrollbar:hover::-webkit-scrollbar { - width: 2px; - /* Afficher la barre de défilement au survol */ + width: 2px; /* Afficher la barre de défilement au survol */ height: 2px; } -@keyframes expandOpen { - from { - opacity: 0; - max-height: 0; - } - - to { - opacity: 1; - max-height: 500px; - } -} - -@keyframes expandClose { - from { - opacity: 1; - max-height: 500px; - } - - to { - opacity: 0; - max-height: 0; - } -} - -.expanded-animation-open { - animation: expandOpen 1.0s ease-in-out forwards; -} - -.expanded-animation-close { - animation: expandClose 1.0s ease-in-out forwards; -} -.MuiCardContent-root { - padding: 0px; +@keyframes expandOpen { + from { + opacity: 0; + max-height: 0; + } + to { + opacity: 1; + max-height: 500px; + } + } + + @keyframes expandClose { + from { + opacity: 1; + max-height: 500px; + } + to { + opacity: 0; + max-height: 0; + } + } + + .expanded-animation-open { + animation: expandOpen 1.0s ease-in-out forwards; + } + + .expanded-animation-close { + animation: expandClose 1.0s ease-in-out forwards; + } + + .MuiCardContent-root { + padding: 0px; } ::-webkit-scrollbar { @@ -1519,8 +140,7 @@ body { .custom-video { width: 310px; height: 170px; - -o-object-fit: cover; - object-fit: cover; + object-fit: cover; } .Toastify__toast-container { @@ -1554,177 +174,6 @@ body { color: var(--text-button-color); } -.hover\:bg-secondary:hover { - background-color: var(--secondary-color); -} - -.hover\:text-secondary:hover { - color: var(--secondary-color); -} - -.hover\:text-text100:hover { - color: var(--text100-color); -} - -.hover\:opacity-\[75\%\]:hover { - opacity: 75%; -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -@media (min-width: 640px) { - .sm\:ml-3 { - margin-left: 0.75rem; - } - - .sm\:max-w-\[80\%\] { - max-width: 80%; - } - - .sm\:max-w-\[90\%\] { - max-width: 90%; - } -} - -@media (min-width: 768px) { - .md\:-mt-\[5px\] { - margin-top: -5px; - } - - .md\:mb-0 { - margin-bottom: 0px; - } - - .md\:me-0 { - margin-inline-end: 0px; - } - - .md\:ml-4 { - margin-left: 1rem; - } - - .md\:ms-0 { - margin-inline-start: 0px; - } - - .md\:block { - display: block; - } - - .md\:inline-block { - display: inline-block; - } - - .md\:inline { - display: inline; - } - - .md\:flex { - display: flex; - } - - .md\:hidden { - display: none; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:w-\[70\%\] { - width: 70%; - } - - .md\:max-w-\[60\%\] { - max-width: 60%; - } - - .md\:max-w-\[75\%\] { - max-width: 75%; - } - - .md\:justify-center { - justify-content: center; - } - - .md\:gap-6 { - gap: 1.5rem; - } - - .md\:border-s-0 { - border-inline-start-width: 0px; - } - - .md\:border-t { - border-top-width: 1px; - } - - .md\:pt-0 { - padding-top: 0px; - } - - .md\:text-base { - font-size: 1rem; - line-height: 1.5rem; - } -} - -@media (min-width: 1024px) { - .lg\:ml-6 { - margin-left: 1.5rem; - } - - .lg\:block { - display: block; - } - - .lg\:inline-block { - display: inline-block; - } - - .lg\:hidden { - display: none; - } - - .lg\:w-\[70\%\] { - width: 70%; - } - - .lg\:max-w-\[50\%\] { - max-width: 50%; - } - - .lg\:max-w-\[60\%\] { - max-width: 60%; - } - - .lg\:text-6xl { - font-size: 3.75rem; - line-height: 1; - } -} - -@media (min-width: 1280px) { - .xl\:block { - display: block; - } - - .xl\:hidden { - display: none; - } - - .xl\:max-w-\[35\%\] { - max-width: 35%; - } - - .xl\:max-w-\[40\%\] { - max-width: 40%; - } - - .xl\:max-w-\[50\%\] { - max-width: 50%; - } +html { + scroll-behavior: smooth; } \ No newline at end of file diff --git a/frontend/src/styles/output.css b/frontend/src/styles/output.css index 70722ae8..9326ed84 100644 --- a/frontend/src/styles/output.css +++ b/frontend/src/styles/output.css @@ -1,1730 +1 @@ -/* -! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.fixed { - position: fixed; -} - -.absolute { - position: absolute; -} - -.relative { - position: relative; -} - -.inset-0 { - inset: 0px; -} - -.inset-y-0 { - top: 0px; - bottom: 0px; -} - -.left-0 { - left: 0px; -} - -.left-1\/2 { - left: 50%; -} - -.right-0 { - right: 0px; -} - -.top-0 { - top: 0px; -} - -.top-1\/2 { - top: 50%; -} - -.z-10 { - z-index: 10; -} - -.z-20 { - z-index: 20; -} - -.z-40 { - z-index: 40; -} - -.z-50 { - z-index: 50; -} - -.m-1 { - margin: 0.25rem; -} - -.m-2 { - margin: 0.5rem; -} - -.m-3 { - margin: 0.75rem; -} - -.m-4 { - margin: 1rem; -} - -.m-5 { - margin: 1.25rem; -} - -.m-\[3\%\] { - margin: 3%; -} - -.mx-4 { - margin-left: 1rem; - margin-right: 1rem; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.-ms-\[5px\] { - margin-inline-start: -5px; -} - -.mb-1 { - margin-bottom: 0.25rem; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mb-6 { - margin-bottom: 1.5rem; -} - -.mb-\[0\.25rem\] { - margin-bottom: 0.25rem; -} - -.mb-\[5\%\] { - margin-bottom: 5%; -} - -.me-3 { - margin-inline-end: 0.75rem; -} - -.ml-1 { - margin-left: 0.25rem; -} - -.ml-14 { - margin-left: 3.5rem; -} - -.ml-2 { - margin-left: 0.5rem; -} - -.ml-3 { - margin-left: 0.75rem; -} - -.ml-6 { - margin-left: 1.5rem; -} - -.mr-2 { - margin-right: 0.5rem; -} - -.mr-3 { - margin-right: 0.75rem; -} - -.mr-3\.5 { - margin-right: 0.875rem; -} - -.mr-4 { - margin-right: 1rem; -} - -.ms-4 { - margin-inline-start: 1rem; -} - -.mt-2 { - margin-top: 0.5rem; -} - -.mt-4 { - margin-top: 1rem; -} - -.mt-5 { - margin-top: 1.25rem; -} - -.mt-8 { - margin-top: 2rem; -} - -.mt-\[-2\%\] { - margin-top: -2%; -} - -.mt-\[1\%\] { - margin-top: 1%; -} - -.mt-\[4\%\] { - margin-top: 4%; -} - -.mt-\[5\%\] { - margin-top: 5%; -} - -.mt-\[10\%\] { - margin-top: 10%; -} - -.mt-\[50\%\] { - margin-top: 50%; -} - -.mt-3 { - margin-top: 0.75rem; -} - -.mt-10 { - margin-top: 2.5rem; -} - -.block { - display: block; -} - -.inline-block { - display: inline-block; -} - -.flex { - display: flex; -} - -.hidden { - display: none; -} - -.h-1\/2 { - height: 50%; -} - -.h-40 { - height: 10rem; -} - -.h-52 { - height: 13rem; -} - -.h-6 { - height: 1.5rem; -} - -.h-\[100\%\] { - height: 100%; -} - -.h-\[170px\] { - height: 170px; -} - -.h-\[460px\] { - height: 460px; -} - -.h-\[9px\] { - height: 9px; -} - -.h-\[calc\(100vh-xpx\)\] { - height: calc(100vh - xpx); -} - -.h-auto { - height: auto; -} - -.h-full { - height: 100%; -} - -.h-screen { - height: 100vh; -} - -.max-h-0 { - max-height: 0px; -} - -.w-12 { - width: 3rem; -} - -.w-40 { - width: 10rem; -} - -.w-6 { - width: 1.5rem; -} - -.w-64 { - width: 16rem; -} - -.w-\[100\%\] { - width: 100%; -} - -.w-\[100vh\] { - width: 100vh; -} - -.w-\[25px\] { - width: 25px; -} - -.w-\[350px\] { - width: 350px; -} - -.w-\[9px\] { - width: 9px; -} - -.w-full { - width: 100%; -} - -.max-w-7xl { - max-width: 80rem; -} - -.max-w-\[160px\] { - max-width: 160px; -} - -.max-w-\[210px\] { - max-width: 210px; -} - -.max-w-\[310px\] { - max-width: 310px; -} - -.max-w-md { - max-width: 28rem; -} - -.flex-1 { - flex: 1 1 0%; -} - -.flex-shrink-0 { - flex-shrink: 0; -} - -.flex-grow { - flex-grow: 1; -} - -.-translate-x-1\/2 { - --tw-translate-x: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.-translate-y-1\/2 { - --tw-translate-y: -50%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.translate-x-full { - --tw-translate-x: 100%; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.rotate-180 { - --tw-rotate: 180deg; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.transform { - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - -.cursor-not-allowed { - cursor: not-allowed; -} - -.cursor-pointer { - cursor: pointer; -} - -.resize { - resize: both; -} - -.flex-row { - flex-direction: row; -} - -.flex-col { - flex-direction: column; -} - -.flex-col-reverse { - flex-direction: column-reverse; -} - -.flex-wrap { - flex-wrap: wrap; -} - -.flex-wrap-reverse { - flex-wrap: wrap-reverse; -} - -.items-center { - align-items: center; -} - -.justify-center { - justify-content: center; -} - -.justify-between { - justify-content: space-between; -} - -.justify-around { - justify-content: space-around; -} - -.space-x-3 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.75rem * var(--tw-space-x-reverse)); - margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-x-5 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1.25rem * var(--tw-space-x-reverse)); - margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); -} - -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-6 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); -} - -.overflow-hidden { - overflow: hidden; -} - -.overflow-x-auto { - overflow-x: auto; -} - -.overflow-y-auto { - overflow-y: auto; -} - -.overflow-y-hidden { - overflow-y: hidden; -} - -.whitespace-nowrap { - white-space: nowrap; -} - -.rounded-full { - border-radius: 9999px; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.rounded-md { - border-radius: 0.375rem; -} - -.border { - border-width: 1px; -} - -.border-4 { - border-width: 4px; -} - -.border-s { - border-inline-start-width: 1px; -} - -.border-none { - border-style: none; -} - -.border-gray-300 { - --tw-border-opacity: 1; - border-color: rgb(209 213 219 / var(--tw-border-opacity)); -} - -.border-primary { - border-color: var(--primary-color); -} - -.border-secondary { - border-color: var(--secondary-color); -} - -.bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); -} - -.bg-body { - background-color: var(--body-color); -} - -.bg-footer { - background-color: var(--footer-color); -} - -.bg-gray-300 { - --tw-bg-opacity: 1; - background-color: rgb(209 213 219 / var(--tw-bg-opacity)); -} - -.bg-primary { - background-color: var(--primary-color); -} - -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.object-cover { - -o-object-fit: cover; - object-fit: cover; -} - -.p-0 { - padding: 0px; -} - -.p-1 { - padding: 0.25rem; -} - -.p-4 { - padding: 1rem; -} - -.p-5 { - padding: 1.25rem; -} - -.p-6 { - padding: 1.5rem; -} - -.p-8 { - padding: 2rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.px-5 { - padding-left: 1.25rem; - padding-right: 1.25rem; -} - -.py-1 { - padding-top: 0.25rem; - padding-bottom: 0.25rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.py-6 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.pb-2 { - padding-bottom: 0.5rem; -} - -.pb-5 { - padding-bottom: 1.25rem; -} - -.pb-\[0\.25rem\] { - padding-bottom: 0.25rem; -} - -.pt-0 { - padding-top: 0px; -} - -.pt-0\.5 { - padding-top: 0.125rem; -} - -.pt-2 { - padding-top: 0.5rem; -} - -.pt-3 { - padding-top: 0.75rem; -} - -.text-center { - text-align: center; -} - -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - -.text-4xl { - font-size: 2.25rem; - line-height: 2.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - -.font-bold { - font-weight: 700; -} - -.font-semibold { - font-weight: 600; -} - -.leading-normal { - line-height: 1.5; -} - -.text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); -} - -.text-primary { - color: var(--primary-color); -} - -.text-secondary { - color: var(--secondary-color); -} - -.text-text { - color: var(--text-color); -} - -.text-text200 { - color: var(--text200-color); -} - -.text-text300 { - color: var(--text300-color); -} - -.text-textButton { - color: var(--textButton-color); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.opacity-0 { - opacity: 0; -} - -.opacity-100 { - opacity: 1; -} - -.opacity-50 { - opacity: 0.5; -} - -.opacity-\[50\%\] { - opacity: 50%; -} - -.shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-inner { - --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-lg { - --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.outline { - outline-style: solid; -} - -.blur { - --tw-blur: blur(8px); - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.filter { - filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -} - -.transition { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-opacity { - transition-property: opacity; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.transition-transform { - transition-property: transform; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -.duration-300 { - transition-duration: 300ms; -} - -.duration-500 { - transition-duration: 500ms; -} - -.ease-in-out { - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -:root { - --primary-color: #B45852; - --secondary-color: #DFBB5F; - --scrollHandle-color: #19252E; - --scrollHandleHover-color: #162028; - --body-color: #01031B; - --grey-color: #7F7F7F; - --placeholder-color: #A0AEC0; - --text-color: #ffffff; - --text100-color: #030303; - --text200-color: #B2BDCC; - --text300-color: #64748b; - --textButton-color: white; - --success-color: #1C8036; - --error-color: #AA2020; - --warn-color: #EBCC2A; - --info-color: #3B89FF; - --footer-color: #050F1A; -} - -body { - background-color: var(--body-color); -} - -/* .custom-scrollbar::-webkit-scrollbar { - display: none; - color: red; -} - -.custom-scrollbar::-webkit-scrollbar:hover { - display: block; - color: red; -} */ - -/* Cacher la barre de défilement par défaut */ - -.custom-scrollbar { - overflow-y: hidden; - /* Désactiver le défilement vertical */ -} - -.custom-scrollbar:hover { - overflow-y: auto; - /* Activer le défilement vertical au survol */ -} - -.custom-scrollbar::-webkit-scrollbar { - width: 0; - /* Masquer la barre de défilement */ - height: 0; -} - -.custom-scrollbar:hover::-webkit-scrollbar { - width: 2px; - /* Afficher la barre de défilement au survol */ - height: 2px; -} - -@keyframes expandOpen { - from { - opacity: 0; - max-height: 0; - } - - to { - opacity: 1; - max-height: 500px; - } -} - -@keyframes expandClose { - from { - opacity: 1; - max-height: 500px; - } - - to { - opacity: 0; - max-height: 0; - } -} - -.expanded-animation-open { - animation: expandOpen 1.0s ease-in-out forwards; -} - -.expanded-animation-close { - animation: expandClose 1.0s ease-in-out forwards; -} - -.MuiCardContent-root { - padding: 0px; -} - -::-webkit-scrollbar { - width: 10px; -} - -::-webkit-scrollbar-track { - background: var(--body-color); -} - -::-webkit-scrollbar-thumb { - background: var(--scrollHandle-color); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--scrollHandleHover-color); -} - -.emulator { - align-items: center; - display: flex; - justify-content: center; - /* height: calc(100vh - 100px); */ - /* height: calc(100vh - 20px); */ - width: 100vh ; - overflow: auto; - /* padding: 10px; */ -} - -.inputTerminal { - background-color: var(--body-color); - color: var(--text-color); - caret-color: var(--primary-color); - margin-left: 5px; - width: 15vh; -} - -.inputTerminal:focus-visible { - outline: none; -} - -.video-container { - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; -} - -.custom-video { - width: 310px; - height: 170px; - -o-object-fit: cover; - object-fit: cover; -} - -.Toastify__toast-container { - font-family: Arial, sans-serif; -} - -.Toastify__toast { - border-radius: 4px; -} - -.Toastify__toast--success .Toastify__icon { - color: var(--success-color); -} - -.Toastify__toast--error .Toastify__icon { - color: var(--error-color); -} - -.Toastify__toast--info .Toastify__icon { - color: var(--info-color); -} - -.Toastify__toast--warn .Toastify__icon { - color: var(--warn-color); -} - -.Toastify__toast--success .Toastify__close-button, -.Toastify__toast--error .Toastify__close-button, -.Toastify__toast--info .Toastify__close-button, -.Toastify__toast--warn .Toastify__close-button { - color: var(--text-button-color); -} - -.hover\:bg-secondary:hover { - background-color: var(--secondary-color); -} - -.hover\:text-secondary:hover { - color: var(--secondary-color); -} - -.hover\:text-text100:hover { - color: var(--text100-color); -} - -.hover\:opacity-\[75\%\]:hover { - opacity: 75%; -} - -.focus\:outline-none:focus { - outline: 2px solid transparent; - outline-offset: 2px; -} - -@media (min-width: 640px) { - .sm\:ml-3 { - margin-left: 0.75rem; - } - - .sm\:max-w-\[80\%\] { - max-width: 80%; - } - - .sm\:max-w-\[90\%\] { - max-width: 90%; - } -} - -@media (min-width: 768px) { - .md\:-mt-\[5px\] { - margin-top: -5px; - } - - .md\:mb-0 { - margin-bottom: 0px; - } - - .md\:me-0 { - margin-inline-end: 0px; - } - - .md\:ml-4 { - margin-left: 1rem; - } - - .md\:ms-0 { - margin-inline-start: 0px; - } - - .md\:block { - display: block; - } - - .md\:inline-block { - display: inline-block; - } - - .md\:inline { - display: inline; - } - - .md\:flex { - display: flex; - } - - .md\:hidden { - display: none; - } - - .md\:w-1\/2 { - width: 50%; - } - - .md\:w-\[70\%\] { - width: 70%; - } - - .md\:max-w-\[60\%\] { - max-width: 60%; - } - - .md\:max-w-\[75\%\] { - max-width: 75%; - } - - .md\:justify-center { - justify-content: center; - } - - .md\:gap-6 { - gap: 1.5rem; - } - - .md\:border-s-0 { - border-inline-start-width: 0px; - } - - .md\:border-t { - border-top-width: 1px; - } - - .md\:pt-0 { - padding-top: 0px; - } - - .md\:text-base { - font-size: 1rem; - line-height: 1.5rem; - } -} - -@media (min-width: 1024px) { - .lg\:ml-6 { - margin-left: 1.5rem; - } - - .lg\:block { - display: block; - } - - .lg\:inline-block { - display: inline-block; - } - - .lg\:hidden { - display: none; - } - - .lg\:w-\[70\%\] { - width: 70%; - } - - .lg\:max-w-\[50\%\] { - max-width: 50%; - } - - .lg\:max-w-\[60\%\] { - max-width: 60%; - } - - .lg\:text-6xl { - font-size: 3.75rem; - line-height: 1; - } -} - -@media (min-width: 1280px) { - .xl\:block { - display: block; - } - - .xl\:hidden { - display: none; - } - - .xl\:max-w-\[35\%\] { - max-width: 35%; - } - - .xl\:max-w-\[40\%\] { - max-width: 40%; - } - - .xl\:max-w-\[50\%\] { - max-width: 50%; - } -} \ No newline at end of file +/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.left-0{left:0}.left-1\/2{left:50%}.right-0{right:0}.top-0{top:0}.top-1\/2{top:50%}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-3{margin:.75rem}.m-5{margin:1.25rem}.m-\[3\%\]{margin:3%}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-\[0\.25rem\]{margin-bottom:.25rem}.ml-1{margin-left:.25rem}.ml-14{margin-left:3.5rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-3\.5{margin-right:.875rem}.mr-4{margin-right:1rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-\[-2\%\]{margin-top:-2%}.mt-\[1\%\]{margin-top:1%}.mt-\[64px\]{margin-top:64px}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.hidden{display:none}.h-1\/2{height:50%}.h-4{height:1rem}.h-40{height:10rem}.h-52{height:13rem}.h-6{height:1.5rem}.h-\[100\%\]{height:100%}.h-\[170px\]{height:170px}.h-\[460px\]{height:460px}.h-\[calc\(100vh-xpx\)\]{height:calc(100vh - xpx)}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-0{max-height:0}.min-h-\[calc\(100vh-64px\)\]{min-height:calc(100vh - 64px)}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-4{width:1rem}.w-40{width:10rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-\[100\%\]{width:100%}.w-\[100vh\]{width:100vh}.w-\[25px\]{width:25px}.w-\[350px\]{width:350px}.w-full{width:100%}.max-w-7xl{max-width:80rem}.max-w-\[160px\]{max-width:160px}.max-w-\[210px\]{max-width:210px}.max-w-\[310px\]{max-width:310px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-full{--tw-translate-x:100%}.rotate-180,.translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem*var(--tw-space-x-reverse));margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-4{border-width:4px}.border-l-2{border-left-width:2px}.border-none{border-style:none}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-primary{border-color:var(--primary-color)}.border-secondary{border-color:var(--secondary-color)}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-body{background-color:var(--body-color)}.bg-footer{background-color:var(--footer-color)}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.bg-primary{background-color:var(--primary-color)}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-2{padding-bottom:.5rem}.pb-\[0\.25rem\]{padding-bottom:.25rem}.pl-8{padding-left:2rem}.pt-0{padding-top:0}.pt-0\.5{padding-top:.125rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-center{text-align:center}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-semibold{font-weight:600}.leading-normal{line-height:1.5}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-primary{color:var(--primary-color)}.text-secondary{color:var(--secondary-color)}.text-text{color:var(--text-color)}.text-text200{color:var(--text200-color)}.text-text300{color:var(--text300-color)}.text-textButton{color:var(--textButton-color)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-\[50\%\]{opacity:50%}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-inner{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 #0000000d;--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.outline{outline-style:solid}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}:root{--primary-color:#b45852;--secondary-color:#dfbb5f;--scrollHandle-color:#19252e;--scrollHandleHover-color:#162028;--body-color:#01031b;--grey-color:#7f7f7f;--placeholder-color:#a0aec0;--text-color:#fff;--text100-color:#030303;--text200-color:#b2bdcc;--text300-color:#64748b;--textButton-color:#fff;--success-color:#1c8036;--error-color:#aa2020;--warn-color:#ebcc2a;--info-color:#3b89ff;--footer-color:#050f1a}body{background-color:var(--body-color)}.custom-scrollbar{overflow-y:hidden}.custom-scrollbar:hover{overflow-y:auto}.custom-scrollbar::-webkit-scrollbar{width:0;height:0}.custom-scrollbar:hover::-webkit-scrollbar{width:2px;height:2px}@keyframes expandOpen{0%{opacity:0;max-height:0}to{opacity:1;max-height:500px}}@keyframes expandClose{0%{opacity:1;max-height:500px}to{opacity:0;max-height:0}}.expanded-animation-open{animation:expandOpen 1s ease-in-out forwards}.expanded-animation-close{animation:expandClose 1s ease-in-out forwards}.MuiCardContent-root{padding:0}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-track{background:var(--body-color)}::-webkit-scrollbar-thumb{background:var(--scrollHandle-color)}::-webkit-scrollbar-thumb:hover{background:var(--scrollHandleHover-color)}.emulator{align-items:center;display:flex;justify-content:center;width:100vh;overflow:auto}.inputTerminal{background-color:var(--body-color);color:var(--text-color);caret-color:var(--primary-color);margin-left:5px;width:15vh}.inputTerminal:focus-visible{outline:none}.video-container{display:flex;justify-content:center;align-items:center;overflow:hidden}.custom-video{width:310px;height:170px;-o-object-fit:cover;object-fit:cover}.Toastify__toast-container{font-family:Arial,sans-serif}.Toastify__toast{border-radius:4px}.Toastify__toast--success .Toastify__icon{color:var(--success-color)}.Toastify__toast--error .Toastify__icon{color:var(--error-color)}.Toastify__toast--info .Toastify__icon{color:var(--info-color)}.Toastify__toast--warn .Toastify__icon{color:var(--warn-color)}.Toastify__toast--error .Toastify__close-button,.Toastify__toast--info .Toastify__close-button,.Toastify__toast--success .Toastify__close-button,.Toastify__toast--warn .Toastify__close-button{color:var(--text-button-color)}html{scroll-behavior:smooth}.hover\:bg-secondary:hover{background-color:var(--secondary-color)}.hover\:text-secondary:hover{color:var(--secondary-color)}.hover\:text-text100:hover{color:var(--text100-color)}.hover\:opacity-\[75\%\]:hover{opacity:75%}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}@media (min-width:640px){.sm\:ml-3{margin-left:.75rem}.sm\:max-w-\[80\%\]{max-width:80%}.sm\:max-w-\[90\%\]{max-width:90%}}@media (min-width:768px){.md\:left-auto{left:auto}.md\:top-0{top:0}.md\:mx-auto{margin-left:auto;margin-right:auto}.md\:mb-0{margin-bottom:0}.md\:ml-4{margin-left:1rem}.md\:block{display:block}.md\:inline-block{display:inline-block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-1\/2{width:50%}.md\:w-\[70\%\]{width:70%}.md\:max-w-\[60\%\]{max-width:60%}.md\:max-w-\[75\%\]{max-width:75%}.md\:-translate-y-1\/2{--tw-translate-y:-50%}.md\:-translate-y-1\/2,.md\:translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.md\:translate-x-0{--tw-translate-x:0px}.md\:justify-center{justify-content:center}.md\:gap-6{gap:1.5rem}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.md\:border-l-0{border-left-width:0}.md\:border-t{border-top-width:1px}.md\:pl-0{padding-left:0}.md\:pt-8{padding-top:2rem}}@media (min-width:1024px){.lg\:ml-6{margin-left:1.5rem}.lg\:block{display:block}.lg\:inline-block{display:inline-block}.lg\:hidden{display:none}.lg\:w-\[70\%\]{width:70%}.lg\:max-w-\[50\%\]{max-width:50%}.lg\:max-w-\[60\%\]{max-width:60%}.lg\:text-6xl{font-size:3.75rem;line-height:1}}@media (min-width:1280px){.xl\:block{display:block}.xl\:hidden{display:none}.xl\:max-w-\[35\%\]{max-width:35%}.xl\:max-w-\[40\%\]{max-width:40%}.xl\:max-w-\[50\%\]{max-width:50%}} \ No newline at end of file diff --git a/frontend/src/types/graphql.ts b/frontend/src/types/graphql.ts index f5d2ac70..8d903b89 100644 --- a/frontend/src/types/graphql.ts +++ b/frontend/src/types/graphql.ts @@ -416,6 +416,7 @@ export type Query = { generateCaptcha: CaptchaResponse; getGlobalStats: GlobalStatsResponse; listBackupFiles: BackupFilesResponse; + me?: Maybe; projectById: ProjectResponse; projectList: ProjectsResponse; skillList: CategoryResponse; @@ -581,6 +582,13 @@ export type SendContactMutationVariables = Exact<{ export type SendContactMutation = { __typename?: 'Mutation', sendContact: { __typename?: 'MessageType', label: string, message: string, status: boolean } }; +export type MutationMutationVariables = Exact<{ + data: LoginInput; +}>; + + +export type MutationMutation = { __typename?: 'Mutation', login: { __typename?: 'LoginResponse', token?: string | null, message: string, code: number } }; + export type GenerateCaptchaQueryVariables = Exact<{ [key: string]: never; }>; @@ -606,6 +614,11 @@ export type GetSkillsListQueryVariables = Exact<{ [key: string]: never; }>; export type GetSkillsListQuery = { __typename?: 'Query', skillList: { __typename?: 'CategoryResponse', code: number, message: string, categories?: Array<{ __typename?: 'Skill', categoryFR: string, id: string, categoryEN: string, skills: Array<{ __typename?: 'SkillSubItem', categoryId: number, id: string, image: string, name: string }> }> | null } }; +export type GetMeQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetMeQuery = { __typename?: 'Query', me?: { __typename?: 'User', role: Role, lastname: string, isPasswordChange: boolean, id: string, firstname: string, email: string } | null }; + export const ValidateCaptchaDocument = gql` mutation ValidateCaptcha($challengeType: String!, $selectedIndices: [Float!]!, $idCaptcha: String!) { @@ -712,6 +725,41 @@ export function useSendContactMutation(baseOptions?: Apollo.MutationHookOptions< export type SendContactMutationHookResult = ReturnType; export type SendContactMutationResult = Apollo.MutationResult; export type SendContactMutationOptions = Apollo.BaseMutationOptions; +export const MutationDocument = gql` + mutation Mutation($data: LoginInput!) { + login(data: $data) { + token + message + code + } +} + `; +export type MutationMutationFn = Apollo.MutationFunction; + +/** + * __useMutationMutation__ + * + * To run a mutation, you first call `useMutationMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useMutationMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [mutationMutation, { data, loading, error }] = useMutationMutation({ + * variables: { + * data: // value for 'data' + * }, + * }); + */ +export function useMutationMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(MutationDocument, options); + } +export type MutationMutationHookResult = ReturnType; +export type MutationMutationResult = Apollo.MutationResult; +export type MutationMutationOptions = Apollo.BaseMutationOptions; export const GenerateCaptchaDocument = gql` query generateCaptcha { generateCaptcha { @@ -979,4 +1027,48 @@ export function useGetSkillsListSuspenseQuery(baseOptions?: Apollo.SuspenseQuery export type GetSkillsListQueryHookResult = ReturnType; export type GetSkillsListLazyQueryHookResult = ReturnType; export type GetSkillsListSuspenseQueryHookResult = ReturnType; -export type GetSkillsListQueryResult = Apollo.QueryResult; \ No newline at end of file +export type GetSkillsListQueryResult = Apollo.QueryResult; +export const GetMeDocument = gql` + query GetMe { + me { + role + lastname + isPasswordChange + id + firstname + email + } +} + `; + +/** + * __useGetMeQuery__ + * + * To run a query within a React component, call `useGetMeQuery` and pass it any options that fit your needs. + * When your component renders, `useGetMeQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetMeQuery({ + * variables: { + * }, + * }); + */ +export function useGetMeQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GetMeDocument, options); + } +export function useGetMeLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GetMeDocument, options); + } +export function useGetMeSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(GetMeDocument, options); + } +export type GetMeQueryHookResult = ReturnType; +export type GetMeLazyQueryHookResult = ReturnType; +export type GetMeSuspenseQueryHookResult = ReturnType; +export type GetMeQueryResult = Apollo.QueryResult; \ No newline at end of file