resolve conflict
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
VITE_API_URL=
|
||||||
@@ -25,3 +25,5 @@ dist-ssr
|
|||||||
|
|
||||||
*storybook.log
|
*storybook.log
|
||||||
storybook-static
|
storybook-static
|
||||||
|
|
||||||
|
.env
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
"react-bootstrap": "^2.10.10",
|
"react-bootstrap": "^2.10.10",
|
||||||
"react-chartjs-2": "^5.3.1",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router": "^7.9.4"
|
"react-router": "^7.9.4",
|
||||||
|
"react-router-dom": "^7.10.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^4.1.2",
|
"@chromatic-com/storybook": "^4.1.2",
|
||||||
@@ -524,278 +525,6 @@
|
|||||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"aix"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==",
|
|
||||||
"cpu": [
|
|
||||||
"mips64el"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.25.10",
|
"version": "0.25.10",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz",
|
||||||
@@ -813,159 +542,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openharmony"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"sunos"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-x64": {
|
|
||||||
"version": "0.25.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz",
|
|
||||||
"integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.9.0",
|
"version": "4.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||||
@@ -1728,216 +1304,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.52.4",
|
"version": "4.52.4",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
|
||||||
@@ -1966,76 +1332,6 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openharmony"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
|
||||||
"version": "4.52.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
|
|
||||||
"integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@standard-schema/spec": {
|
"node_modules/@standard-schema/spec": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
|
||||||
@@ -4001,21 +3297,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -4953,21 +4234,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright/node_modules/fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pngjs": {
|
"node_modules/pngjs": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
||||||
@@ -5241,9 +4507,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.9.4",
|
"version": "7.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz",
|
||||||
"integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==",
|
"integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
@@ -5262,6 +4528,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "7.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz",
|
||||||
|
"integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-router": "7.10.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-transition-group": {
|
"node_modules/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build"
|
"build-storybook": "storybook build",
|
||||||
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
@@ -22,7 +23,8 @@
|
|||||||
"react-bootstrap": "^2.10.10",
|
"react-bootstrap": "^2.10.10",
|
||||||
"react-chartjs-2": "^5.3.1",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router": "^7.9.4"
|
"react-router": "^7.9.4",
|
||||||
|
"react-router-dom": "^7.10.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^4.1.2",
|
"@chromatic-com/storybook": "^4.1.2",
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
chart.js:
|
||||||
|
specifier: ^4.5.1
|
||||||
|
version: 4.5.1
|
||||||
react:
|
react:
|
||||||
specifier: ^19.1.1
|
specifier: ^19.1.1
|
||||||
version: 19.2.0
|
version: 19.2.0
|
||||||
|
react-chartjs-2:
|
||||||
|
specifier: ^5.3.1
|
||||||
|
version: 5.3.1(chart.js@4.5.1)(react@19.2.0)
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.1.1
|
specifier: ^19.1.1
|
||||||
version: 19.2.0(react@19.2.0)
|
version: 19.2.0(react@19.2.0)
|
||||||
@@ -436,6 +442,9 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@kurkle/color@0.3.4':
|
||||||
|
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
|
||||||
|
|
||||||
'@mdx-js/react@3.1.1':
|
'@mdx-js/react@3.1.1':
|
||||||
resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==}
|
resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -949,6 +958,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chart.js@4.5.1:
|
||||||
|
resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==}
|
||||||
|
engines: {pnpm: '>=8'}
|
||||||
|
|
||||||
check-error@2.1.1:
|
check-error@2.1.1:
|
||||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
@@ -1510,6 +1523,12 @@ packages:
|
|||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
react-chartjs-2@5.3.1:
|
||||||
|
resolution: {integrity: sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A==}
|
||||||
|
peerDependencies:
|
||||||
|
chart.js: ^4.1.1
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0:
|
react-docgen-typescript@2.4.0:
|
||||||
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2186,6 +2205,8 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@kurkle/color@0.3.4': {}
|
||||||
|
|
||||||
'@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0)':
|
'@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdx': 2.0.13
|
'@types/mdx': 2.0.13
|
||||||
@@ -2738,6 +2759,10 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
chart.js@4.5.1:
|
||||||
|
dependencies:
|
||||||
|
'@kurkle/color': 0.3.4
|
||||||
|
|
||||||
check-error@2.1.1: {}
|
check-error@2.1.1: {}
|
||||||
|
|
||||||
chromatic@12.2.0: {}
|
chromatic@12.2.0: {}
|
||||||
@@ -3255,6 +3280,11 @@ snapshots:
|
|||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
|
react-chartjs-2@5.3.1(chart.js@4.5.1)(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
chart.js: 4.5.1
|
||||||
|
react: 19.2.0
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 605 B After Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40974 4.40973 4.7157 4.21799 5.09202C4 5.51985 4 6.0799 4 7.2V16.8C4 17.9201 4 18.4802 4.21799 18.908C4.40973 19.2843 4.71569 19.5903 5.09202 19.782C5.51984 20 6.0799 20 7.2 20H16.8C17.9201 20 18.4802 20 18.908 19.782C19.2843 19.5903 19.5903 19.2843 19.782 18.908C20 18.4802 20 17.9201 20 16.8V12.5M15.5 5.5L18.3284 8.32843M10.7627 10.2373L17.411 3.58902C18.192 2.80797 19.4584 2.80797 20.2394 3.58902C21.0205 4.37007 21.0205 5.6364 20.2394 6.41745L13.3774 13.2794C12.6158 14.0411 12.235 14.4219 11.8012 14.7247C11.4162 14.9936 11.0009 15.2162 10.564 15.3882C10.0717 15.582 9.54378 15.6885 8.48793 15.9016L8 16L8.04745 15.6678C8.21536 14.4925 8.29932 13.9048 8.49029 13.3561C8.65975 12.8692 8.89125 12.4063 9.17906 11.9786C9.50341 11.4966 9.92319 11.0768 10.7627 10.2373Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M30.015 12.97l-2.567-0.569c-0.2-0.64-0.462-1.252-0.762-1.841l1.389-2.313c0.518-0.829 0.78-2.047 0-2.829l-1.415-1.414c-0.78-0.781-2.098-0.64-2.894-0.088l-2.251 1.434c-0.584-0.303-1.195-0.563-1.829-0.768l-0.576-2.598c-0.172-0.953-1.005-1.984-2.11-1.984h-2c-1.104 0-1.781 1.047-2 2l-0.642 2.567c-0.678 0.216-1.328 0.492-1.948 0.819l-2.308-1.47c-0.795-0.552-2.114-0.692-2.894 0.088l-1.415 1.414c-0.781 0.782-0.519 2 0 2.828l1.461 2.435c-0.274 0.552-0.517 1.123-0.705 1.72l-2.566 0.569c-0.953 0.171-1.984 1.005-1.984 2.109v2c0 1.105 1.047 1.782 2 2l2.598 0.649c0.179 0.551 0.404 1.080 0.658 1.593l-1.462 2.438c-0.518 0.828-0.78 2.047 0 2.828l1.415 1.414c0.78 0.782 2.098 0.64 2.894 0.089l2.313-1.474c0.623 0.329 1.277 0.608 1.96 0.823l0.64 2.559c0.219 0.953 0.896 2 2 2h2c1.105 0 1.938-1.032 2.11-1.985l0.577-2.604c0.628-0.203 1.23-0.459 1.808-0.758l2.256 1.438c0.796 0.552 2.114 0.692 2.895-0.089l1.415-1.414c0.78-0.782 0.518-2 0-2.828l-1.39-2.317c0.279-0.549 0.521-1.12 0.716-1.714l2.599-0.649c0.953-0.219 2-0.895 2-2v-2c0-1.104-1.031-1.938-1.985-2.11zM30.001 16.939c-0.085 0.061-0.245 0.145-0.448 0.192l-3.708 0.926-0.344 1.051c-0.155 0.474-0.356 0.954-0.597 1.428l-0.502 0.986 1.959 3.267c0.125 0.2 0.183 0.379 0.201 0.485l-1.316 1.314c-0.127-0.040-0.271-0.092-0.341-0.14l-3.292-2.099-1.023 0.529c-0.493 0.256-0.999 0.468-1.503 0.631l-1.090 0.352-0.824 3.723c-0.038 0.199-0.145 0.36-0.218 0.417h-1.8c-0.061-0.085-0.145-0.245-0.191-0.448l-0.921-3.681-1.066-0.338c-0.549-0.173-1.097-0.404-1.63-0.684l-1.028-0.543-3.293 2.099c-0.135 0.091-0.279 0.143-0.409 0.143l-1.311-1.276c0.018-0.104 0.072-0.274 0.181-0.449l2.045-3.408-0.487-0.98c-0.227-0.462-0.407-0.895-0.547-1.325l-0.343-1.052-3.671-0.918c-0.231-0.052-0.398-0.139-0.485-0.2v-1.86c0.001 0.001 0.002 0.001 0.005 0.001 0.034 0 0.198-0.117 0.335-0.142l3.772-0.835 0.346-1.103c0.141-0.449 0.333-0.917 0.588-1.43l0.487-0.98-2.024-3.373c-0.125-0.201-0.184-0.38-0.201-0.485l1.315-1.314c0.128 0.041 0.271 0.093 0.34 0.14l3.354 2.138 1.027-0.542c0.527-0.278 1.073-0.507 1.622-0.682l1.063-0.338 0.912-3.649c0.053-0.231 0.138-0.398 0.2-0.485h1.859c-0.014 0.020 0.115 0.195 0.142 0.339l0.84 3.794 1.089 0.352c0.511 0.165 1.023 0.38 1.523 0.639l1.023 0.532 3.224-2.053c0.135-0.092 0.279-0.143 0.409-0.143l1.313 1.276c-0.017 0.104-0.072 0.276-0.181 0.45l-1.98 3.296 0.505 0.988c0.273 0.533 0.48 1.033 0.635 1.529l0.346 1.104 3.697 0.82c0.224 0.041 0.398 0.171 0.434 0.241zM16.013 9.99c-3.321 0-6.023 2.697-6.023 6.010s2.702 6.010 6.023 6.010 6.023-2.697 6.023-6.009c0-3.313-2.702-6.010-6.023-6.010zM16 20c-2.205 0-4-1.794-4-4s1.794-4 4-4c2.206 0 4 1.794 4 4s-1.794 4-4 4z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
fill="#000000"
|
||||||
|
width="800"
|
||||||
|
height="800"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
id="Dark">
|
||||||
|
<path
|
||||||
|
d="m 12.741,20.917 a 9.389,9.389 0 0 1 -1.395,-0.105 9.141,9.141 0 0 1 -1.465,-17.7 1.177,1.177 0 0 1 1.21,0.281 1.273,1.273 0 0 1 0.325,1.293 8.112,8.112 0 0 0 -0.353,2.68 8.266,8.266 0 0 0 4.366,6.857 7.628,7.628 0 0 0 3.711,0.993 1.242,1.242 0 0 1 0.994,1.963 v 0 a 9.148,9.148 0 0 1 -7.393,3.738 z M 10.261,4.05 a 0.211,0.211 0 0 0 -0.065,0.011 8.137,8.137 0 1 0 9.131,12.526 v 0 A 0.224,0.224 0 0 0 19.34,16.352 0.232,0.232 0 0 0 19.134,16.216 8.619,8.619 0 0 1 14.946,15.1 9.274,9.274 0 0 1 10.063,7.4 9.123,9.123 0 0 1 10.463,4.392 0.286,0.286 0 0 0 10.394,4.107 0.184,0.184 0 0 0 10.261,4.05 Z"
|
||||||
|
id="path1" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.6;stroke-opacity:1"
|
||||||
|
d="M 12.27,19.887864 C 8.6252997,19.678981 5.5285722,16.983616 4.802205,13.387962 4.6751742,12.759136 4.6511961,12.497206 4.6531749,11.76 c 0.00153,-0.569372 0.01461,-0.787852 0.068001,-1.135667 C 4.9722048,8.989005 5.6646891,7.5303876 6.7700292,6.3087222 7.5904449,5.4019665 8.7542199,4.6113468 9.8911899,4.1883372 10.240576,4.0583481 10.339583,4.0517409 10.40655,4.1539446 10.47235,4.2543711 10.46876,4.3012971 10.3656,4.6890069 10.138096,5.5440534 10.057589,6.1522074 10.055041,7.035 c -0.002,0.7022856 0.03507,1.0781595 0.174729,1.7704386 0.441048,2.1862074 1.740566,4.2313134 3.533802,5.5612914 1.522152,1.128924 3.239127,1.76416 5.001426,1.850398 0.426916,0.02089 0.522077,0.04974 0.567784,0.172121 0.01351,0.03616 0.01272,0.09717 -0.0018,0.135571 -0.0463,0.122854 -0.480151,0.639299 -0.836032,0.99518 -1.646591,1.64659 -3.893995,2.501458 -6.225,2.367864 z"
|
||||||
|
id="path2" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;stroke-width:0.6"
|
||||||
|
d="M 12.24,20.891972 C 11.360122,20.823621 10.700414,20.69763 9.9480678,20.454259 8.4650988,19.974545 7.068363,19.071092 6.0273591,17.91822 3.5681787,15.194772 2.9603488,11.317696 4.4732148,8.0050236 5.4683391,5.8260321 7.3075485,4.1019471 9.54,3.2553909 c 0.474857,-0.1800684 0.671021,-0.2157843 0.918134,-0.1671678 0.388517,0.076436 0.689105,0.3111453 0.870584,0.6797817 0.07976,0.1620108 0.09971,0.2428032 0.110439,0.4473105 0.01165,0.2220426 -0.0021,0.3102909 -0.125027,0.7996359 -0.186386,0.7422312 -0.231182,1.0670649 -0.250567,1.816968 -0.01825,0.7060722 0.01143,1.0908765 0.130201,1.6880808 0.513779,2.583326 2.113703,4.659009 4.525866,5.871694 1.023273,0.514437 1.887784,0.742874 3.138603,0.829338 0.686858,0.04748 0.813587,0.08854 1.095191,0.354845 0.396466,0.374925 0.503868,0.948025 0.265728,1.417923 -0.188868,0.372671 -1.032844,1.304647 -1.622363,1.791523 -1.361429,1.124387 -3.007202,1.834185 -4.749821,2.048528 -0.372347,0.0458 -1.32298,0.08018 -1.606968,0.05812 z m 1.812099,-1.059112 c 2.09671,-0.362295 3.901569,-1.432794 5.155126,-3.05761 0.138666,-0.179734 0.168974,-0.239673 0.166437,-0.329157 -0.0049,-0.17327 -0.112259,-0.246087 -0.362835,-0.246118 -0.638294,-9e-5 -1.608859,-0.157002 -2.343213,-0.378853 -0.845921,-0.255557 -1.94952,-0.811781 -2.743228,-1.382616 C 12.717736,13.570686 11.728623,12.407926 11.05893,11.07 10.402835,9.7592388 10.076992,8.4125052 10.082658,7.035 10.086058,6.2084796 10.19082,5.4236802 10.394064,4.7021844 10.501518,4.3207323 10.50584,4.2345858 10.422823,4.1290458 10.333346,4.0152951 10.211226,4.0262211 9.8074461,4.1841018 8.7208542,4.6089669 7.7283636,5.269389 6.9148926,6.1088616 6.4866426,6.5508003 6.2969187,6.7845894 5.9734524,7.2689589 5.2794414,8.3081952 4.8329166,9.5184891 4.659645,10.83 4.602754,11.260615 4.6119,12.415262 4.675686,12.855 c 0.1862775,1.284197 0.6107841,2.410878 1.2942264,3.435 0.3498402,0.524227 0.5965434,0.824106 1.0489137,1.275 1.2796647,1.275489 2.8952232,2.063697 4.7361019,2.310675 0.483003,0.0648 1.818404,0.03991 2.297171,-0.04282 z"
|
||||||
|
id="path3" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;stroke-width:2.75694"
|
||||||
|
d="m 4.285738,11.874144 c 0.031414,-0.01988 0.3856,-0.02656 0.7854538,-0.01483 0.3998535,0.01172 0.3734269,0.02799 -0.058508,0.03615 -0.4319739,0.0082 -0.7591449,-0.0014 -0.7269853,-0.02132 z"
|
||||||
|
id="path4" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="800px" height="800px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<circle fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" cx="32.003" cy="32.005" r="16.001"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M12.001,31.997c0-2.211-1.789-4-4-4H4c-2.211,0-4,1.789-4,4
|
||||||
|
s1.789,4,4,4h4C10.212,35.997,12.001,34.208,12.001,31.997z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M12.204,46.139l-2.832,2.833c-1.563,1.562-1.563,4.094,0,5.656
|
||||||
|
c1.562,1.562,4.094,1.562,5.657,0l2.833-2.832c1.562-1.562,1.562-4.095,0-5.657C16.298,44.576,13.767,44.576,12.204,46.139z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M32.003,51.999c-2.211,0-4,1.789-4,4V60c0,2.211,1.789,4,4,4
|
||||||
|
s4-1.789,4-4l-0.004-4.001C36.003,53.788,34.21,51.999,32.003,51.999z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M51.798,46.143c-1.559-1.566-4.091-1.566-5.653-0.004
|
||||||
|
s-1.562,4.095,0,5.657l2.829,2.828c1.562,1.57,4.094,1.562,5.656,0s1.566-4.09,0-5.656L51.798,46.143z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M60.006,27.997l-4.009,0.008
|
||||||
|
c-2.203-0.008-3.992,1.781-3.992,3.992c-0.008,2.211,1.789,4,3.992,4h4.001c2.219,0.008,4-1.789,4-4
|
||||||
|
C64.002,29.79,62.217,27.997,60.006,27.997z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M51.798,17.859l2.828-2.829c1.574-1.566,1.562-4.094,0-5.657
|
||||||
|
c-1.559-1.567-4.09-1.567-5.652-0.004l-2.829,2.836c-1.562,1.555-1.562,4.086,0,5.649C47.699,19.426,50.239,19.418,51.798,17.859z"
|
||||||
|
/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M32.003,11.995c2.207,0.016,4-1.789,4-3.992v-4
|
||||||
|
c0-2.219-1.789-4-4-4c-2.211-0.008-4,1.781-4,3.993l0.008,4.008C28.003,10.206,29.792,11.995,32.003,11.995z"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#231F20" d="M12.212,17.855c1.555,1.562,4.079,1.562,5.646-0.004
|
||||||
|
c1.574-1.551,1.566-4.09,0.008-5.649l-2.829-2.828c-1.57-1.571-4.094-1.559-5.657,0c-1.575,1.559-1.575,4.09-0.012,5.653
|
||||||
|
L12.212,17.855z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 376 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="m80-280 150-400h86l150 400h-82l-34-96H196l-32 96H80Zm140-164h104l-48-150h-6l-50 150Zm328 164v-76l202-252H556v-72h282v76L638-352h202v72H548ZM360-760l120-120 120 120H360ZM480-80 360-200h240L480-80Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 320 B |
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="M 480,-880 360,-760 H 600 Z M 360,-200 480,-80 600,-200 Z" />
|
||||||
|
<path d="M 71.679924,-674.40094 H 398.50264 v 60.67705 l -208.59362,252.3436 h 214.5832 v 75.7812 H 65.690344 v -60.67705 L 274.28397,-598.61973 H 71.679924 Z" />
|
||||||
|
<path d="M 769.04932,-356.43238 H 612.27858 l -24.73957,70.83329 H 486.75782 l 144.01033,-388.80185 h 119.53118 l 144.01033,388.80185 H 793.52847 Z m -131.77076,-72.13537 h 106.51035 l -53.12496,-154.68741 z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 545 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm440 0L640-300l56-56 44 44v-488h80v487l43-43 57 56-140 140ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 475 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm400 0v-488l-44 44-56-56 140-140 140 140-57 56-43-43v487h-80ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,84 @@
|
|||||||
|
import styles from "./Event.module.css";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import Button from "/src/components/ui/button/button.jsx"
|
||||||
|
|
||||||
|
function Event({event}){
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [eventMenu, seteventMenu] = useState(false);
|
||||||
|
const [windowSize, setWindowSize] = useState({
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleResize() {
|
||||||
|
setWindowSize({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
handleResize();
|
||||||
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.eventContainer} glassCard`}>
|
||||||
|
<div className={styles.eventContainerTop}>
|
||||||
|
<div className={styles.eventContent}>
|
||||||
|
<div className={`${styles.eventImageDiv}`}>
|
||||||
|
{event.image == null ? (
|
||||||
|
<img src="/empty.png" className={styles.eventImage} alt="Pas de photo de l'événement" />
|
||||||
|
) : (
|
||||||
|
<img src={event.image + `.png`} alt="Photo de l'événement" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.eventInfos}>
|
||||||
|
<p className={styles.eventTitle}>{event.name}</p>
|
||||||
|
<p>{event.description}</p>
|
||||||
|
{event.tasks != null ? (
|
||||||
|
<div className={eventMenu ? styles.eventMenuOpen : styles.eventMenuClose}>
|
||||||
|
<ul>
|
||||||
|
{event.tasks
|
||||||
|
.slice(0,5)
|
||||||
|
.map((task, index) => (
|
||||||
|
<li key={index}> - {task.name}</li>
|
||||||
|
))}
|
||||||
|
{event.tasks.length > 5 ? (
|
||||||
|
<li>...</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{windowSize.width > 768 ? (
|
||||||
|
<div className={styles.eventButtons}>
|
||||||
|
<Button
|
||||||
|
variant={"transparent"}
|
||||||
|
onClick={()=>{seteventMenu(!eventMenu)}}
|
||||||
|
className={`${eventMenu ? styles.moreButtonClicked : ""}`}
|
||||||
|
>
|
||||||
|
{">"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className={styles.eventContainerBottom}>
|
||||||
|
{(windowSize.width <= 768 || eventMenu) ? (
|
||||||
|
|
||||||
|
<Button onClick={() => navigate(`/event/` + event.id)}>Voir plus</Button>
|
||||||
|
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Event;
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
.eventContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 1rem 0;
|
||||||
|
width: 80%;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventContainerTop {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventContainerBottom{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreLink {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventImageDiv {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
width: auto;
|
||||||
|
height: 6rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventImage {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.eventContent {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventTitle {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventButtons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
width: fit-content;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventButtons a {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreButton {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #019AFF;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 55px;
|
||||||
|
height: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreButton:hover {
|
||||||
|
font-size: 2rem;
|
||||||
|
background: rgba(191, 191, 191, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreButton:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.moreButton:focus-visible {
|
||||||
|
outline: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreButtonClicked {
|
||||||
|
rotate: 90deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventMenuOpen {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventMenuClose {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.eventImage {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventContainer{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventImageDiv {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventContent {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventInfos, .eventContainerBottom {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import styles from "./Filter.module.css";
|
||||||
|
|
||||||
|
function Filter({isFilterVisible, filters, setFilters}){
|
||||||
|
const setAlphabeticalOrder = (order) => {
|
||||||
|
setFilters(prev => ({
|
||||||
|
...prev,
|
||||||
|
alphabetical : prev.alphabetical === order ? null : order
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const setYearOrder = (order) => {
|
||||||
|
setFilters(prev => ({
|
||||||
|
...prev,
|
||||||
|
yearOrder: prev.yearOrder === order ? null : order
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.filterContainer} ${isFilterVisible ? styles.filterOpen : styles.filterClose}`}>
|
||||||
|
<abbr title="Tri alphabétique croissant (A à Z)">
|
||||||
|
<button className={`${styles.filterTag} glassCard ${filters.alphabetical !== "asc" ? "" : styles.active}`}
|
||||||
|
onClick={() => setAlphabeticalOrder("asc")}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="m80-280 150-400h86l150 400h-82l-34-96H196l-32 96H80Zm140-164h104l-48-150h-6l-50 150Zm328 164v-76l202-252H556v-72h282v76L638-352h202v72H548ZM360-760l120-120 120 120H360ZM480-80 360-200h240L480-80Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</abbr>
|
||||||
|
|
||||||
|
<abbr title="Tri alphabétique décroissant (Z à A)">
|
||||||
|
<button className={`${styles.filterTag} glassCard ${filters.alphabetical !== "desc" ? "" : styles.active}`}
|
||||||
|
onClick={() => setAlphabeticalOrder("desc")}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="M 480,-880 360,-760 H 600 Z M 360,-200 480,-80 600,-200 Z" />
|
||||||
|
<path d="M 71.679924,-674.40094 H 398.50264 v 60.67705 l -208.59362,252.3436 h 214.5832 v 75.7812 H 65.690344 v -60.67705 L 274.28397,-598.61973 H 71.679924 Z" />
|
||||||
|
<path d="M 769.04932,-356.43238 H 612.27858 l -24.73957,70.83329 H 486.75782 l 144.01033,-388.80185 h 119.53118 l 144.01033,388.80185 H 793.52847 Z m -131.77076,-72.13537 h 106.51035 l -53.12496,-154.68741 z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</abbr>
|
||||||
|
|
||||||
|
<abbr title="Tri chronologique décroissant (du plus récent au plus ancien)">
|
||||||
|
<button className={`${styles.filterTag} glassCard ${filters.yearOrder !== "desc" ? "" : styles.active}`}
|
||||||
|
onClick={() => setYearOrder("desc")}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm400 0v-488l-44 44-56-56 140-140 140 140-57 56-43-43v487h-80ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</abbr>
|
||||||
|
|
||||||
|
<abbr title="Tri chronologique croissant (du plus ancien au plus récent)">
|
||||||
|
<button className={`${styles.filterTag} glassCard ${filters.yearOrder !== "asc" ? "" : styles.active}`}
|
||||||
|
onClick={() => setYearOrder("asc")}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="M340-160q-125 0-212.5-87.5T40-460q0-125 87.5-212.5T340-760q52 0 98 16.5t84 45.5l42-42 56 56-42 42q29 38 45.5 84.5T640-460q0 125-87.5 212.5T340-160Zm440 0L640-300l56-56 44 44v-488h80v487l43-43 57 56-140 140ZM240-800v-80h200v80H240Zm100 560q92 0 156-64t64-156q0-92-64-156t-156-64q-92 0-156 64t-64 156q0 92 64 156t156 64Zm-40-180h80v-200h-80v200Zm40-40Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</abbr>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Filter;
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
.filterContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: max-height 0.5s ease, opacity 0.8s ease, transform 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterOpen {
|
||||||
|
max-height: 200px;
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterClose{
|
||||||
|
max-height: 200px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.filterTag {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.filterTag:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.filterTag:focus-visible {
|
||||||
|
outline: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterTag:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
svg{
|
||||||
|
fill: #019AFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.filterTag svg {
|
||||||
|
transform: scale(0.7);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.active {
|
||||||
|
border: 1px solid #019AFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active svg {
|
||||||
|
fill: #019AFF;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px){
|
||||||
|
.filterContainer {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterOpen{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.filterClose{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterMenu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ function Header() {
|
|||||||
setNotifications(notifData || []);
|
setNotifications(notifData || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadNotifications();
|
//loadNotifications();
|
||||||
const handleClickOutside = (event) => {
|
const handleClickOutside = (event) => {
|
||||||
if (
|
if (
|
||||||
notificationRef.current &&
|
notificationRef.current &&
|
||||||
@@ -41,7 +41,7 @@ function Header() {
|
|||||||
<nav className={`${mobileMenu ? styles.inactiveHeaderContent : styles.activeHeaderContent}`}>
|
<nav className={`${mobileMenu ? styles.inactiveHeaderContent : styles.activeHeaderContent}`}>
|
||||||
<div className={styles.headerLeftContent}>
|
<div className={styles.headerLeftContent}>
|
||||||
<NavLink className={styles.logoLink} to="/">
|
<NavLink className={styles.logoLink} to="/">
|
||||||
<img src="/src/assets/logo.png" alt="logo" className={styles.logo} />
|
<img src="logo.png" alt="logo" className={styles.logo} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<div className={styles.navLinks}>
|
<div className={styles.navLinks}>
|
||||||
<NavLink to="/" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
|
<NavLink to="/" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
|
||||||
@@ -70,7 +70,7 @@ function Header() {
|
|||||||
{unreadNotification && <span className={styles.notificationBadge}></span>}
|
{unreadNotification && <span className={styles.notificationBadge}></span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Link to="" className={styles.profileLink}>
|
<Link to="/profile" className={styles.profileLink}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="auto" fill="currentFill">
|
<svg xmlns="http://www.w3.org/2000/svg" height="30px" viewBox="0 -960 960 960" width="auto" fill="currentFill">
|
||||||
<path d="M480-492.31q-57.75 0-98.87-41.12Q340-574.56 340-632.31q0-57.75 41.13-98.87 41.12-41.13 98.87-41.13 57.75 0 98.87 41.13Q620-690.06 620-632.31q0 57.75-41.13 98.88-41.12 41.12-98.87 41.12ZM180-187.69v-88.93q0-29.38 15.96-54.42 15.96-25.04 42.66-38.5 59.3-29.07 119.65-43.61 60.35-14.54 121.73-14.54t121.73 14.54q60.35 14.54 119.65 43.61 26.7 13.46 42.66 38.5Q780-306 780-276.62v88.93H180Z" />
|
<path d="M480-492.31q-57.75 0-98.87-41.12Q340-574.56 340-632.31q0-57.75 41.13-98.87 41.12-41.13 98.87-41.13 57.75 0 98.87 41.13Q620-690.06 620-632.31q0 57.75-41.13 98.88-41.12 41.12-98.87 41.12ZM180-187.69v-88.93q0-29.38 15.96-54.42 15.96-25.04 42.66-38.5 59.3-29.07 119.65-43.61 60.35-14.54 121.73-14.54t121.73 14.54q60.35 14.54 119.65 43.61 26.7 13.46 42.66 38.5Q780-306 780-276.62v88.93H180Z" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ svg {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-10px);
|
transform: translateY(-10px);
|
||||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
z-index: 1;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activeNotificationDiv {
|
.activeNotificationDiv {
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import styles from "./SearchBar.module.css"
|
||||||
|
import {Link} from "react-router";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import searchEvents from "../../utils/searchEvents.js";
|
||||||
|
import searchUsers from "../../utils/searchUsers.js";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
|
||||||
|
|
||||||
|
function SearchBar() {
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const performSearch = async (query) => {
|
||||||
|
if (query.trim() === '') {
|
||||||
|
setSearchResults([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (location.pathname.includes("events")) {
|
||||||
|
data = await searchEvents(query);
|
||||||
|
} else if (location.pathname.includes("volunteers")) {
|
||||||
|
data = await searchUsers(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
setSearchResults(data);
|
||||||
|
} else {
|
||||||
|
setSearchResults([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
performSearch(searchQuery);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler);
|
||||||
|
};
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.searchBarContainer} glassCard`}>
|
||||||
|
<form className={styles.searchBarForm} onSubmit={(element) => element.preventDefault()}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24px" viewBox="0 -960 960 960" fill="#currentColor">
|
||||||
|
<path
|
||||||
|
d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
|
||||||
|
</svg>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Rechercher un événement"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(element) => setSearchQuery(element.target.value)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className={styles.lineContainer}>
|
||||||
|
{searchResults.length > 0 ? (
|
||||||
|
<div className={styles.line}></div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.elementList}>
|
||||||
|
{searchResults.map((result, i) => (
|
||||||
|
(location.pathname.includes("events")) ? (
|
||||||
|
<Link to={`/event/` + result.id}>
|
||||||
|
<div className={styles.searchedEvent} key={i}>
|
||||||
|
<div className={`${styles.eventImageDiv}`}>
|
||||||
|
{result.image == null ? (
|
||||||
|
<img src="empty.png" className={styles.eventImage} alt="Pas de photo de l'événement" />
|
||||||
|
) : (
|
||||||
|
<img src={result.image + `.png`} alt="Photo de l'événement" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p>{result.name}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
) : location.pathname.includes("volunteers") ? (
|
||||||
|
<Link to={`/profile/` + result.id}>
|
||||||
|
<div className={styles.searchedEvent} key={i}>
|
||||||
|
<div className={`${styles.eventImageDiv}`}>
|
||||||
|
{result.image == null ? (
|
||||||
|
<img src="empty.png" className={styles.eventImage} alt="Pas de photo de l'événement" />
|
||||||
|
) : (
|
||||||
|
<img src={result.image + `.png`} alt="Photo de l'événement" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p>{result.name} {result.lastname}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
) : null
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchBar;
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
.searchBarContainer{
|
||||||
|
position: fixed;
|
||||||
|
top: 4rem;
|
||||||
|
z-index: 100;
|
||||||
|
width: 60%;
|
||||||
|
background: #fff;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBarForm{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elementList {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.eventImageDiv {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
width: auto;
|
||||||
|
height: 2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchedEvent{
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 15px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchedEvent:hover {
|
||||||
|
background: #afafaf;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchedEvent p {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lineContainer{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line{
|
||||||
|
height: 0;
|
||||||
|
width: 96%;
|
||||||
|
border: #606060 solid 1px;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventImage {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBarForm input{
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchBarForm input:focus{
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
.searchBarContainer{
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import Modal from "../ui/modal/modal.jsx";
|
||||||
|
import Button from "../ui/button/button.jsx";
|
||||||
|
import styles from "./SettingsModal.module.css"
|
||||||
|
import { useContext, useState } from "react";
|
||||||
|
import ThemeSwitcher from "../ui/themeSwitcher/ThemeSwitcher.jsx";
|
||||||
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
|
import TextInput from "../ui/input/input.jsx";
|
||||||
|
import updateUser from "../../utils/users/updateUser.js";
|
||||||
|
|
||||||
|
|
||||||
|
export default function SettingsModal() {
|
||||||
|
|
||||||
|
const { user, logout, update } = useContext(AuthContext);
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const [name, setName] = useState(user.name);
|
||||||
|
const [lastname, setLastName] = useState(user.lastname);
|
||||||
|
const [phone, setPhone] = useState(user.phone);
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await updateUser(name, lastname, phone);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant={"transparent"} onClick={() => setOpen(!open)}>
|
||||||
|
<img src={"/icons/settings-wheel.svg"} alt={"Modify btn"} className={styles.modifyIcon}/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Modal title={"Paramètres"} open={open} onClose={() => setOpen(false)}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Compte</h3>
|
||||||
|
<Button variant={"danger"} onClick={handleLogout}> Déconnexion </Button>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de Prénom</h4>
|
||||||
|
<TextInput value={name} onChange={e => setName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de nom</h4>
|
||||||
|
<TextInput value={lastname} onChange={e => setLastName(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h4>Changer de numéro de téléphone</h4>
|
||||||
|
<TextInput value={phone} onChange={e => setPhone(e.target.value)} type={"text"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button className={styles.submitBtn} onClick={handleSubmit}>Confirmer</Button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<h3>Apparence</h3>
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modifyIcon {
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitBtn {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import getEventById from "../../utils/event/getEventById.js";
|
||||||
|
import styles from "./Task.module.css";
|
||||||
|
import formatDate from "../../utils/date/formatDate.js";
|
||||||
|
|
||||||
|
const Task = ({ task }) => {
|
||||||
|
const [event, setEvent] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const e = await getEventById(task.id);
|
||||||
|
setEvent(e);
|
||||||
|
}) ()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.task} glassCard`}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<div>
|
||||||
|
<h2> {event && event.name} - {task.name}</h2>
|
||||||
|
<p className={styles.desc}> {task.description} </p>
|
||||||
|
<p className={styles.date}> {formatDate(task.start)} </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Task;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
.task {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #666;
|
||||||
|
margin: 0 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import styles from "./ToolBar.module.css"
|
||||||
|
import {Link} from "react-router";
|
||||||
|
import Filter from "../Filter/Filter.jsx";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import SearchBar from "../SearchBar/SearchBar.jsx";
|
||||||
|
|
||||||
|
|
||||||
|
function ToolBar({setFilters, filters}) {
|
||||||
|
const [isFilterVisible, setIsFilterVisible] = useState(false);
|
||||||
|
const [isSearchVisible, setIsSearchVisible] = useState(false);
|
||||||
|
const toggleFilters = () => {
|
||||||
|
setIsFilterVisible(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSearchVisible) {
|
||||||
|
document.documentElement.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.documentElement.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.documentElement.style.overflow = '';
|
||||||
|
};
|
||||||
|
}, [isSearchVisible]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isSearchVisible ? (
|
||||||
|
<div className={styles.searchBarContainer}>
|
||||||
|
<div className={styles.searchBarOverlay} onClick={() => setIsSearchVisible(false)}></div>
|
||||||
|
<SearchBar/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className={styles.eventsButtons}>
|
||||||
|
<Link
|
||||||
|
to="/event/create"
|
||||||
|
className={`${styles.createButton} glassCard`}>
|
||||||
|
+
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button className={`${styles.searchButton} glassCard`} onClick={() => setIsSearchVisible(true)}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#currentColor">
|
||||||
|
<path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className={`${styles.filterContainer} ${isFilterVisible ? "glassCard" : ""}`}>
|
||||||
|
<button className={`${isFilterVisible ? "" : styles.mobileFilter} ${styles.sortButton} glassCard`} onClick={toggleFilters}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor">
|
||||||
|
<path d="M440-160q-17 0-28.5-11.5T400-200v-240L168-736q-15-20-4.5-42t36.5-22h560q26 0 36.5 22t-4.5 42L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-308 198-252H282l198 252Zm0 0Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div className={styles.filterMenu}>
|
||||||
|
<Filter isFilterVisible={isFilterVisible} filters={filters} setFilters={setFilters}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ToolBar;
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
.eventsButtons {
|
||||||
|
overflow: visible;
|
||||||
|
position: fixed;
|
||||||
|
top: 10rem;
|
||||||
|
right: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.filterContainer{
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 30px;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterMenu{
|
||||||
|
padding: 5px 5px 5px 5px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createButton, .searchButton, .sortButton {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
width: 55px !important;
|
||||||
|
height: 55px !important;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createButton {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: #019AFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createButton:hover, .searchButton:hover, .mobileFilter:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.searchBarOverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
.searchBarContainer{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortButton svg, .searchButton svg {
|
||||||
|
transform: scale(0.5);
|
||||||
|
fill: #019AFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sortButton:focus, .searchButton:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.createButton,
|
||||||
|
.searchButton,
|
||||||
|
.sortButton {
|
||||||
|
flex: 0 0 55px;
|
||||||
|
width: 55px !important;
|
||||||
|
height: 55px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.eventsButtons {
|
||||||
|
flex-direction: row;
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterContainer{
|
||||||
|
display: flex;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filterMenu{
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createButton:hover, .searchButton:hover, .mobileFilter:hover {
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import styles from "./background.module.css";
|
import styles from "./background.module.css";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
const Background = ({ children, className }) => {
|
const Background = ({ children, className }) => {
|
||||||
const circlesRef = useRef([]);
|
|
||||||
|
const circlesRef = useRef([]);
|
||||||
|
const [theme, setTheme] = useState("light");
|
||||||
|
|
||||||
const getRandomTranslation = () => {
|
const getRandomTranslation = () => {
|
||||||
const randomX = Math.floor(Math.random() * 100) - 30;
|
const randomX = Math.floor(Math.random() * 100) - 30;
|
||||||
@@ -20,8 +22,23 @@ const Background = ({ children, className }) => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<div className={`${styles["background-container"]} ${className || ''}`}>
|
const updateTheme = () => {
|
||||||
|
setTheme(localStorage.getItem("theme") || "light");
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTheme(); // Initial load
|
||||||
|
|
||||||
|
window.addEventListener("theme-changed", updateTheme);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("theme-changed", updateTheme);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.backgroundContainer} ${className || ''} ${theme === "light" ? styles.backgroundLight : styles.backgroundDark}`}>
|
||||||
<div
|
<div
|
||||||
ref={(el) => (circlesRef.current[0] = el)}
|
ref={(el) => (circlesRef.current[0] = el)}
|
||||||
className={`${styles["circle"]} ${styles["circle-1"]}`}
|
className={`${styles["circle"]} ${styles["circle-1"]}`}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
/* BACKGROUND ////////////////////////////////////////////////////////*/
|
/* BACKGROUND ////////////////////////////////////////////////////////*/
|
||||||
.background-container {
|
.backgroundContainer {
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #00F0FF;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@@ -13,6 +12,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backgroundLight {
|
||||||
|
background: #00F0FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backgroundDark {
|
||||||
|
background: #001C83;
|
||||||
|
}
|
||||||
|
|
||||||
.circle {
|
.circle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -82,7 +89,6 @@
|
|||||||
.circle-1 {
|
.circle-1 {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
background-color: #019AFF;
|
|
||||||
top: -5%;
|
top: -5%;
|
||||||
left: -5%;
|
left: -5%;
|
||||||
}
|
}
|
||||||
@@ -90,7 +96,6 @@
|
|||||||
.circle-2 {
|
.circle-2 {
|
||||||
width: 175px;
|
width: 175px;
|
||||||
height: 175px;
|
height: 175px;
|
||||||
background-color: #91FCFF;
|
|
||||||
top: 58%;
|
top: 58%;
|
||||||
left: 10%;
|
left: 10%;
|
||||||
}
|
}
|
||||||
@@ -98,7 +103,6 @@
|
|||||||
.circle-3 {
|
.circle-3 {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
height: 140px;
|
height: 140px;
|
||||||
background-color: #91FCFF;
|
|
||||||
top: 29%;
|
top: 29%;
|
||||||
right: 9%;
|
right: 9%;
|
||||||
}
|
}
|
||||||
@@ -106,7 +110,6 @@
|
|||||||
.circle-4 {
|
.circle-4 {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
background-color: #019AFF;
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
@@ -114,7 +117,6 @@
|
|||||||
.circle-5 {
|
.circle-5 {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 240px;
|
height: 240px;
|
||||||
background-color: #015AFF;
|
|
||||||
top: 2%;
|
top: 2%;
|
||||||
right: 10%;
|
right: 10%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
/*import { useState } from "react";
|
|
||||||
import login from "../../utils/login.js";
|
|
||||||
import getUser from "../../utils/getUser.js";
|
|
||||||
import styles from "./form.module.css";
|
|
||||||
function LoginForm({ className }) {
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
async function handleSubmit(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
await login(email, password);
|
|
||||||
getUser();
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={`${className} ${"glassCard"}`}>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<input type="email" id="email" value={email} placeholder="Adresse mail" onChange={e => setEmail(e.target.value)} required/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<input type="password" id="password" value={password} placeholder="Mot de passe" onChange={e => setPassword(e.target.value)} required/>
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.logButton} >
|
|
||||||
<button className={styles.loginButton} onClick={e => handleSubmit(e)}>Se connecter</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default LoginForm;*/
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import login from "../../utils/login.js";
|
|
||||||
import getUser from "../../utils/getUser.js";
|
|
||||||
import styles from "./form.module.css";
|
|
||||||
|
|
||||||
function LoginForm({ className }) {
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
|
||||||
|
|
||||||
async function handleSubmit(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
await login(email, password);
|
|
||||||
getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleShowPassword = () => {
|
|
||||||
setShowPassword(!showPassword);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`${className} ${"glassCard"}`}>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
className={styles.input}
|
|
||||||
value={email}
|
|
||||||
placeholder="Adresse mail"
|
|
||||||
onChange={e => setEmail(e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.formgroup}>
|
|
||||||
<div style={{ position: 'relative', width: '100%' }}>
|
|
||||||
<input
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
id="password"
|
|
||||||
className={styles.input}
|
|
||||||
value={password}
|
|
||||||
placeholder="Mot de passe"
|
|
||||||
onChange={e => setPassword(e.target.value)}
|
|
||||||
required
|
|
||||||
style={{ width: '100%', paddingRight: '40px' }}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={toggleShowPassword}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
width: '30px',
|
|
||||||
right: '10px',
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
cursor: 'pointer',
|
|
||||||
fontSize: '16px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{showPassword ? '👁️' : '🔒'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.logButton}>
|
|
||||||
<button className={styles.loginButton} onClick={handleSubmit}>Se connecter</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginForm;
|
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { useState, useContext } from "react";
|
||||||
|
import styles from "./loginForm.module.css";
|
||||||
|
import Button from "../ui/button/button.jsx";
|
||||||
|
import Input from "../ui/input/input.jsx";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
|
|
||||||
|
|
||||||
|
function LoginForm({ className }) {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
let navigate = useNavigate();
|
||||||
|
const { login } = useContext(AuthContext);
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
await login(email, password);
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${className} glassCard ${styles.inputContainer}`}>
|
||||||
|
<div className={styles.formgroup}>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
placeholder="Adresse mail"
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.formgroup}>
|
||||||
|
<Input password={true} onChange={e => setPassword(e.target.value)} value={password} placeholder={"Mot de passe"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.logButton}>
|
||||||
|
<Button variant={"transparent"} onClick={() => navigate("/register")}>Vous n'avez pas de compte ?</Button>
|
||||||
|
<Button onClick={handleSubmit} variant={"default"}> Se connecter </Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginForm;
|
||||||
|
|
||||||
@@ -18,14 +18,21 @@ input::placeholder{
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: rgba(0,0,0,0.45);
|
color: rgba(0,0,0,0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.formgroup{
|
||||||
|
margin-top:23px;
|
||||||
|
}
|
||||||
|
|
||||||
.logButton{
|
.logButton{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
.formgroup{
|
|
||||||
margin-top:23px;
|
.inputContainer {
|
||||||
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* style scoped (module) */
|
/* style scoped (module) */
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import styles from "./button.module.css";
|
import styles from "./button.module.css";
|
||||||
|
|
||||||
const Button = ({ children, variant = "primary", onClick }) => {
|
const Button = ({ children, variant = "primary", onClick, className }) => {
|
||||||
let btnStyle;
|
let btnStyle;
|
||||||
|
|
||||||
if (variant === "primary") btnStyle = styles.primary;
|
if (variant === "primary") btnStyle = styles.primary;
|
||||||
@@ -8,7 +8,7 @@ const Button = ({ children, variant = "primary", onClick }) => {
|
|||||||
else if(variant === "transparent") btnStyle = styles.transparent;
|
else if(variant === "transparent") btnStyle = styles.transparent;
|
||||||
else btnStyle = styles.default;
|
else btnStyle = styles.default;
|
||||||
|
|
||||||
return <div className={`${styles.btn} ${btnStyle}`} onClick={onClick}>
|
return <div className={`${styles.btn} ${btnStyle} ${className}`} onClick={onClick}>
|
||||||
{children}
|
{children}
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,38 @@
|
|||||||
import styles from "./input.module.css";
|
import styles from "./input.module.css";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
const TextInput = ({ placeholder, onChange, value, borderStyle, ...props }) => {
|
const TextInput = ({ placeholder, onChange, value, borderStyle, password, className, ...props }) => {
|
||||||
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
let inputBorderStyle = styles.square;
|
let inputBorderStyle = styles.square;
|
||||||
if (borderStyle === "square") inputBorderStyle = styles.square;
|
if (borderStyle === "square") inputBorderStyle = styles.square;
|
||||||
if(borderStyle === "rounded") inputBorderStyle = styles.rounded;
|
if(borderStyle === "rounded") inputBorderStyle = styles.rounded;
|
||||||
|
|
||||||
return <input className={`${styles.input} ${inputBorderStyle}`} onChange={onChange} placeholder={placeholder} value={value} {...props} />
|
const toggleShowPassword = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(password) return (
|
||||||
|
<div className={styles.passwordContainer}>
|
||||||
|
<TextInput
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={onChange}
|
||||||
|
className={styles.passwordInput}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={toggleShowPassword}
|
||||||
|
className={styles.passwordBtn}
|
||||||
|
>
|
||||||
|
{showPassword ? '👁️' : '🔒'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <input className={`${styles.input} ${inputBorderStyle} ${className}`} onChange={onChange} placeholder={placeholder} value={value} {...props} />
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TextInput;
|
export default TextInput;
|
||||||
@@ -30,3 +30,23 @@
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.passwordContainer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordInput {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordBtn {
|
||||||
|
position: absolute;
|
||||||
|
width: 30px;
|
||||||
|
right: 10px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import styles from "./modal.module.css";
|
||||||
|
import Button from "../button/button.jsx";
|
||||||
|
|
||||||
|
const Modal = ({ open, onClose, children, title }) => {
|
||||||
|
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModalClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className={styles.overlay} onClick={handleOverlayClick}>
|
||||||
|
<div className={styles.modal} onClick={handleModalClick}>
|
||||||
|
{title && <h2>{title}</h2>}
|
||||||
|
{children}
|
||||||
|
<div className={styles.modalFooter}>
|
||||||
|
<Button onClick={onClose}>Fermer</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
align-items: flex-start;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
padding: 20px 20px 20px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 70%;
|
||||||
|
max-height: none;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
width: clamp(300px, 50vw, 600px);
|
||||||
|
border: 1px solid rgba(200, 200, 200, 0.3);
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
color: #000;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalFooter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import Button from "../button/button.jsx";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import styles from "./ThemeSwitcher.module.css";
|
||||||
|
|
||||||
|
|
||||||
|
export default function ThemeSwitcher() {
|
||||||
|
|
||||||
|
const [theme, setTheme] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const t = localStorage.getItem("theme");
|
||||||
|
if(t) setTheme(t);
|
||||||
|
else setTheme("light");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const switchTheme = () => {
|
||||||
|
|
||||||
|
let newTheme = theme === "light" ? "dark" : "light";
|
||||||
|
|
||||||
|
if(theme === null || theme === "") newTheme = "dark";
|
||||||
|
|
||||||
|
localStorage.setItem("theme", newTheme);
|
||||||
|
setTheme(newTheme);
|
||||||
|
|
||||||
|
window.dispatchEvent(new Event("theme-changed"));
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Button variant={"default"} onClick={switchTheme} className={styles.themeBtn}>
|
||||||
|
<img src={`/icons/theme/${theme}.svg`} alt={`${theme} icon`} className={styles.themeIcon} />
|
||||||
|
<p>Changer de thème </p>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.themeBtn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.themeIcon {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
export const AuthContext = createContext(null);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import loginFunction from "../../utils/login.js";
|
||||||
|
import getUser from "../../utils/getUser.js";
|
||||||
|
import { AuthContext } from "./AuthContext";
|
||||||
|
import logoutFunction from "../../utils/auth/logout.js"
|
||||||
|
|
||||||
|
|
||||||
|
export function AuthProvider({ children }) {
|
||||||
|
const [user, setUser] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const u = await getUser();
|
||||||
|
setUser(u);
|
||||||
|
setLoading(false);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const login = async (email, password) => {
|
||||||
|
const res = await loginFunction(email, password);
|
||||||
|
if (!res || res.status !== 200) throw new Error("Login error");
|
||||||
|
const u = await getUser();
|
||||||
|
setUser(u);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = async () => {
|
||||||
|
await logoutFunction();
|
||||||
|
setUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = async () => {
|
||||||
|
const u = await getUser();
|
||||||
|
setUser(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={{ user, loading, login, logout, update }}>
|
||||||
|
{children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,16 +19,19 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.content{
|
||||||
|
padding: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #646cff;
|
color: #646cff;
|
||||||
@@ -45,7 +50,6 @@ body {
|
|||||||
|
|
||||||
/*.glassCard {
|
/*.glassCard {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
background: rgba(255, 255, 255, 0.65);
|
background: rgba(255, 255, 255, 0.65);
|
||||||
backdrop-filter: blur(3px);
|
backdrop-filter: blur(3px);
|
||||||
|
|||||||
@@ -2,10 +2,18 @@ import { Outlet } from "react-router";
|
|||||||
import Footer from "./components/Footer/Footer.jsx"
|
import Footer from "./components/Footer/Footer.jsx"
|
||||||
import Header from "./components/Header/Header.jsx";
|
import Header from "./components/Header/Header.jsx";
|
||||||
import Background from "./components/background/background.jsx";
|
import Background from "./components/background/background.jsx";
|
||||||
|
import { Navigate } from "react-router";
|
||||||
|
import { useContext} from "react";
|
||||||
|
import { AuthContext } from "./contexts/auth/AuthContext.js";
|
||||||
|
|
||||||
|
|
||||||
function Layout(){
|
function Layout(){
|
||||||
|
|
||||||
|
const { user, loading } = useContext(AuthContext);
|
||||||
|
|
||||||
|
if (loading) return <p>Loading</p>;
|
||||||
|
if (!user) return <Navigate to="/login" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Background>
|
<Background>
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ import { createRoot } from 'react-dom/client'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import { RouterProvider } from "react-router";
|
import { RouterProvider } from "react-router";
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
import { AuthProvider } from "./contexts/auth/authProvider.jsx";
|
||||||
|
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<RouterProvider router={router} />
|
<AuthProvider>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</AuthProvider>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Event from "../../components/Event/Event.jsx";
|
||||||
|
import styles from "./EventsPage.module.css";
|
||||||
|
import ToolBar from "../../components/ToolBar/ToolBar.jsx";
|
||||||
|
import getAllEvents from "../../utils/event/getAllEvents.js";
|
||||||
|
import filter from "../../utils/filter.js";
|
||||||
|
|
||||||
|
|
||||||
|
function EventsPage(){
|
||||||
|
const [filters, setFilters] = useState({
|
||||||
|
alphabetical: "asc",
|
||||||
|
yearOrder: "desc",
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const [events, setEvents] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const data = await getAllEvents();
|
||||||
|
setEvents(data.data);
|
||||||
|
}) ()
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.eventsContainer}>
|
||||||
|
<ToolBar setFilters={setFilters} filters={filters}/>
|
||||||
|
<div className={styles.eventsList}>
|
||||||
|
{filter(events, filters, "event").map((event, index) => (
|
||||||
|
<Event key={index} event={event} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventsPage;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.eventsContainer {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.eventsList{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
@@ -1,11 +1,181 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import styles from "./HomePage.module.css";
|
||||||
|
|
||||||
|
function HomePage() {
|
||||||
|
const events = {
|
||||||
|
"2025-11-07": ["Réunion de projet", "Anniversaire de Marie"],
|
||||||
|
"2025-11-10": ["Cours de React", "Rendez-vous médecin"],
|
||||||
|
"2025-11-14": ["Sortie cinéma"],
|
||||||
|
};
|
||||||
|
|
||||||
function HomePage(){
|
const monthNames = [
|
||||||
|
"Janvier", "Février", "Mars", "Avril", "Mai", "Juin",
|
||||||
|
"Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
|
||||||
|
];
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const [currentMonth, setCurrentMonth] = useState(today.getMonth());
|
||||||
|
const [currentYear, setCurrentYear] = useState(today.getFullYear());
|
||||||
|
const [selectedDay, setSelectedDay] = useState(() => {
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(today.getDate()).padStart(2, "0");
|
||||||
|
const key = `${year}-${month}-${day}`;
|
||||||
|
return { date: key, events: events[key] || [] };
|
||||||
|
});
|
||||||
|
|
||||||
|
const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
|
||||||
|
const firstDay = new Date(currentYear, currentMonth, 1).getDay();
|
||||||
|
const startDay = (firstDay + 6) % 7;
|
||||||
|
|
||||||
|
const days = [];
|
||||||
|
for (let i = 0; i < startDay; i++) days.push(null);
|
||||||
|
for (let i = 1; i <= daysInMonth; i++) days.push(i);
|
||||||
|
|
||||||
|
const handlePrevMonth = () => {
|
||||||
|
let newMonth = currentMonth - 1;
|
||||||
|
let newYear = currentYear;
|
||||||
|
|
||||||
|
if (newMonth < 0) {
|
||||||
|
newMonth = 11;
|
||||||
|
newYear--;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentMonth(newMonth);
|
||||||
|
setCurrentYear(newYear);
|
||||||
|
|
||||||
|
const key = `${newYear}-${String(newMonth + 1).padStart(2, "0")}-01`;
|
||||||
|
setSelectedDay({ date: key, events: events[key] || [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNextMonth = () => {
|
||||||
|
let newMonth = currentMonth + 1;
|
||||||
|
let newYear = currentYear;
|
||||||
|
|
||||||
|
if (newMonth > 11) {
|
||||||
|
newMonth = 0;
|
||||||
|
newYear++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentMonth(newMonth);
|
||||||
|
setCurrentYear(newYear);
|
||||||
|
|
||||||
|
const key = `${newYear}-${String(newMonth + 1).padStart(2, "0")}-01`;
|
||||||
|
setSelectedDay({ date: key, events: events[key] || [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDayClick = (day) => {
|
||||||
|
if (!day) return;
|
||||||
|
const key = `${currentYear}-${String(currentMonth + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
||||||
|
setSelectedDay({ date: key, events: events[key] || [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedEvents = useMemo(() => {
|
||||||
|
return Object.entries(events)
|
||||||
|
.map(([dateKey, eventList]) => ({ date: dateKey, events: eventList }))
|
||||||
|
.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||||||
|
}, [events]);
|
||||||
|
|
||||||
|
const formatDate = (dateString) => {
|
||||||
|
const [year, month, day] = dateString.split('-');
|
||||||
|
return `${day} ${monthNames[parseInt(month) - 1]} ${year}`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.allContent}>
|
||||||
|
<div className={styles.glassCard}>
|
||||||
|
<div className={styles.calendarContainer}>
|
||||||
|
<div className={styles.calendarHeader}>
|
||||||
|
<button onClick={handlePrevMonth}>◀</button>
|
||||||
|
<p>{monthNames[currentMonth]} {currentYear}</p>
|
||||||
|
<button onClick={handleNextMonth}>▶</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table className={styles.calendar}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Lun</th>
|
||||||
|
<th>Mar</th>
|
||||||
|
<th>Mer</th>
|
||||||
|
<th>Jeu</th>
|
||||||
|
<th>Ven</th>
|
||||||
|
<th>Sam</th>
|
||||||
|
<th>Dim</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{Array.from({ length: Math.ceil(days.length / 7) }).map((_, weekIndex) => (
|
||||||
|
<tr key={weekIndex}>
|
||||||
|
{days.slice(weekIndex * 7, weekIndex * 7 + 7).map((day, index) => {
|
||||||
|
const key = `${currentYear}-${String(currentMonth + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
||||||
|
const hasEvent = events[key];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
key={index}
|
||||||
|
className={[
|
||||||
|
day === today.getDate() &&
|
||||||
|
currentMonth === today.getMonth() &&
|
||||||
|
currentYear === today.getFullYear()
|
||||||
|
? styles.today : "",
|
||||||
|
hasEvent ? styles.hasEvent : "",
|
||||||
|
selectedDay?.date === key ? styles.selected : ""
|
||||||
|
].join(" ")}
|
||||||
|
onClick={() => handleDayClick(day)}
|
||||||
|
>
|
||||||
|
{day || ""}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{selectedDay && (
|
||||||
|
<div className={styles.eventBox}>
|
||||||
|
<h3>Événements du {formatDate(selectedDay.date)}</h3>
|
||||||
|
|
||||||
|
{selectedDay.events.length > 0 ? (
|
||||||
|
<ul>
|
||||||
|
{selectedDay.events.map((ev, i) => (
|
||||||
|
<li key={i}>{ev}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p>Aucun événement ce jour-là.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.glassCard}>
|
||||||
|
<div className={styles.eventListContainer}>
|
||||||
|
<h2>Liste des Événements à Venir</h2>
|
||||||
|
{sortedEvents.length > 0 ? (
|
||||||
|
<ul className={styles.eventList}>
|
||||||
|
{sortedEvents.map((eventGroup) => (
|
||||||
|
<li key={eventGroup.date} className={styles.eventGroup}>
|
||||||
|
<div className={styles.eventDate}>
|
||||||
|
<strong>{formatDate(eventGroup.date)}</strong>
|
||||||
|
</div>
|
||||||
|
<ul className={styles.eventDetails}>
|
||||||
|
{eventGroup.events.map((event, i) => (
|
||||||
|
<li key={i}>- {event}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p>Aucun événement planifié pour l'instant.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HomePage;
|
export default HomePage;
|
||||||
@@ -1,3 +1,173 @@
|
|||||||
.title {
|
.calendarContainer {
|
||||||
color: red;
|
padding: 20px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarHeader {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarHeader p {
|
||||||
|
width: auto;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarHeader button {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow:
|
||||||
|
inset 1.25px 1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -1.25px -1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 15px;
|
||||||
|
color: #019AFF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 80%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar th, .calendar td {
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
width: 14.2%;
|
||||||
|
line-height: 130%;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.today {
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasEvent {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasEvent::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 10px;
|
||||||
|
width: 20px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #007bff;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasEvent.selected::before {
|
||||||
|
width: 25px;
|
||||||
|
height: 2px;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
position: relative;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border-radius: 38%;
|
||||||
|
pointer-events: none;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow:
|
||||||
|
inset 1.25px 1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -1.25px -1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventBox {
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: rgb(255, 255, 255, 0.25);
|
||||||
|
box-shadow:
|
||||||
|
inset 1.25px 1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -1.25px -1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventBox ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventGroup{
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 16px;
|
||||||
|
border: none;
|
||||||
|
background: rgb(255, 255, 255, 0.25);
|
||||||
|
box-shadow:
|
||||||
|
inset 1.25px 1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -1.25px -1.25px 1px rgba(255, 255, 255, 1),
|
||||||
|
inset -0.25px 0.25px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0.25px -0.25px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glassCard {
|
||||||
|
width: 45%;
|
||||||
|
margin: 16px;
|
||||||
|
padding: 10px 20px 10px 20px;
|
||||||
|
height: fit-content;
|
||||||
|
background: rgba(255, 255, 255, 0.65);
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
-webkit-backdrop-filter: blur(3px);
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.5),
|
||||||
|
inset 0 -1px 0 rgba(255, 255, 255, 0.1),
|
||||||
|
inset 0 0 8px 4px rgba(255, 255, 255, 0.4);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.allContent{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.glassCard {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0px;
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.allContent{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,29 @@
|
|||||||
import styles from "./LoginPage.module.css";
|
import styles from "./LoginPage.module.css";
|
||||||
import LoginForm from "../../components/form/form.jsx";
|
import LoginForm from "../../components/loginform/loginForm.jsx";
|
||||||
|
import Background from "../../components/background/background.jsx";
|
||||||
|
import { useEffect, useContext } from "react";
|
||||||
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
const { user, loading } = useContext(AuthContext)
|
||||||
<LoginForm />
|
const navigate = useNavigate();
|
||||||
</div>
|
|
||||||
);
|
useEffect(() => {
|
||||||
|
if (!loading && user) {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
}, [loading, user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Background>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<h1 className={styles.title}>Connexion</h1>
|
||||||
|
<LoginForm />
|
||||||
|
</div>
|
||||||
|
</Background>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
overflow: hidden;
|
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius:12px;
|
border-radius: 12px;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 36px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import React, { useContext } from "react";
|
||||||
|
import styles from "./ProfilePage.module.css";
|
||||||
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
|
import formatDate from "../../utils/date/formatDate.js";
|
||||||
|
import Task from "../../components/Task/Task";
|
||||||
|
import SettingsModal from "../../components/SettingsModal/SettingsModal.jsx";
|
||||||
|
|
||||||
|
function ProfilePage() {
|
||||||
|
|
||||||
|
const { user } = useContext(AuthContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={`${styles.profileboard} glassCard`}>
|
||||||
|
<div className={styles.contentWrapper}>
|
||||||
|
<div className={styles.profilePictureContainer}>
|
||||||
|
<img
|
||||||
|
src="/react.svg"
|
||||||
|
alt="Photo de profil"
|
||||||
|
className={styles.profilePicture}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
<div className={styles.headerProfile}>
|
||||||
|
<h2> {user.name} {user.lastname} </h2>
|
||||||
|
</div>
|
||||||
|
<div className={styles.topDescription}>
|
||||||
|
<div className={styles.infoBlock}>
|
||||||
|
<p><strong>Role :</strong> {user.role}</p>
|
||||||
|
<p><strong>Membre depuis :</strong> {formatDate(user.created_at)}</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.infoBlock}>
|
||||||
|
<p><strong>Mail :</strong> {user.email} </p>
|
||||||
|
<p><strong>Téléphone :</strong> {user.phone}</p>
|
||||||
|
</div>
|
||||||
|
<SettingsModal />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Vos Événements :</h2>
|
||||||
|
{user.tasks && user.tasks.length > 0 ? (
|
||||||
|
user.tasks.map((task, index) => (
|
||||||
|
<Task key={index} task={task} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p>Aucune tâche pour le moment.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProfilePage;
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileboard {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePicture {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
margin-left:4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topDescription {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBlock {
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventTasks {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease-out, opacity 0.2s ease;
|
||||||
|
opacity: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eventTasks.expanded {
|
||||||
|
max-height: 500px;
|
||||||
|
opacity: 1;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.description h2{
|
||||||
|
font-size:30px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.profilePicture{
|
||||||
|
width:90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingsBtns {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.contentWrapper {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
width: calc(100% - 170px);
|
||||||
|
margin-left:4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topDescription {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBlock {
|
||||||
|
width: 48%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
.description h2{
|
||||||
|
font-size:30px;
|
||||||
|
}
|
||||||
|
.profilePicture{
|
||||||
|
width:90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.description button{
|
||||||
|
width:50px;
|
||||||
|
height:50px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerProfile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Desktop (1024px et plus) */
|
||||||
|
@media screen and (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileboard {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 30%;
|
||||||
|
height:auto;
|
||||||
|
}
|
||||||
|
.profilePicture{
|
||||||
|
width:90%;
|
||||||
|
}
|
||||||
|
.description {
|
||||||
|
width: calc(100% - 200px);
|
||||||
|
margin-left:4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
.description h2{
|
||||||
|
font-size:30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import styles from "./RegisterPage.module.css";
|
||||||
|
import Background from "../../components/background/background.jsx";
|
||||||
|
import Input from "../../components/ui/input/input.jsx";
|
||||||
|
import Button from "../../components/ui/button/button.jsx";
|
||||||
|
import {useContext, useEffect, useState} from "react";
|
||||||
|
import register from "../../utils/register.js";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import { AuthContext } from "../../contexts/auth/AuthContext.js";
|
||||||
|
import Modal from "../../components/ui/modal/modal.jsx";
|
||||||
|
|
||||||
|
|
||||||
|
function RegisterPage() {
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [phone, setPhone] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [lastName, setLastName] = useState("");
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [title, setTitle] = useState("");
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
|
const { user, loading } = useContext(AuthContext)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && user) {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
}, [loading, user]);
|
||||||
|
|
||||||
|
const handleModal = (t, m) => {
|
||||||
|
setTitle(t)
|
||||||
|
setMessage(m)
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if(!(password === confirmPassword)) handleModal("Mot de passe invalide", "Les mots de passes ne conrrespondent pas");
|
||||||
|
else if(password.length < 8) handleModal("Mot de passe invalide", "Le mot de passe doit faire plus de 8 charactères");
|
||||||
|
else {
|
||||||
|
|
||||||
|
const res = await register(email, password, name, lastName, phone);
|
||||||
|
|
||||||
|
if (res.status === 422) {
|
||||||
|
if (res.errors.email) handleModal("Adresse Email invalide", "Le formet de votre adresse Email n'est pas valide");
|
||||||
|
if (res.errors.phone) handleModal("Numéro de téléphone invalide", "Le formet de votre numéro de téléphone n'est pas valide");
|
||||||
|
} else navigate("/login");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePhone = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
if (value === "" || /^[0-9 +]+$/.test(value)) {
|
||||||
|
setPhone(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return <Background>
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
|
||||||
|
<h1 className={styles.title}>Créer un compte</h1>
|
||||||
|
|
||||||
|
<div className={`glassCard ${styles.inputContainer}`}>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
value={name}
|
||||||
|
placeholder="Prénom"
|
||||||
|
onChange={e => setName(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
value={lastName}
|
||||||
|
placeholder="Nom"
|
||||||
|
onChange={e => setLastName(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
placeholder="Adresse mail"
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input
|
||||||
|
value={phone}
|
||||||
|
placeholder="Numéro de téléphone"
|
||||||
|
onChange={handlePhone}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input password={true} onChange={e => setPassword(e.target.value)} value={password} placeholder={"Mot de passe"}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.formGroup}>
|
||||||
|
<Input password={true} onChange={e => setConfirmPassword(e.target.value)} value={confirmPassword} placeholder={"Confirmer le mot de passe"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.logButton}>
|
||||||
|
<Button variant={"transparent"} onClick={() => navigate("/login")}>Vous avez déjà un compte ?</Button>
|
||||||
|
<Button variant={"default"} onClick={handleSubmit}> Se connecter </Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal open={open} onClose={() => setOpen(false)} title={title}>
|
||||||
|
<p>{message}</p>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
</Background>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RegisterPage;
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 30%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius:12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 36px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:global(.glassCard) {
|
||||||
|
--glass-bg: #ffffff9f;
|
||||||
|
}
|
||||||
|
.logButton{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.formGroup{
|
||||||
|
margin-top:23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
overflow: visible;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputContainer {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import styles from "./StatPage.module.css";
|
import styles from "./StatPage.module.css";
|
||||||
// Importez les composants avec une majuscule
|
|
||||||
import ParticipationChart from "../../components/chart/ParticipationChart.jsx";
|
import ParticipationChart from "../../components/chart/ParticipationChart.jsx";
|
||||||
import IncompleteEvents from "../../components/IncompleteEvent/IncompleteEvent.jsx";
|
import IncompleteEvents from "../../components/IncompleteEvent/IncompleteEvent.jsx";
|
||||||
import PendingMembers from "../../components/PendingMembers/PendingMembers.jsx";
|
import PendingMembers from "../../components/PendingMembers/PendingMembers.jsx";
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import React, {useState} from "react";
|
||||||
|
import styles from "./VolunteersPage.module.css";
|
||||||
|
import formatDate from "../../utils/date/formatDate.js";
|
||||||
|
import { useNavigate } from "react-router";
|
||||||
|
import Button from "../../components/ui/button/button.jsx";
|
||||||
|
|
||||||
|
export default function VolunteerCard({ user }) {
|
||||||
|
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
|
const toggleExpand = () => {
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.profileboard} glassCard`}>
|
||||||
|
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h2>{user?.name} {user?.lastname}</h2>
|
||||||
|
<Button
|
||||||
|
variant={"transparent"}
|
||||||
|
onClick={toggleExpand}
|
||||||
|
className={`${isExpanded ? styles.moreButtonClicked : ""}`}
|
||||||
|
>
|
||||||
|
{">"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.mainContent}>
|
||||||
|
{!isExpanded ? (
|
||||||
|
<div className={styles.basicInfo}>
|
||||||
|
<p><strong>Rôle :</strong> {user?.role}</p>
|
||||||
|
<p><strong>Membre depuis :</strong> {user?.created_at && formatDate(user.created_at)}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.expandedContent}>
|
||||||
|
<div className={styles.profilePictureContainer}>
|
||||||
|
<img src={"/react.svg"} alt="profile" className={styles.profilePicture} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.textInfo}>
|
||||||
|
<div className={styles.basicInfo}>
|
||||||
|
<p><strong>Rôle :</strong> {user?.role}</p>
|
||||||
|
<p><strong>Membre depuis :</strong> {user?.created_at && formatDate(user.created_at)}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.contactInfo}>
|
||||||
|
<p><strong>Email :</strong> {user?.email}</p>
|
||||||
|
{user?.phone && <p><strong>Téléphone :</strong> {user.phone}</p>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isExpanded && (
|
||||||
|
<div className={styles.bottomBar}>
|
||||||
|
<Button
|
||||||
|
onClick={() => navigate(`/profile/${user.id}`)}
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
Voir plus
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import styles from "./VolunteersPage.module.css";
|
||||||
|
import VolunteerCard from "./VolunteerCard.jsx";
|
||||||
|
import getAllUser from "../../utils/users/getAllUsers.js";
|
||||||
|
import ToolBar from "../../components/ToolBar/ToolBar.jsx";
|
||||||
|
import filter from "../../utils/filter.js";
|
||||||
|
import getUserById from "../../utils/users/getUserById.js";
|
||||||
|
|
||||||
|
|
||||||
|
function VolunteerPage() {
|
||||||
|
const [filters, setFilters] = useState({
|
||||||
|
alphabetical: "asc",
|
||||||
|
yearOrder: "desc",
|
||||||
|
});
|
||||||
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const ids = await getAllUser();
|
||||||
|
const usersData = await Promise.all(ids.map(id => getUserById(id)));
|
||||||
|
setUsers(usersData);
|
||||||
|
})()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<ToolBar setFilters={setFilters} filters={filters}/>
|
||||||
|
<div className={styles.usersList}>
|
||||||
|
{filter(users, filters, "user").map((user, index) => (
|
||||||
|
<VolunteerCard key={index} user={user} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VolunteerPage;
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileboard {
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usersList{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.mainContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basicInfo {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandedContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactInfo {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactInfo p {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomBar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePicture {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expandButton {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreButtonClicked {
|
||||||
|
rotate: 90deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profileboard {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profilePictureContainer {
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,21 @@ import Layout from "./layout.jsx";
|
|||||||
import HomePage from "./pages/Home/HomePage";
|
import HomePage from "./pages/Home/HomePage";
|
||||||
import LoginPage from "./pages/Login/LoginPage";
|
import LoginPage from "./pages/Login/LoginPage";
|
||||||
import StatPage from "./pages/Stat/StatPage";
|
import StatPage from "./pages/Stat/StatPage";
|
||||||
import testPage from "./pages/Stat/test.jsx"
|
import RegisterPage from "./pages/Register/RegisterPage.jsx";
|
||||||
|
import VolunteersPage from "./pages/Volunteers/VolunteersPage";
|
||||||
|
import ProfilePage from "./pages/Profile/ProfilePage";
|
||||||
|
import EventsPage from "./pages/Events/EventsPage.jsx";
|
||||||
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
Component: LoginPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/register",
|
||||||
|
Component: RegisterPage,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
Component: Layout,
|
Component: Layout,
|
||||||
@@ -15,17 +27,25 @@ const router = createBrowserRouter([
|
|||||||
Component: HomePage,
|
Component: HomePage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/admin",
|
||||||
Component: LoginPage,
|
Component:StatPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"/profile",
|
||||||
|
Component:ProfilePage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/events",
|
||||||
|
Component: EventsPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/volunteers",
|
||||||
|
Component: VolunteersPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/stat",
|
path: "/stat",
|
||||||
Component:StatPage,
|
Component:StatPage,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/test",
|
|
||||||
Component:testPage,
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import Modal from "../components/ui/modal/modal.jsx";
|
||||||
|
import Button from "../components/ui/button/button.jsx";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'UI/Modal',
|
||||||
|
component: Modal,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
argTypes: {
|
||||||
|
open: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Variable qui gère si la modal est ouverte ou fermée.',
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
control: Object,
|
||||||
|
description: 'Contenu de la modal.',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
control: 'text',
|
||||||
|
description: 'Titre de la modal',
|
||||||
|
},
|
||||||
|
onClose: {
|
||||||
|
action: 'changed',
|
||||||
|
description: 'Fonction appelée pour fermer la modal.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = (args) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button onClick={() => setIsOpen(true)}>Ouvrir</Button>
|
||||||
|
<Modal {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
||||||
|
{args.children}
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {
|
||||||
|
title: "Hello",
|
||||||
|
children: <p>Lorem Ipsum...</p>,
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import getXSRFToken from "../getXSRF.js";
|
||||||
|
|
||||||
|
export default async function logout() {
|
||||||
|
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/logout`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-XSRF-TOKEN': csrfToken
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Code HTTP :', response.status);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Erreur HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: response.status };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default function formatDate(d) {
|
||||||
|
const date = new Date(d);
|
||||||
|
|
||||||
|
const jj = String(date.getUTCDate()).padStart(2, '0');
|
||||||
|
const mm = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||||
|
const aaaa = date.getUTCFullYear();
|
||||||
|
|
||||||
|
return `${jj}/${mm}/${aaaa}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { expect, test } from 'vitest';
|
||||||
|
import formatDate from './formatDate';
|
||||||
|
|
||||||
|
const date = "2025-11-28T11:26:45.000000Z"
|
||||||
|
const res = "28/11/2025"
|
||||||
|
|
||||||
|
test("Format Date", () => {
|
||||||
|
expect(formatDate(date)).toBe(res);
|
||||||
|
})
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export default async function getAllEvents() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/events/`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export default async function GetEventById(id) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/events/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
function filter(elements, filters, type) {
|
||||||
|
const compareDate = (dateA, dateB) => {
|
||||||
|
const dA = new Date(dateA);
|
||||||
|
const dB = new Date(dateB);
|
||||||
|
|
||||||
|
if (filters.yearOrder === "asc") {
|
||||||
|
return dA - dB; // plus ancien -> plus récent
|
||||||
|
} else {
|
||||||
|
return dB - dA; // plus récent -> plus ancien
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return [...elements].sort((a, b) => {
|
||||||
|
let timeComparison;
|
||||||
|
|
||||||
|
if (filters.yearOrder !== null) {
|
||||||
|
if (type === "event") {
|
||||||
|
timeComparison = compareDate(a.start, b.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (type === "user") {
|
||||||
|
timeComparison = compareDate(a.created_at, b.created_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeComparison !== 0 && timeComparison !== undefined) {
|
||||||
|
return timeComparison;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.alphabetical !== null) {
|
||||||
|
return filters.alphabetical === "asc"
|
||||||
|
? a.name.localeCompare(b.name)
|
||||||
|
: b.name.localeCompare(a.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default filter;
|
||||||
@@ -1,12 +1,21 @@
|
|||||||
export default async function getUser() {
|
export default async function getUser() {
|
||||||
await fetch('http://localhost:8000/api/me', {
|
try {
|
||||||
method: 'GET',
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/me`, {
|
||||||
credentials: 'include',
|
method: 'GET',
|
||||||
headers: {
|
credentials: 'include',
|
||||||
'Accept': 'application/json'
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.then(res => res.json())
|
const data = await res.json();
|
||||||
.then(data => console.log('Utilisateur connecté :', data))
|
return data;
|
||||||
.catch(err => console.error('Erreur :', err));
|
} catch (err) {
|
||||||
}
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export default async function getUserNotifications() {
|
export default async function getUserNotifications() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
'http://localhost:8000/api/users/1/notifications',
|
'http://localhost:80/api/users/1/notifications',
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default async function getXSRFToken() {
|
export default async function getXSRFToken() {
|
||||||
const response = await fetch('http://localhost:8000/sanctum/csrf-cookie', {
|
const response = await fetch(`http://${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,31 +3,29 @@ import getXSRFToken from "./getXSRF.js";
|
|||||||
export default async function login(email, password) {
|
export default async function login(email, password) {
|
||||||
const csrfToken = await getXSRFToken();
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
await fetch('http://localhost:8000/api/users/login', {
|
const response = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/login`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-XSRF-TOKEN': csrfToken,
|
'X-XSRF-TOKEN': csrfToken,
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({ email, password })
|
||||||
email: email,
|
|
||||||
password: password
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
console.log('Code HTTP :', response.status);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Erreur HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
console.log('Connexion réussie ! Données utilisateur :', data);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Erreur lors de la connexion :', error.message);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
console.log('Code HTTP :', response.status);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Erreur HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('Connexion réussie ! Données utilisateur :', data);
|
||||||
|
|
||||||
|
return { status: response.status, data };
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import getXSRFToken from "./getXSRF.js";
|
||||||
|
|
||||||
|
export default async function register(email, password, name, lastname, phone) {
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-XSRF-TOKEN': csrfToken,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ email, password, name, lastname, phone }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 422) {
|
||||||
|
return {
|
||||||
|
status: 422,
|
||||||
|
errors: data.errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Erreur HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: response.status, data };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return { status: "error", error };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export default async function searchEvents(query) {
|
||||||
|
try {
|
||||||
|
const encodedQuery = encodeURIComponent(query);
|
||||||
|
const response = await fetch(
|
||||||
|
`http://${import.meta.env.VITE_API_URL}/api/events/search?query=${encodedQuery}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
return data.hits.map(hit => hit.document);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur :", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
export default async function searchUsers(query) {
|
||||||
|
try {
|
||||||
|
const encodedQuery = encodeURIComponent(query);
|
||||||
|
const response = await fetch(
|
||||||
|
`http://localhost:80/api/users/search?query=${encodedQuery}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
return data.hits.map(hit => hit.document);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur :", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export default async function getAllUser() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export default async function getUserById (id) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import getXSRFToken from "../getXSRF.js";
|
||||||
|
|
||||||
|
export default async function updateUser(name, lastname, phone) {
|
||||||
|
|
||||||
|
const csrfToken = await getXSRFToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://${import.meta.env.VITE_API_URL}/api/users/update`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-XSRF-TOKEN': csrfToken,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: name,
|
||||||
|
phone: phone,
|
||||||
|
lastname: lastname,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Erreur serveur : ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erreur :', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +1,48 @@
|
|||||||
|
// vite.config.js
|
||||||
/// <reference types="vitest/config" />
|
/// <reference types="vitest/config" />
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'
|
||||||
|
import { playwright } from '@vitest/browser-playwright'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
|
|
||||||
import { playwright } from '@vitest/browser-playwright';
|
|
||||||
const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
test: {
|
base: '/',
|
||||||
projects: [{
|
test: {
|
||||||
extends: true,
|
projects: [
|
||||||
plugins: [
|
{
|
||||||
// The plugin will run tests for the stories defined in your Storybook config
|
extends: true,
|
||||||
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
plugins: [
|
||||||
storybookTest({
|
storybookTest({
|
||||||
configDir: path.join(dirname, '.storybook')
|
configDir: path.join(dirname, '.storybook'),
|
||||||
})],
|
}),
|
||||||
test: {
|
],
|
||||||
name: 'storybook',
|
test: {
|
||||||
browser: {
|
name: 'storybook',
|
||||||
enabled: true,
|
browser: {
|
||||||
headless: true,
|
enabled: true,
|
||||||
provider: playwright({}),
|
headless: true,
|
||||||
instances: [{
|
provider: playwright({}),
|
||||||
browser: 'chromium'
|
instances: [{ browser: 'chromium' }],
|
||||||
}]
|
},
|
||||||
},
|
setupFiles: ['.storybook/vitest.setup.js'],
|
||||||
setupFiles: ['.storybook/vitest.setup.js']
|
},
|
||||||
}
|
},
|
||||||
}]
|
{
|
||||||
},
|
test: {
|
||||||
server: {
|
name: 'unit',
|
||||||
host: true,
|
include: [
|
||||||
}
|
'src/**/*.test.js',
|
||||||
});
|
'src/**/*.test.ts',
|
||||||
|
'utils/**/*.test.js',
|
||||||
|
],
|
||||||
|
environment: 'node',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||