From 2a94172777716c19e3cc53a820c5b915c42f5eff Mon Sep 17 00:00:00 2001
From: alexandre-kakal-akarah
Date: Sat, 18 May 2024 17:05:08 +0200
Subject: [PATCH 1/4] WIP: journey multi step form
---
next.config.mjs | 11 +-
package-lock.json | 355 +++++++++++++++++-
package.json | 3 +
prisma/schema.prisma | 8 +-
src/app/(app)/parcours/[id]/page.tsx | 10 +
src/app/(app)/parcours/page.tsx | 21 ++
src/components/AddButton.tsx | 24 ++
src/components/Icons.tsx | 20 +-
src/components/JourneyCard.tsx | 54 +++
src/components/form/StepCounter.tsx | 26 ++
src/components/form/journey/JourneyForm.tsx | 109 ++++++
.../form/journey/steps/FirstStep.tsx | 65 ++++
.../form/journey/steps/SecondStep.tsx | 188 ++++++++++
src/components/form/journey/steps/Steps.tsx | 63 ++++
.../form/journey/steps/ThirdStep.tsx | 31 ++
.../form/journey/steps/TreasureStep.tsx | 58 +++
src/components/ui/select.tsx | 160 ++++++++
src/components/ui/textarea.tsx | 24 ++
src/store/example.ts | 13 -
src/store/journeyFormStore.ts | 21 ++
src/validators/journeyFormSchema.ts | 56 +++
21 files changed, 1297 insertions(+), 23 deletions(-)
create mode 100644 src/app/(app)/parcours/[id]/page.tsx
create mode 100644 src/app/(app)/parcours/page.tsx
create mode 100644 src/components/AddButton.tsx
create mode 100644 src/components/JourneyCard.tsx
create mode 100644 src/components/form/StepCounter.tsx
create mode 100644 src/components/form/journey/JourneyForm.tsx
create mode 100644 src/components/form/journey/steps/FirstStep.tsx
create mode 100644 src/components/form/journey/steps/SecondStep.tsx
create mode 100644 src/components/form/journey/steps/Steps.tsx
create mode 100644 src/components/form/journey/steps/ThirdStep.tsx
create mode 100644 src/components/form/journey/steps/TreasureStep.tsx
create mode 100644 src/components/ui/select.tsx
create mode 100644 src/components/ui/textarea.tsx
delete mode 100644 src/store/example.ts
create mode 100644 src/store/journeyFormStore.ts
create mode 100644 src/validators/journeyFormSchema.ts
diff --git a/next.config.mjs b/next.config.mjs
index 4678774..30acda4 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+const nextConfig = {
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "via.assets.so",
+ },
+ ],
+ },
+};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index dcd9478..1241f8e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,16 +12,19 @@
"@prisma/client": "^5.13.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
+ "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
+ "framer-motion": "^11.2.4",
"lucide-react": "^0.376.0",
"next": "14.2.3",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
+ "react-spring-bottom-sheet": "^3.4.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8",
@@ -302,6 +305,11 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@juggle/resize-observer": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
+ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
+ },
"node_modules/@next/env": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
@@ -554,6 +562,14 @@
"@prisma/debug": "5.13.0"
}
},
+ "node_modules/@radix-ui/number": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
+ "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
"node_modules/@radix-ui/primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
@@ -585,6 +601,32 @@
}
}
},
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
+ "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
@@ -619,6 +661,23 @@
}
}
},
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
+ "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
@@ -868,6 +927,49 @@
}
}
},
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz",
+ "integrity": "sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/number": "1.0.1",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.4",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-popper": "1.1.3",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-visually-hidden": "1.0.3",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-slot": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@@ -956,6 +1058,23 @@
}
}
},
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz",
+ "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-rect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
@@ -992,6 +1111,29 @@
}
}
},
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
+ "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/rect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
@@ -1084,6 +1226,11 @@
"@types/react": "*"
}
},
+ "node_modules/@types/warning": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
+ "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
+ },
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
@@ -1556,6 +1703,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/body-scroll-lock": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
+ "integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg=="
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2846,6 +2998,14 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
+ "node_modules/focus-trap": {
+ "version": "6.9.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.4.tgz",
+ "integrity": "sha512-v2NTsZe2FF59Y+sDykKY+XjqZ0cPfhq/hikWVL88BqLivnNiEffAsac6rP6H45ff9wG9LL5ToiDqrLEP9GX9mw==",
+ "dependencies": {
+ "tabbable": "^5.3.3"
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -2870,6 +3030,30 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/framer-motion": {
+ "version": "11.2.4",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.4.tgz",
+ "integrity": "sha512-D+EXd0lspaZijv3BJhAcSsyGz+gnvoEdnf+QWkPZdhoFzbeX/2skrH9XSVFb0osgUnCajW8x1frjhLuKwa/Reg==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -4492,7 +4676,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -4581,8 +4764,7 @@
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-remove-scroll": {
"version": "2.5.5",
@@ -4629,6 +4811,110 @@
}
}
},
+ "node_modules/react-spring": {
+ "version": "8.0.27",
+ "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
+ "integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
+ "dependencies": {
+ "@babel/runtime": "^7.3.1",
+ "prop-types": "^15.5.8"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0"
+ }
+ },
+ "node_modules/react-spring-bottom-sheet": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/react-spring-bottom-sheet/-/react-spring-bottom-sheet-3.4.1.tgz",
+ "integrity": "sha512-yDFqiPMm/fjefjnOe6Q9zxccbCl6HMUKsK5bWgfGHJIj4zmXVKio5d4icQvmOLuwpuCA2pwv4J6nGWS6fUZidQ==",
+ "dependencies": {
+ "@juggle/resize-observer": "^3.2.0",
+ "@reach/portal": "^0.13.0",
+ "@xstate/react": "^1.2.0",
+ "body-scroll-lock": "^3.1.5",
+ "focus-trap": "^6.2.2",
+ "react-spring": "^8.0.27",
+ "react-use-gesture": "^8.0.1",
+ "xstate": "^4.15.1"
+ },
+ "peerDependencies": {
+ "react": "^16.14.0 || 17 || 18"
+ }
+ },
+ "node_modules/react-spring-bottom-sheet/node_modules/@reach/portal": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz",
+ "integrity": "sha512-g74BnCdtuTGthzzHn2cWW+bcyIYb0iIE/yRsm89i8oNzNgpopbkh9UY8TPbhNlys52h7U60s4kpRTmcq+JqsTA==",
+ "dependencies": {
+ "@reach/utils": "0.13.2",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || 17.x",
+ "react-dom": "^16.8.0 || 17.x"
+ }
+ },
+ "node_modules/react-spring-bottom-sheet/node_modules/@reach/portal/node_modules/@reach/utils": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz",
+ "integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==",
+ "dependencies": {
+ "@types/warning": "^3.0.0",
+ "tslib": "^2.1.0",
+ "warning": "^4.0.3"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || 17.x",
+ "react-dom": "^16.8.0 || 17.x"
+ }
+ },
+ "node_modules/react-spring-bottom-sheet/node_modules/@xstate/react": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@xstate/react/-/react-1.6.3.tgz",
+ "integrity": "sha512-NCUReRHPGvvCvj2yLZUTfR0qVp6+apc8G83oXSjN4rl89ZjyujiKrTff55bze/HrsvCsP/sUJASf2n0nzMF1KQ==",
+ "dependencies": {
+ "use-isomorphic-layout-effect": "^1.0.0",
+ "use-subscription": "^1.3.0"
+ },
+ "peerDependencies": {
+ "@xstate/fsm": "^1.0.0",
+ "react": "^16.8.0 || ^17.0.0",
+ "xstate": "^4.11.0"
+ },
+ "peerDependenciesMeta": {
+ "@xstate/fsm": {
+ "optional": true
+ },
+ "xstate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-spring-bottom-sheet/node_modules/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+ "peer": true,
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "scheduler": "^0.20.2"
+ },
+ "peerDependencies": {
+ "react": "17.0.2"
+ }
+ },
+ "node_modules/react-spring-bottom-sheet/node_modules/scheduler": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+ "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+ "peer": true,
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
@@ -4651,6 +4937,15 @@
}
}
},
+ "node_modules/react-use-gesture": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-8.0.1.tgz",
+ "integrity": "sha512-CXzUNkulUdgouaAlvAsC5ZVo0fi9KGSBSk81WrE4kOIcJccpANe9zZkAYr5YZZhqpicIFxitsrGVS4wmoMun9A==",
+ "deprecated": "This package is no longer maintained. Please use @use-gesture/react instead",
+ "peerDependencies": {
+ "react": ">= 16.8.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -5234,6 +5529,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/tabbable": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
+ "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA=="
+ },
"node_modules/tailwind-merge": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz",
@@ -5578,6 +5878,19 @@
}
}
},
+ "node_modules/use-isomorphic-layout-effect": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
+ "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
@@ -5599,6 +5912,25 @@
}
}
},
+ "node_modules/use-subscription": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.8.2.tgz",
+ "integrity": "sha512-yC2ShScvQ1lAGRp5Y6pz1MqBIU81REfJ/sQIe16BCgKK9mRlZCnU90uY0alKsN6e/Next0vXTsvH3HbAfdH68w==",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/use-subscription/node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
@@ -5618,6 +5950,14 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"devOptional": true
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5813,6 +6153,15 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
+ "node_modules/xstate": {
+ "version": "4.38.3",
+ "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.38.3.tgz",
+ "integrity": "sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/xstate"
+ }
+ },
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
diff --git a/package.json b/package.json
index af036ae..df00cd7 100644
--- a/package.json
+++ b/package.json
@@ -16,16 +16,19 @@
"@prisma/client": "^5.13.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
+ "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
+ "framer-motion": "^11.2.4",
"lucide-react": "^0.376.0",
"next": "14.2.3",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
+ "react-spring-bottom-sheet": "^3.4.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index e19b93e..deb5d71 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -91,9 +91,9 @@ model Journey {
physicalDifficulty Int
lastCompletion DateTime?
mobilityImpaired String
- PartiallySighted String
- PartiallyDeaf String
- CognitivelyImpaired String
+ partiallySighted String
+ partiallyDeaf String
+ cognitivelyImpaired String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
steps Step[]
@@ -132,4 +132,4 @@ model Comment {
updatedAt DateTime @updatedAt
user User @relation(fields: [authorId], references: [id])
journey Journey @relation(fields: [journeyId], references: [id])
-}
\ No newline at end of file
+}
diff --git a/src/app/(app)/parcours/[id]/page.tsx b/src/app/(app)/parcours/[id]/page.tsx
new file mode 100644
index 0000000..7ec18da
--- /dev/null
+++ b/src/app/(app)/parcours/[id]/page.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+
+type Params = { id: string };
+
+const JourneyDetail = ({ params }: { params: Params }) => {
+ // get journey by id
+ return JourneyDetail;
+};
+
+export default JourneyDetail;
diff --git a/src/app/(app)/parcours/page.tsx b/src/app/(app)/parcours/page.tsx
new file mode 100644
index 0000000..4a8ccd1
--- /dev/null
+++ b/src/app/(app)/parcours/page.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import JourneyCard from "@/components/JourneyCard";
+import AddButton from "@/components/AddButton";
+import JourneyForm from "@/components/form/journey/JourneyForm";
+
+const Parcours = () => {
+ return (
+
+ {
+ // to do: fetch parcours
+ Array.from({ length: 10 }).map((_, i) => (
+
+ ))
+ }
+
+
+
+ );
+};
+
+export default Parcours;
diff --git a/src/components/AddButton.tsx b/src/components/AddButton.tsx
new file mode 100644
index 0000000..13be050
--- /dev/null
+++ b/src/components/AddButton.tsx
@@ -0,0 +1,24 @@
+"use client";
+import React from "react";
+import { useJourneyFormStore } from "@/store/journeyFormStore";
+import { Icons } from "./Icons";
+
+type AddButtonProps = {
+ action: "journey" | "event";
+};
+
+const AddButton = ({ action }: AddButtonProps) => {
+ const { showModal } = useJourneyFormStore();
+ return (
+ {
+ action === "journey" ? showModal() : showModal();
+ }}
+ className="fixed bottom-24 right-[20px] cursor-pointer rounded-lg bg-slate-500 p-4 shadow-lg"
+ >
+
+
+ );
+};
+
+export default AddButton;
diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx
index bfb7227..134ebb2 100644
--- a/src/components/Icons.tsx
+++ b/src/components/Icons.tsx
@@ -1,4 +1,4 @@
-import { LucideProps } from "lucide-react";
+import { LucideProps, Plus } from "lucide-react";
export const Icons = {
flag: (props: LucideProps) => (
@@ -44,7 +44,7 @@ export const Icons = {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
-
+
),
+ mapPin: (props: LucideProps) => (
+
+ ),
+ plus: (props: LucideProps) => ,
};
diff --git a/src/components/JourneyCard.tsx b/src/components/JourneyCard.tsx
new file mode 100644
index 0000000..5c69bf7
--- /dev/null
+++ b/src/components/JourneyCard.tsx
@@ -0,0 +1,54 @@
+import React from "react";
+import Image from "next/image";
+import Link from "next/link";
+import { Icons } from "./Icons";
+
+// type JourneyCardProps = {
+// id: string;
+// title: string;
+// location: string;
+// description: string;
+// image: string;
+// cluesDifficulty: number;
+// physicalDifficulty: number;
+// commentsCount: number;
+// rating: number;
+// };
+
+const JourneyCard = (/* {
+ id,
+ title,
+ location,
+ description,
+ cluesDifficulty,
+ commentsCount,
+ image,
+ physicalDifficulty,
+ rating,
+}: JourneyCardProps */) => {
+ const id = "1";
+ return (
+
+
+
+
Titre
+ {/* to do: add rating */}
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi in
+ sodales mauris.
+
+
+
+ );
+};
+
+export default JourneyCard;
diff --git a/src/components/form/StepCounter.tsx b/src/components/form/StepCounter.tsx
new file mode 100644
index 0000000..120300b
--- /dev/null
+++ b/src/components/form/StepCounter.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+type StepCounterProps = {
+ step: number;
+ totalSteps: number;
+};
+
+const StepCounter = ({ step, totalSteps }: StepCounterProps) => {
+ return (
+
+ {[...Array(totalSteps)].map((_, index) => (
+
+ ))}
+
+ Étape {step + 1}/{totalSteps}
+
+
+ );
+};
+
+export default StepCounter;
diff --git a/src/components/form/journey/JourneyForm.tsx b/src/components/form/journey/JourneyForm.tsx
new file mode 100644
index 0000000..e2dda91
--- /dev/null
+++ b/src/components/form/journey/JourneyForm.tsx
@@ -0,0 +1,109 @@
+"use client";
+import React, { useState } from "react";
+import {
+ journeyFormSchema,
+ firstStepSchema,
+ secondStepSchema,
+ thirdStepSchema,
+ forthStepSchema,
+} from "@/validators/journeyFormSchema";
+import { z } from "zod";
+import { useJourneyFormStore } from "@/store/journeyFormStore";
+import { useForm, SubmitHandler } from "react-hook-form";
+import { Form } from "@/components/ui/form";
+import { Button } from "@/components/ui/button";
+import { zodResolver } from "@hookform/resolvers/zod";
+import StepCounter from "../StepCounter";
+import Steps from "./steps/Steps";
+
+export type JourneyFormValues = z.infer;
+type FieldName = keyof JourneyFormValues;
+
+const firstStepFields = Object.keys(firstStepSchema.shape);
+const secondStepFields = Object.keys(secondStepSchema.shape);
+const thirdStepFields = Object.keys(thirdStepSchema.shape);
+const forthStepFields = Object.keys(forthStepSchema.shape);
+
+const steps = [
+ {
+ fields: firstStepFields,
+ },
+ {
+ fields: secondStepFields,
+ },
+ {
+ fields: thirdStepFields,
+ },
+ {
+ fields: forthStepFields,
+ },
+];
+
+const JourneyForm = () => {
+ const { isVisible, hideModal } = useJourneyFormStore();
+ const [formStatus, setFormStatus] = useState<"idle" | "errored">("idle");
+ const [currentStep, setCurrentStep] = useState(0);
+ const [dir, setDir] = useState<"ltr" | "rtl">("rtl");
+
+ const form = useForm({
+ resolver: zodResolver(journeyFormSchema),
+ });
+
+ const processForm: SubmitHandler = async (data) => {
+ console.log(data);
+ };
+
+ const next = async () => {
+ const fields = steps[currentStep].fields;
+ const output = await form.trigger(fields as FieldName[]);
+ if (!output) return;
+ if (currentStep < steps.length) {
+ if (currentStep === steps.length - 1) {
+ await form.handleSubmit(processForm)();
+ }
+
+ if (currentStep < steps.length - 1) {
+ setCurrentStep((step) => step + 1);
+ setDir("ltr");
+ }
+ }
+ };
+
+ const prev = () => {
+ if (currentStep > 0) {
+ setCurrentStep((step) => step - 1);
+ setDir("rtl");
+ }
+ };
+
+ const dismissModal = () => {
+ hideModal();
+ setCurrentStep(0);
+ form.reset();
+ setFormStatus("idle");
+ };
+
+ if (!isVisible) return null;
+
+ return (
+
+
+
+ )}
+
+
+ );
+};
+
+export default JourneyForm;
diff --git a/src/components/form/journey/steps/FirstStep.tsx b/src/components/form/journey/steps/FirstStep.tsx
new file mode 100644
index 0000000..ed2f824
--- /dev/null
+++ b/src/components/form/journey/steps/FirstStep.tsx
@@ -0,0 +1,65 @@
+import React from "react";
+import { UseFormReturn } from "react-hook-form";
+import { JourneyFormValues } from "../JourneyForm";
+import {
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+
+type FirstStepProps = {
+ next: () => Promise;
+ form: UseFormReturn;
+};
+
+const FirstStep = ({ form, next }: FirstStepProps) => {
+ return (
+
+ );
+};
+
+export default FirstStep;
diff --git a/src/components/form/journey/steps/SecondStep.tsx b/src/components/form/journey/steps/SecondStep.tsx
new file mode 100644
index 0000000..144fba6
--- /dev/null
+++ b/src/components/form/journey/steps/SecondStep.tsx
@@ -0,0 +1,188 @@
+import React from "react";
+import { UseFormReturn } from "react-hook-form";
+import { JourneyFormValues } from "../JourneyForm";
+import {
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
+} from "@/components/ui/form";
+import { Textarea } from "@/components/ui/textarea";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+
+type SecondStepProps = {
+ prev: () => void;
+ next: () => Promise;
+ form: UseFormReturn;
+};
+
+const SecondStep = ({ form, next, prev }: SecondStepProps) => {
+ return (
+
+
Que doit-on savoir sur le parcours ?
+
(
+
+
+ <>
+
+
+ >
+
+
+
+ )}
+ />
+
+ {/* TO DO: radio fields on physical difficulty and clues difficulty */}
+
+ (
+
+
+
+
+
+ )}
+ />
+
+ (
+
+
+
+
+
+ )}
+ />
+
+ (
+
+
+
+
+
+ )}
+ />
+
+ (
+
+
+
+
+
+ )}
+ />
+
+
+
+
+
+
+ );
+};
+
+export default SecondStep;
diff --git a/src/components/form/journey/steps/Steps.tsx b/src/components/form/journey/steps/Steps.tsx
new file mode 100644
index 0000000..c9be48b
--- /dev/null
+++ b/src/components/form/journey/steps/Steps.tsx
@@ -0,0 +1,63 @@
+"use client";
+
+import { UseFormReturn } from "react-hook-form";
+import { JourneyFormValues } from "../JourneyForm";
+import FirstStep from "./FirstStep";
+import SecondStep from "./SecondStep";
+import ThirdStep from "./ThirdStep";
+import TreasureStep from "./TreasureStep";
+
+import { motion } from "framer-motion";
+
+type StepsProps = {
+ step: number;
+ next: () => Promise;
+ prev: () => void;
+ form: UseFormReturn;
+ dir: "ltr" | "rtl";
+};
+
+const Steps = ({ step, next, prev, form, dir }: StepsProps) => {
+ return (
+ <>
+ {step === 0 && (
+
+
+
+ )}
+ {step === 1 && (
+
+
+
+ )}
+ {step === 2 && (
+
+
+
+ )}
+ {step === 3 && (
+
+
+
+ )}
+ >
+ );
+};
+
+export default Steps;
diff --git a/src/components/form/journey/steps/ThirdStep.tsx b/src/components/form/journey/steps/ThirdStep.tsx
new file mode 100644
index 0000000..5d698c3
--- /dev/null
+++ b/src/components/form/journey/steps/ThirdStep.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { UseFormReturn } from "react-hook-form";
+import { JourneyFormValues } from "../JourneyForm";
+import { Button } from "@/components/ui/button";
+
+type ThirdStepProps = {
+ prev: () => void;
+ next: () => Promise;
+ form: UseFormReturn;
+};
+
+const ThirdStep = ({ form, next, prev }: ThirdStepProps) => {
+ return (
+
+
Quelles sont les étapes ?
+
+
+
+
+
+
+ );
+};
+
+export default ThirdStep;
diff --git a/src/components/form/journey/steps/TreasureStep.tsx b/src/components/form/journey/steps/TreasureStep.tsx
new file mode 100644
index 0000000..208776b
--- /dev/null
+++ b/src/components/form/journey/steps/TreasureStep.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+import { UseFormReturn } from "react-hook-form";
+import { JourneyFormValues } from "../JourneyForm";
+import {
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+
+type TreasureStepProps = {
+ prev: () => void;
+ next: () => Promise;
+ form: UseFormReturn;
+};
+
+const TreasureStep = ({ form, next, prev }: TreasureStepProps) => {
+ return (
+
+
Définissez le trésor
+
(
+
+
+ <>
+
+
+ >
+
+
+
+ )}
+ />
+
+
+
+
+
+
+ );
+};
+
+export default TreasureStep;
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
new file mode 100644
index 0000000..cbe5a36
--- /dev/null
+++ b/src/components/ui/select.tsx
@@ -0,0 +1,160 @@
+"use client"
+
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { Check, ChevronDown, ChevronUp } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..9f9a6dc
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/src/store/example.ts b/src/store/example.ts
deleted file mode 100644
index eddb2d7..0000000
--- a/src/store/example.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { create } from "zustand";
-
-interface ExampleStore {
- count: number;
- increment: () => void;
- decrement: () => void;
-}
-
-export const useExampleStore = create((set) => ({
- count: 0,
- increment: () => set((state) => ({ count: state.count + 1 })),
- decrement: () => set((state) => ({ count: state.count - 1 })),
-}));
diff --git a/src/store/journeyFormStore.ts b/src/store/journeyFormStore.ts
new file mode 100644
index 0000000..d42d020
--- /dev/null
+++ b/src/store/journeyFormStore.ts
@@ -0,0 +1,21 @@
+import { create } from "zustand";
+
+interface JourneyFormStore {
+ isVisible: boolean;
+ showModal: () => void;
+ hideModal: () => void;
+}
+
+export const useJourneyFormStore = create((set) => ({
+ isVisible: false,
+ showModal: () => {
+ const body = document.querySelector("body");
+ body?.classList.add("overflow-hidden");
+ set({ isVisible: true });
+ },
+ hideModal: () => {
+ const body = document.querySelector("body");
+ body?.classList.remove("overflow-hidden");
+ set({ isVisible: false });
+ },
+}));
diff --git a/src/validators/journeyFormSchema.ts b/src/validators/journeyFormSchema.ts
new file mode 100644
index 0000000..2ced4ca
--- /dev/null
+++ b/src/validators/journeyFormSchema.ts
@@ -0,0 +1,56 @@
+import { z } from "zod";
+
+export const firstStepSchema = z.object({
+ title: z.string({ required_error: "Ce champ est requis" }),
+ description: z.string({ required_error: "Ce champ est requis" }),
+ // image: z.string(),
+});
+
+export const secondStepSchema = z.object({
+ requirement: z.string({ required_error: "Ce champ est requis" }),
+ physicalDifficulty: z.enum(["easy", "medium", "hard"], {
+ required_error: "Vous devez sélectionner une option",
+ }),
+ cluesDifficulty: z.enum(["easy", "medium", "hard"], {
+ required_error: "Vous devez sélectionner une option",
+ }),
+ mobilityImpaired: z.enum(
+ ["undefined", "unaccessible", "partiallyAccessible", "accessible"],
+ {
+ required_error: "Vous devez sélectionner une option",
+ }
+ ),
+ partiallySighted: z.enum(
+ ["undefined", "unaccessible", "partiallyAccessible", "accessible"],
+ {
+ required_error: "Vous devez sélectionner une option",
+ }
+ ),
+ partiallyDeaf: z.enum(
+ ["undefined", "unaccessible", "partiallyAccessible", "accessible"],
+ {
+ required_error: "Vous devez sélectionner une option",
+ }
+ ),
+ cognitivelyImpaired: z.enum(
+ ["undefined", "unaccessible", "partiallyAccessible", "accessible"],
+ {
+ required_error: "Vous devez sélectionner une option",
+ }
+ ),
+});
+
+export const thirdStepSchema = z.object({
+ // to do: add journey steps schema
+});
+
+export const forthStepSchema = z.object({
+ treasure: z.string(),
+});
+
+export const journeyFormSchema = z.object({
+ ...firstStepSchema.shape,
+ ...secondStepSchema.shape,
+ ...thirdStepSchema.shape,
+ ...forthStepSchema.shape,
+});
From 73f7673883ec9cf454d36c7815b90ff0710dc26b Mon Sep 17 00:00:00 2001
From: alexandre-kakal-akarah
Date: Wed, 22 May 2024 12:03:03 +0200
Subject: [PATCH 2/4] WIP: crud step client side with bottomsheet form
---
.eslintrc.json | 3 +-
package-lock.json | 401 +++++++-----------
package.json | 16 +-
prisma/schema.prisma | 3 +-
prisma/seed.ts | 40 +-
public/img/pin.png | Bin 0 -> 394 bytes
src/components/form/Register.tsx | 2 +-
.../form/journey/BottomSheetModal.tsx | 201 +++++++++
src/components/form/journey/JourneyForm.tsx | 14 +-
.../form/journey/steps/ThirdStep.tsx | 54 ++-
src/components/map/LeafletMap.tsx | 5 +
src/components/map/LocationMarker.tsx | 53 +++
src/components/map/Map.tsx | 49 +++
src/components/ui/button.tsx | 2 +-
src/components/ui/calendar.tsx | 20 +-
src/components/ui/form.tsx | 93 ++--
src/components/ui/input.tsx | 12 +-
src/components/ui/label.tsx | 18 +-
src/components/ui/popover.tsx | 18 +-
src/components/ui/select.tsx | 46 +-
src/components/ui/textarea.tsx | 12 +-
src/lib/{utils.ts => tailwindUtils.ts} | 0
src/store/journeyFormStore.ts | 50 +++
src/validators/journeyFormSchema.ts | 46 +-
src/validators/stepFormSchema.ts | 90 ++++
25 files changed, 859 insertions(+), 389 deletions(-)
create mode 100644 public/img/pin.png
create mode 100644 src/components/form/journey/BottomSheetModal.tsx
create mode 100644 src/components/map/LeafletMap.tsx
create mode 100644 src/components/map/LocationMarker.tsx
create mode 100644 src/components/map/Map.tsx
rename src/lib/{utils.ts => tailwindUtils.ts} (100%)
create mode 100644 src/validators/stepFormSchema.ts
diff --git a/.eslintrc.json b/.eslintrc.json
index 207de6a..8b476e0 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -6,6 +6,7 @@
"prettier"
],
"rules": {
- "react/no-unescaped-entities": "off"
+ "react/no-unescaped-entities": "off",
+ "react-hooks/exhaustive-deps": "off"
}
}
diff --git a/package-lock.json b/package-lock.json
index 1241f8e..d882dc3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,8 @@
"name": "next-patrigma",
"version": "0.1.0",
"dependencies": {
- "@hookform/resolvers": "^3.3.4",
- "@prisma/client": "^5.13.0",
+ "@hookform/resolvers": "^3.4.2",
+ "@prisma/client": "^5.14.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-select": "^2.0.0",
@@ -18,29 +18,31 @@
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"framer-motion": "^11.2.4",
- "lucide-react": "^0.376.0",
+ "lucide-react": "^0.378.0",
"next": "14.2.3",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
- "react-spring-bottom-sheet": "^3.4.1",
+ "react-leaflet": "^4.2.1",
+ "react-modal-sheet": "^3.1.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8",
"zustand": "^4.5.2"
},
"devDependencies": {
- "@types/node": "^20.12.11",
+ "@types/leaflet": "^1.9.12",
+ "@types/node": "^20.12.12",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8.57.0",
"eslint-config-next": "14.2.3",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
- "eslint-plugin-tailwindcss": "^3.15.1",
+ "eslint-plugin-tailwindcss": "^3.15.2",
"postcss": "^8",
- "prisma": "^5.13.0",
+ "prisma": "^5.14.0",
"tailwindcss": "^3.4.3",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
@@ -181,9 +183,9 @@
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
},
"node_modules/@hookform/resolvers": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
- "integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.2.tgz",
+ "integrity": "sha512-1m9uAVIO8wVf7VCDAGsuGA0t6Z3m6jVGAN50HkV9vYLl0yixKK/Z1lr01vaRvYCkIKGoy1noVRxMzQYb4y/j1Q==",
"peerDependencies": {
"react-hook-form": "^7.0.0"
}
@@ -305,11 +307,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@juggle/resize-observer": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
- "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
- },
"node_modules/@next/env": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
@@ -501,9 +498,9 @@
}
},
"node_modules/@prisma/client": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.13.0.tgz",
- "integrity": "sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.14.0.tgz",
+ "integrity": "sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==",
"hasInstallScript": true,
"engines": {
"node": ">=16.13"
@@ -518,48 +515,48 @@
}
},
"node_modules/@prisma/debug": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz",
- "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.14.0.tgz",
+ "integrity": "sha512-iq56qBZuFfX3fCxoxT8gBX33lQzomBU0qIUaEj1RebsKVz1ob/BVH1XSBwwwvRVtZEV1b7Fxx2eVu34Ge/mg3w==",
"devOptional": true
},
"node_modules/@prisma/engines": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz",
- "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.14.0.tgz",
+ "integrity": "sha512-lgxkKZ6IEygVcw6IZZUlPIfLQ9hjSYAtHjZ5r64sCLDgVzsPFCi2XBBJgzPMkOQ5RHzUD4E/dVdpn9+ez8tk1A==",
"devOptional": true,
"hasInstallScript": true,
"dependencies": {
- "@prisma/debug": "5.13.0",
- "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b",
- "@prisma/fetch-engine": "5.13.0",
- "@prisma/get-platform": "5.13.0"
+ "@prisma/debug": "5.14.0",
+ "@prisma/engines-version": "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48",
+ "@prisma/fetch-engine": "5.14.0",
+ "@prisma/get-platform": "5.14.0"
}
},
"node_modules/@prisma/engines-version": {
- "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b",
- "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz",
- "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==",
+ "version": "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48",
+ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48.tgz",
+ "integrity": "sha512-ip6pNkRo1UxWv+6toxNcYvItNYaqQjXdFNGJ+Nuk2eYtRoEdoF13wxo7/jsClJFFenMPVNVqXQDV0oveXnR1cA==",
"devOptional": true
},
"node_modules/@prisma/fetch-engine": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz",
- "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.14.0.tgz",
+ "integrity": "sha512-VrheA9y9DMURK5vu8OJoOgQpxOhas3qF0IBHJ8G/0X44k82kc8E0w98HCn2nhnbOOMwbWsJWXfLC2/F8n5u0gQ==",
"devOptional": true,
"dependencies": {
- "@prisma/debug": "5.13.0",
- "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b",
- "@prisma/get-platform": "5.13.0"
+ "@prisma/debug": "5.14.0",
+ "@prisma/engines-version": "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48",
+ "@prisma/get-platform": "5.14.0"
}
},
"node_modules/@prisma/get-platform": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz",
- "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.14.0.tgz",
+ "integrity": "sha512-/yAyBvcEjRv41ynZrhdrPtHgk47xLRRq/o5eWGcUpBJ1YrUZTYB8EoPiopnP7iQrMATK8stXQdPOoVlrzuTQZw==",
"devOptional": true,
"dependencies": {
- "@prisma/debug": "5.13.0"
+ "@prisma/debug": "5.14.0"
}
},
"node_modules/@radix-ui/number": {
@@ -1142,6 +1139,64 @@
"@babel/runtime": "^7.13.10"
}
},
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz",
+ "integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.24.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.0.tgz",
+ "integrity": "sha512-JAxkPhK5fCvFVNY2YG3TW3m1nTzwRcbz7iyTSkUzLFat4N4LZ7Kzh7NMHsgeE/oMOxd8zLY+XsUxMu/E/2GujA==",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.3",
+ "@react-stately/utils": "^3.10.0",
+ "@react-types/shared": "^3.23.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-leaflet/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz",
+ "integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.23.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz",
+ "integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
+ }
+ },
"node_modules/@rushstack/eslint-patch": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz",
@@ -1186,16 +1241,31 @@
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"devOptional": true
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.14",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
+ "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==",
+ "dev": true
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "node_modules/@types/leaflet": {
+ "version": "1.9.12",
+ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz",
+ "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==",
+ "dev": true,
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
"node_modules/@types/node": {
- "version": "20.12.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
- "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
"devOptional": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -1226,11 +1296,6 @@
"@types/react": "*"
}
},
- "node_modules/@types/warning": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
- "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q=="
- },
"node_modules/@typescript-eslint/parser": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
@@ -1703,11 +1768,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/body-scroll-lock": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-3.1.5.tgz",
- "integrity": "sha512-Yi1Xaml0EvNA0OYWxXiYNqY24AfWkbA6w5vxE7GWxtKfzIbZM+Qw+aSmkgsbWzbHiy/RCSkUZBplVxTA+E4jJg=="
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2785,9 +2845,9 @@
}
},
"node_modules/eslint-plugin-tailwindcss": {
- "version": "3.15.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.15.1.tgz",
- "integrity": "sha512-4RXRMIaMG07C2TBEW1k0VM4+dDazz1kxcZhkK4zirvmHGZTA4jnlSO2kq5mamuSPi+Wo17dh2SlC8IyFBuCd7Q==",
+ "version": "3.15.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.15.2.tgz",
+ "integrity": "sha512-+HJfWcyP5B/e8r8qVSaTbf2i4+HsESJJsue66qFHRstV11CNTfdaDD9zkCVA1pm2EplBZ/BSJ3Htfzvb4YTVKw==",
"dev": true,
"dependencies": {
"fast-glob": "^3.2.5",
@@ -2998,14 +3058,6 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
- "node_modules/focus-trap": {
- "version": "6.9.4",
- "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.4.tgz",
- "integrity": "sha512-v2NTsZe2FF59Y+sDykKY+XjqZ0cPfhq/hikWVL88BqLivnNiEffAsac6rP6H45ff9wG9LL5ToiDqrLEP9GX9mw==",
- "dependencies": {
- "tabbable": "^5.3.3"
- }
- },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -3958,6 +4010,12 @@
"node": ">=0.10"
}
},
+ "node_modules/leaflet": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
+ "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
+ "peer": true
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -4025,9 +4083,9 @@
}
},
"node_modules/lucide-react": {
- "version": "0.376.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.376.0.tgz",
- "integrity": "sha512-g91IX3ERD6yUR1TL2dsL4BkcGygpZz/EsqjAeL/kcRQV0EApIOr/9eBfKhYOVyQIcGGuotFGjF3xKLHMEz+b7g==",
+ "version": "0.378.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.378.0.tgz",
+ "integrity": "sha512-u6EPU8juLUk9ytRcyapkWI18epAv3RU+6+TC23ivjR0e+glWKBobFeSgRwOIJihzktILQuy6E0E80P2jVTDR5g==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
@@ -4657,13 +4715,13 @@
}
},
"node_modules/prisma": {
- "version": "5.13.0",
- "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz",
- "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==",
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.14.0.tgz",
+ "integrity": "sha512-gCNZco7y5XtjrnQYeDJTiVZmT/ncqCr5RY1/Cf8X2wgLRmyh9ayPAGBNziI4qEE4S6SxCH5omQLVo9lmURaJ/Q==",
"devOptional": true,
"hasInstallScript": true,
"dependencies": {
- "@prisma/engines": "5.13.0"
+ "@prisma/engines": "5.14.0"
},
"bin": {
"prisma": "build/index.js"
@@ -4676,6 +4734,7 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@@ -4764,7 +4823,36 @@
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true
+ },
+ "node_modules/react-leaflet": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
+ "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
+ "dependencies": {
+ "@react-leaflet/core": "^2.1.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/react-modal-sheet": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/react-modal-sheet/-/react-modal-sheet-3.1.0.tgz",
+ "integrity": "sha512-IjmVnNV4O1MXpYv6DFkmKmTQFYMklFig6uYLYz707uOBOO6RQMoG2dOPmQMIs41/psnp2NvHmQNA2pN1+UwgcA==",
+ "dependencies": {
+ "@react-aria/utils": "3.24.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "framer-motion": ">=8",
+ "react": ">=16"
+ }
},
"node_modules/react-remove-scroll": {
"version": "2.5.5",
@@ -4811,110 +4899,6 @@
}
}
},
- "node_modules/react-spring": {
- "version": "8.0.27",
- "resolved": "https://registry.npmjs.org/react-spring/-/react-spring-8.0.27.tgz",
- "integrity": "sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==",
- "dependencies": {
- "@babel/runtime": "^7.3.1",
- "prop-types": "^15.5.8"
- },
- "peerDependencies": {
- "react": ">= 16.8.0",
- "react-dom": ">= 16.8.0"
- }
- },
- "node_modules/react-spring-bottom-sheet": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/react-spring-bottom-sheet/-/react-spring-bottom-sheet-3.4.1.tgz",
- "integrity": "sha512-yDFqiPMm/fjefjnOe6Q9zxccbCl6HMUKsK5bWgfGHJIj4zmXVKio5d4icQvmOLuwpuCA2pwv4J6nGWS6fUZidQ==",
- "dependencies": {
- "@juggle/resize-observer": "^3.2.0",
- "@reach/portal": "^0.13.0",
- "@xstate/react": "^1.2.0",
- "body-scroll-lock": "^3.1.5",
- "focus-trap": "^6.2.2",
- "react-spring": "^8.0.27",
- "react-use-gesture": "^8.0.1",
- "xstate": "^4.15.1"
- },
- "peerDependencies": {
- "react": "^16.14.0 || 17 || 18"
- }
- },
- "node_modules/react-spring-bottom-sheet/node_modules/@reach/portal": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.13.2.tgz",
- "integrity": "sha512-g74BnCdtuTGthzzHn2cWW+bcyIYb0iIE/yRsm89i8oNzNgpopbkh9UY8TPbhNlys52h7U60s4kpRTmcq+JqsTA==",
- "dependencies": {
- "@reach/utils": "0.13.2",
- "tslib": "^2.1.0"
- },
- "peerDependencies": {
- "react": "^16.8.0 || 17.x",
- "react-dom": "^16.8.0 || 17.x"
- }
- },
- "node_modules/react-spring-bottom-sheet/node_modules/@reach/portal/node_modules/@reach/utils": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.13.2.tgz",
- "integrity": "sha512-3ir6cN60zvUrwjOJu7C6jec/samqAeyAB12ZADK+qjnmQPdzSYldrFWwDVV5H0WkhbYXR3uh+eImu13hCetNPQ==",
- "dependencies": {
- "@types/warning": "^3.0.0",
- "tslib": "^2.1.0",
- "warning": "^4.0.3"
- },
- "peerDependencies": {
- "react": "^16.8.0 || 17.x",
- "react-dom": "^16.8.0 || 17.x"
- }
- },
- "node_modules/react-spring-bottom-sheet/node_modules/@xstate/react": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/@xstate/react/-/react-1.6.3.tgz",
- "integrity": "sha512-NCUReRHPGvvCvj2yLZUTfR0qVp6+apc8G83oXSjN4rl89ZjyujiKrTff55bze/HrsvCsP/sUJASf2n0nzMF1KQ==",
- "dependencies": {
- "use-isomorphic-layout-effect": "^1.0.0",
- "use-subscription": "^1.3.0"
- },
- "peerDependencies": {
- "@xstate/fsm": "^1.0.0",
- "react": "^16.8.0 || ^17.0.0",
- "xstate": "^4.11.0"
- },
- "peerDependenciesMeta": {
- "@xstate/fsm": {
- "optional": true
- },
- "xstate": {
- "optional": true
- }
- }
- },
- "node_modules/react-spring-bottom-sheet/node_modules/react-dom": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
- "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "scheduler": "^0.20.2"
- },
- "peerDependencies": {
- "react": "17.0.2"
- }
- },
- "node_modules/react-spring-bottom-sheet/node_modules/scheduler": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
- "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- },
"node_modules/react-style-singleton": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
@@ -4937,15 +4921,6 @@
}
}
},
- "node_modules/react-use-gesture": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-8.0.1.tgz",
- "integrity": "sha512-CXzUNkulUdgouaAlvAsC5ZVo0fi9KGSBSk81WrE4kOIcJccpANe9zZkAYr5YZZhqpicIFxitsrGVS4wmoMun9A==",
- "deprecated": "This package is no longer maintained. Please use @use-gesture/react instead",
- "peerDependencies": {
- "react": ">= 16.8.0"
- }
- },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -5529,11 +5504,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/tabbable": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
- "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA=="
- },
"node_modules/tailwind-merge": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz",
@@ -5878,19 +5848,6 @@
}
}
},
- "node_modules/use-isomorphic-layout-effect": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
- "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
"node_modules/use-sidecar": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
@@ -5912,25 +5869,6 @@
}
}
},
- "node_modules/use-subscription": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.8.2.tgz",
- "integrity": "sha512-yC2ShScvQ1lAGRp5Y6pz1MqBIU81REfJ/sQIe16BCgKK9mRlZCnU90uY0alKsN6e/Next0vXTsvH3HbAfdH68w==",
- "dependencies": {
- "use-sync-external-store": "^1.2.2"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/use-subscription/node_modules/use-sync-external-store": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
- "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
@@ -5950,14 +5888,6 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"devOptional": true
},
- "node_modules/warning": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
- "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
- "dependencies": {
- "loose-envify": "^1.0.0"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6153,15 +6083,6 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
- "node_modules/xstate": {
- "version": "4.38.3",
- "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.38.3.tgz",
- "integrity": "sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/xstate"
- }
- },
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
diff --git a/package.json b/package.json
index df00cd7..eb2e43e 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,8 @@
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
},
"dependencies": {
- "@hookform/resolvers": "^3.3.4",
- "@prisma/client": "^5.13.0",
+ "@hookform/resolvers": "^3.4.2",
+ "@prisma/client": "^5.14.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-select": "^2.0.0",
@@ -22,29 +22,31 @@
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"framer-motion": "^11.2.4",
- "lucide-react": "^0.376.0",
+ "lucide-react": "^0.378.0",
"next": "14.2.3",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
- "react-spring-bottom-sheet": "^3.4.1",
+ "react-leaflet": "^4.2.1",
+ "react-modal-sheet": "^3.1.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8",
"zustand": "^4.5.2"
},
"devDependencies": {
- "@types/node": "^20.12.11",
+ "@types/leaflet": "^1.9.12",
+ "@types/node": "^20.12.12",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8.57.0",
"eslint-config-next": "14.2.3",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
- "eslint-plugin-tailwindcss": "^3.15.1",
+ "eslint-plugin-tailwindcss": "^3.15.2",
"postcss": "^8",
- "prisma": "^5.13.0",
+ "prisma": "^5.14.0",
"tailwindcss": "^3.4.3",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index deb5d71..4d02667 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -112,7 +112,8 @@ model Step {
pictureHint String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- coordinates String
+ latitude Float
+ longitude Float
address String
city String
postalCode String
diff --git a/prisma/seed.ts b/prisma/seed.ts
index a0e92ea..61c7279 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -1,4 +1,12 @@
-import { Journey, Event, PrismaClient, Role, User, UserEvent, Comment } from "@prisma/client";
+import {
+ Journey,
+ Event,
+ PrismaClient,
+ Role,
+ User,
+ UserEvent,
+ Comment,
+} from "@prisma/client";
const prisma = new PrismaClient();
@@ -269,9 +277,9 @@ async function main() {
physicalDifficulty: 1,
lastCompletion: new Date("2021-01-01"),
mobilityImpaired: "Partially accessible",
- PartiallySighted: "Partially accessible",
- PartiallyDeaf: "Partially accessible",
- CognitivelyImpaired: "Partially accessible",
+ partiallySighted: "Partially accessible",
+ partiallyDeaf: "Partially accessible",
+ cognitivelyImpaired: "Partially accessible",
steps: {
create: [
{
@@ -406,9 +414,9 @@ async function main() {
physicalDifficulty: 1,
lastCompletion: new Date("2021-02-01"),
mobilityImpaired: "Partially accessible",
- PartiallySighted: "Partially accessible",
- PartiallyDeaf: "Partially accessible",
- CognitivelyImpaired: "Partially accessible",
+ partiallySighted: "Partially accessible",
+ partiallyDeaf: "Partially accessible",
+ cognitivelyImpaired: "Partially accessible",
steps: {
create: [
{
@@ -458,9 +466,9 @@ async function main() {
physicalDifficulty: 2,
lastCompletion: new Date("2021-03-01"),
mobilityImpaired: "Partially accessible",
- PartiallySighted: "Partially accessible",
- PartiallyDeaf: "Partially accessible",
- CognitivelyImpaired: "Partially accessible",
+ partiallySighted: "Partially accessible",
+ partiallyDeaf: "Partially accessible",
+ cognitivelyImpaired: "Partially accessible",
steps: {
create: [
{
@@ -592,9 +600,9 @@ async function main() {
physicalDifficulty: 2,
lastCompletion: new Date("2021-04-01"),
mobilityImpaired: "Partially accessible",
- PartiallySighted: "Partially accessible",
- PartiallyDeaf: "Partially accessible",
- CognitivelyImpaired: "Partially accessible",
+ partiallySighted: "Partially accessible",
+ partiallyDeaf: "Partially accessible",
+ cognitivelyImpaired: "Partially accessible",
steps: {
create: [
{
@@ -672,9 +680,9 @@ async function main() {
physicalDifficulty: 2,
lastCompletion: new Date("2021-05-01"),
mobilityImpaired: "Partially accessible",
- PartiallySighted: "Partially accessible",
- PartiallyDeaf: "Partially accessible",
- CognitivelyImpaired: "Partially accessible",
+ partiallySighted: "Partially accessible",
+ partiallyDeaf: "Partially accessible",
+ cognitivelyImpaired: "Partially accessible",
steps: {
create: [
{
diff --git a/public/img/pin.png b/public/img/pin.png
new file mode 100644
index 0000000000000000000000000000000000000000..7582c544ed420e2e7dd96b0d85484fc8e017ce76
GIT binary patch
literal 394
zcmV;50d@X~P)Px$L`g(JRA@u(SwRlMFbwlv2D}V-8T<^0v`JNAQ|%_&D$)y=rm^FsOUi`7*EBeO
zB!RsZ^ibdi;3^V%0=NUPUupFaI0JY{UcZfG9J1jI-bi>XQ^Ay^R}X;~?QPpY!Xg>X
zIFdjmh!jl8C>8i!3sNv8qm^1@Yr@61%OPS)x%hTDBnfnUP*Q;+cv-|sCS?j`3MqM0
z*klUL!BECZd59@R>s3GjUi#m4Chi4a6S*kmpeW!u%-5KW1oc9p5U`gBuVKC>VBjew
z6as9^z-9)8iP|i-%}HP@0)MH%@I`DlkUi#l-$c?!O-Z5FqO5PL5MLpOuH053zCsR7
z0kQM&iV_$gSXQU!(#H0ZSihNI4P#
o28*;eS^)_ZW0VA#XhrOO121GZF`?~WiU0rr07*qoM6N<$f>e>5TmS$7
literal 0
HcmV?d00001
diff --git a/src/components/form/Register.tsx b/src/components/form/Register.tsx
index f9f6f66..65a396e 100644
--- a/src/components/form/Register.tsx
+++ b/src/components/form/Register.tsx
@@ -22,7 +22,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
-import { cn } from "@/lib/utils";
+import { cn } from "@/lib/tailwindUtils";
type RegisterForm = z.infer;
diff --git a/src/components/form/journey/BottomSheetModal.tsx b/src/components/form/journey/BottomSheetModal.tsx
new file mode 100644
index 0000000..a63bfd2
--- /dev/null
+++ b/src/components/form/journey/BottomSheetModal.tsx
@@ -0,0 +1,201 @@
+"use client";
+import { useJourneyFormStore } from "@/store/journeyFormStore";
+import { Button } from "@/components/ui/button";
+import { addStepSchema } from "@/validators/stepFormSchema";
+import { useForm, SubmitHandler } from "react-hook-form";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { z } from "zod";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Sheet } from "react-modal-sheet";
+import "leaflet/dist/leaflet.css";
+import { useEffect, useState } from "react";
+import LeafletMap from "@/components/map/LeafletMap";
+
+type AddStepFormValues = z.infer;
+
+const BottomSheetModal = () => {
+ const {
+ bottomSheetVisible,
+ hideBottomSheet,
+ addStep,
+ editedStep,
+ editStep,
+ setEditedStep,
+ } = useJourneyFormStore();
+ const [dragDisabled, setDragDisabled] = useState(false);
+
+ useEffect(() => {
+ console.log(editedStep, "edited step");
+ if (editedStep) {
+ form.setValue("puzzle", editedStep.puzzle);
+ form.setValue("answer", editedStep.answer);
+ form.setValue("hint", editedStep.hint);
+ form.setValue(
+ "coordinates",
+ `${editedStep.coordinates.latitude};${editedStep.coordinates.longitude}`
+ );
+ }
+ }, [editedStep]);
+
+ const form = useForm({
+ resolver: zodResolver(addStepSchema),
+ defaultValues: {
+ puzzle: "",
+ answer: "",
+ hint: "",
+ coordinates: "",
+ },
+ });
+
+ const processForm: SubmitHandler = (data) => {
+ const coordinates = data.coordinates.split(";");
+ const latitude = parseFloat(coordinates[0]);
+ const longitude = parseFloat(coordinates[1]);
+
+ if (editedStep) {
+ editStep(
+ {
+ hint: data.hint,
+ puzzle: data.puzzle,
+ answer: data.answer,
+ coordinates: {
+ latitude,
+ longitude,
+ },
+ },
+ editedStep.index
+ );
+ } else {
+ addStep({
+ answer: data.answer,
+ coordinates: {
+ latitude,
+ longitude,
+ },
+ hint: data.hint,
+ puzzle: data.puzzle,
+ });
+ }
+
+ hideBottomSheet();
+ onDismiss();
+ };
+
+ const onDismiss = () => {
+ hideBottomSheet();
+ setEditedStep(null);
+ form.reset();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default BottomSheetModal;
diff --git a/src/components/form/journey/JourneyForm.tsx b/src/components/form/journey/JourneyForm.tsx
index e2dda91..8d0a493 100644
--- a/src/components/form/journey/JourneyForm.tsx
+++ b/src/components/form/journey/JourneyForm.tsx
@@ -42,11 +42,20 @@ const steps = [
const JourneyForm = () => {
const { isVisible, hideModal } = useJourneyFormStore();
const [formStatus, setFormStatus] = useState<"idle" | "errored">("idle");
- const [currentStep, setCurrentStep] = useState(0);
- const [dir, setDir] = useState<"ltr" | "rtl">("rtl");
+ const [currentStep, setCurrentStep] = useState(2);
+ const [dir, setDir] = useState<"ltr" | "rtl">("ltr");
const form = useForm({
resolver: zodResolver(journeyFormSchema),
+ defaultValues: {
+ title: "",
+ description: "",
+ mobilityImpaired: "undefined",
+ partiallySighted: "undefined",
+ partiallyDeaf: "undefined",
+ cognitivelyImpaired: "undefined",
+ treasure: "",
+ },
});
const processForm: SubmitHandler = async (data) => {
@@ -56,6 +65,7 @@ const JourneyForm = () => {
const next = async () => {
const fields = steps[currentStep].fields;
const output = await form.trigger(fields as FieldName[]);
+ // console.log(output, "output");
if (!output) return;
if (currentStep < steps.length) {
if (currentStep === steps.length - 1) {
diff --git a/src/components/form/journey/steps/ThirdStep.tsx b/src/components/form/journey/steps/ThirdStep.tsx
index 5d698c3..b131972 100644
--- a/src/components/form/journey/steps/ThirdStep.tsx
+++ b/src/components/form/journey/steps/ThirdStep.tsx
@@ -1,7 +1,18 @@
-import React from "react";
+"use client";
import { UseFormReturn } from "react-hook-form";
import { JourneyFormValues } from "../JourneyForm";
import { Button } from "@/components/ui/button";
+import { useJourneyFormStore } from "@/store/journeyFormStore";
+import { Plus, Edit, Trash } from "lucide-react";
+import BottomSheetModal from "../BottomSheetModal";
+import {
+ FormControl,
+ FormField,
+ FormItem,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { useEffect } from "react";
type ThirdStepProps = {
prev: () => void;
@@ -10,9 +21,49 @@ type ThirdStepProps = {
};
const ThirdStep = ({ form, next, prev }: ThirdStepProps) => {
+ const { steps, showBottomSheet, removeStep, setEditedStep } =
+ useJourneyFormStore();
+
+ useEffect(() => {
+ form.setValue("steps", JSON.stringify(steps));
+ }, [steps]);
+
return (
Quelles sont les étapes ?
+ {steps.map((step, index) => (
+
+
{step.puzzle}
+
{
+ setEditedStep({ ...step, index });
+ showBottomSheet();
+ }}
+ />
+ removeStep(index)} />
+
+ ))}
+
+
(
+
+
+ <>
+
+ >
+
+
+
+ )}
+ />
+
+
@@ -24,6 +75,7 @@ const ThirdStep = ({ form, next, prev }: ThirdStepProps) => {
Continuer
+
);
};
diff --git a/src/components/map/LeafletMap.tsx b/src/components/map/LeafletMap.tsx
new file mode 100644
index 0000000..65f806f
--- /dev/null
+++ b/src/components/map/LeafletMap.tsx
@@ -0,0 +1,5 @@
+import dynamic from "next/dynamic"; // Composant LeafletMap chargé dynamiquement
+
+const LeafletMap = dynamic(() => import("./Map"), { ssr: false });
+
+export default LeafletMap;
diff --git a/src/components/map/LocationMarker.tsx b/src/components/map/LocationMarker.tsx
new file mode 100644
index 0000000..3bde87b
--- /dev/null
+++ b/src/components/map/LocationMarker.tsx
@@ -0,0 +1,53 @@
+import { addStepSchema } from "@/validators/stepFormSchema";
+import L, { LatLng } from "leaflet";
+import { useState } from "react";
+import { UseFormReturn } from "react-hook-form";
+import { useMapEvents, Marker } from "react-leaflet";
+import { z } from "zod";
+
+type AddStepFormValues = z.infer;
+
+type LocationMarkerProps = {
+ setDragDisabled: (value: boolean) => void;
+ form: UseFormReturn;
+ defaultCoordinates: {
+ latitude: number;
+ longitude: number;
+ } | null;
+};
+
+const LocationMarker = ({
+ setDragDisabled,
+ form,
+ defaultCoordinates,
+}: LocationMarkerProps) => {
+ const defaultPosition = defaultCoordinates
+ ? new L.LatLng(defaultCoordinates.latitude, defaultCoordinates.longitude)
+ : null;
+ const [position, setPosition] = useState(defaultPosition);
+ useMapEvents({
+ click(e) {
+ setPosition(e.latlng);
+ form.setValue("coordinates", `${e.latlng.lat};${e.latlng.lng}`);
+ form.trigger("coordinates");
+ },
+ dragstart() {
+ setDragDisabled(true);
+ },
+ dragend() {
+ setDragDisabled(false);
+ },
+ });
+
+ const myIcon = L.icon({
+ iconUrl: "/img/pin.png",
+ iconSize: [30, 30],
+ iconAnchor: [15, 30],
+ });
+
+ return position === null ? null : (
+
+ );
+};
+
+export default LocationMarker;
diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx
new file mode 100644
index 0000000..1dfcfe6
--- /dev/null
+++ b/src/components/map/Map.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import { useJourneyFormStore } from "@/store/journeyFormStore";
+import React from "react";
+import { UseFormReturn } from "react-hook-form";
+import { MapContainer, ZoomControl, TileLayer } from "react-leaflet";
+import LocationMarker from "./LocationMarker";
+import { addStepSchema } from "@/validators/stepFormSchema";
+import { z } from "zod";
+
+type AddStepFormValues = z.infer;
+
+type MapProps = {
+ setDragDisabled: (value: boolean) => void;
+ form: UseFormReturn;
+};
+
+const Map = ({ setDragDisabled, form }: MapProps) => {
+ const { editedStep } = useJourneyFormStore();
+ return (
+
+
+
+
+
+ );
+};
+
+export default Map;
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index b9cd8f9..0c50212 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils";
+import { cn } from "@/lib/tailwindUtils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx
index 2f02434..bab7515 100644
--- a/src/components/ui/calendar.tsx
+++ b/src/components/ui/calendar.tsx
@@ -1,13 +1,13 @@
-"use client"
+"use client";
-import * as React from "react"
-import { ChevronLeft, ChevronRight } from "lucide-react"
-import { DayPicker } from "react-day-picker"
+import * as React from "react";
+import { ChevronLeft, ChevronRight } from "lucide-react";
+import { DayPicker } from "react-day-picker";
-import { cn } from "@/lib/utils"
-import { buttonVariants } from "@/components/ui/button"
+import { cn } from "@/lib/tailwindUtils";
+import { buttonVariants } from "@/components/ui/button";
-export type CalendarProps = React.ComponentProps
+export type CalendarProps = React.ComponentProps;
function Calendar({
className,
@@ -59,8 +59,8 @@ function Calendar({
}}
{...props}
/>
- )
+ );
}
-Calendar.displayName = "Calendar"
+Calendar.displayName = "Calendar";
-export { Calendar }
+export { Calendar };
diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx
index 4603f8b..4501fd5 100644
--- a/src/components/ui/form.tsx
+++ b/src/components/ui/form.tsx
@@ -1,6 +1,6 @@
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
-import { Slot } from "@radix-ui/react-slot"
+import * as React from "react";
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { Slot } from "@radix-ui/react-slot";
import {
Controller,
ControllerProps,
@@ -8,23 +8,23 @@ import {
FieldValues,
FormProvider,
useFormContext,
-} from "react-hook-form"
+} from "react-hook-form";
-import { cn } from "@/lib/utils"
-import { Label } from "@/components/ui/label"
+import { cn } from "@/lib/tailwindUtils";
+import { Label } from "@/components/ui/label";
-const Form = FormProvider
+const Form = FormProvider;
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath = FieldPath
> = {
- name: TName
-}
+ name: TName;
+};
const FormFieldContext = React.createContext(
{} as FormFieldContextValue
-)
+);
const FormField = <
TFieldValues extends FieldValues = FieldValues,
@@ -36,21 +36,21 @@ const FormField = <
- )
-}
+ );
+};
const useFormField = () => {
- const fieldContext = React.useContext(FormFieldContext)
- const itemContext = React.useContext(FormItemContext)
- const { getFieldState, formState } = useFormContext()
+ const fieldContext = React.useContext(FormFieldContext);
+ const itemContext = React.useContext(FormItemContext);
+ const { getFieldState, formState } = useFormContext();
- const fieldState = getFieldState(fieldContext.name, formState)
+ const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
- throw new Error("useFormField should be used within ")
+ throw new Error("useFormField should be used within ");
}
- const { id } = itemContext
+ const { id } = itemContext;
return {
id,
@@ -59,36 +59,36 @@ const useFormField = () => {
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
- }
-}
+ };
+};
type FormItemContextValue = {
- id: string
-}
+ id: string;
+};
const FormItemContext = React.createContext(
{} as FormItemContextValue
-)
+);
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => {
- const id = React.useId()
+ const id = React.useId();
return (
- )
-})
-FormItem.displayName = "FormItem"
+ );
+});
+FormItem.displayName = "FormItem";
const FormLabel = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => {
- const { error, formItemId } = useFormField()
+ const { error, formItemId } = useFormField();
return (
- )
-})
-FormLabel.displayName = "FormLabel"
+ );
+});
+FormLabel.displayName = "FormLabel";
const FormControl = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ ...props }, ref) => {
- const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
+ const { error, formItemId, formDescriptionId, formMessageId } =
+ useFormField();
return (
- )
-})
-FormControl.displayName = "FormControl"
+ );
+});
+FormControl.displayName = "FormControl";
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, ...props }, ref) => {
- const { formDescriptionId } = useFormField()
+ const { formDescriptionId } = useFormField();
return (
- )
-})
-FormDescription.displayName = "FormDescription"
+ );
+});
+FormDescription.displayName = "FormDescription";
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes
>(({ className, children, ...props }, ref) => {
- const { error, formMessageId } = useFormField()
- const body = error ? String(error?.message) : children
+ const { error, formMessageId } = useFormField();
+ const body = error ? String(error?.message) : children;
if (!body) {
- return null
+ return null;
}
return (
@@ -160,9 +161,9 @@ const FormMessage = React.forwardRef<
>
{body}
- )
-})
-FormMessage.displayName = "FormMessage"
+ );
+});
+FormMessage.displayName = "FormMessage";
export {
useFormField,
@@ -173,4 +174,4 @@ export {
FormDescription,
FormMessage,
FormField,
-}
+};
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
index 677d05f..aec2612 100644
--- a/src/components/ui/input.tsx
+++ b/src/components/ui/input.tsx
@@ -1,6 +1,6 @@
-import * as React from "react"
+import * as React from "react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/tailwindUtils";
export interface InputProps
extends React.InputHTMLAttributes {}
@@ -17,9 +17,9 @@ const Input = React.forwardRef(
ref={ref}
{...props}
/>
- )
+ );
}
-)
-Input.displayName = "Input"
+);
+Input.displayName = "Input";
-export { Input }
+export { Input };
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
index 5341821..7a5d530 100644
--- a/src/components/ui/label.tsx
+++ b/src/components/ui/label.tsx
@@ -1,14 +1,14 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/tailwindUtils";
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
-)
+);
const Label = React.forwardRef<
React.ElementRef,
@@ -20,7 +20,7 @@ const Label = React.forwardRef<
className={cn(labelVariants(), className)}
{...props}
/>
-))
-Label.displayName = LabelPrimitive.Root.displayName
+));
+Label.displayName = LabelPrimitive.Root.displayName;
-export { Label }
+export { Label };
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
index a0ec48b..f8fe796 100644
--- a/src/components/ui/popover.tsx
+++ b/src/components/ui/popover.tsx
@@ -1,13 +1,13 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as PopoverPrimitive from "@radix-ui/react-popover"
+import * as React from "react";
+import * as PopoverPrimitive from "@radix-ui/react-popover";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/tailwindUtils";
-const Popover = PopoverPrimitive.Root
+const Popover = PopoverPrimitive.Root;
-const PopoverTrigger = PopoverPrimitive.Trigger
+const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef,
@@ -25,7 +25,7 @@ const PopoverContent = React.forwardRef<
{...props}
/>
-))
-PopoverContent.displayName = PopoverPrimitive.Content.displayName
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
-export { Popover, PopoverTrigger, PopoverContent }
+export { Popover, PopoverTrigger, PopoverContent };
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
index cbe5a36..6ffe7fb 100644
--- a/src/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -1,16 +1,16 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as SelectPrimitive from "@radix-ui/react-select"
-import { Check, ChevronDown, ChevronUp } from "lucide-react"
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/tailwindUtils";
-const Select = SelectPrimitive.Root
+const Select = SelectPrimitive.Root;
-const SelectGroup = SelectPrimitive.Group
+const SelectGroup = SelectPrimitive.Group;
-const SelectValue = SelectPrimitive.Value
+const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<
React.ElementRef,
@@ -29,8 +29,8 @@ const SelectTrigger = React.forwardRef<
-))
-SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<
React.ElementRef,
@@ -46,8 +46,8 @@ const SelectScrollUpButton = React.forwardRef<
>
-))
-SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<
React.ElementRef,
@@ -63,9 +63,9 @@ const SelectScrollDownButton = React.forwardRef<
>
-))
+));
SelectScrollDownButton.displayName =
- SelectPrimitive.ScrollDownButton.displayName
+ SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef,
@@ -96,8 +96,8 @@ const SelectContent = React.forwardRef<
-))
-SelectContent.displayName = SelectPrimitive.Content.displayName
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef,
@@ -108,8 +108,8 @@ const SelectLabel = React.forwardRef<
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
-))
-SelectLabel.displayName = SelectPrimitive.Label.displayName
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef,
@@ -131,8 +131,8 @@ const SelectItem = React.forwardRef<
{children}
-))
-SelectItem.displayName = SelectPrimitive.Item.displayName
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef,
@@ -143,8 +143,8 @@ const SelectSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
-))
-SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
@@ -157,4 +157,4 @@ export {
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
-}
+};
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
index 9f9a6dc..c9ba003 100644
--- a/src/components/ui/textarea.tsx
+++ b/src/components/ui/textarea.tsx
@@ -1,6 +1,6 @@
-import * as React from "react"
+import * as React from "react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/tailwindUtils";
export interface TextareaProps
extends React.TextareaHTMLAttributes {}
@@ -16,9 +16,9 @@ const Textarea = React.forwardRef(
ref={ref}
{...props}
/>
- )
+ );
}
-)
-Textarea.displayName = "Textarea"
+);
+Textarea.displayName = "Textarea";
-export { Textarea }
+export { Textarea };
diff --git a/src/lib/utils.ts b/src/lib/tailwindUtils.ts
similarity index 100%
rename from src/lib/utils.ts
rename to src/lib/tailwindUtils.ts
diff --git a/src/store/journeyFormStore.ts b/src/store/journeyFormStore.ts
index d42d020..abd915e 100644
--- a/src/store/journeyFormStore.ts
+++ b/src/store/journeyFormStore.ts
@@ -1,9 +1,32 @@
import { create } from "zustand";
+interface AddStepFormValues {
+ puzzle: string;
+ answer: string;
+ hint: string;
+ coordinates: {
+ latitude: number;
+ longitude: number;
+ };
+}
+
+interface EditedStep extends AddStepFormValues {
+ index: number;
+}
+
interface JourneyFormStore {
isVisible: boolean;
showModal: () => void;
hideModal: () => void;
+ bottomSheetVisible: boolean;
+ showBottomSheet: () => void;
+ hideBottomSheet: () => void;
+ steps: AddStepFormValues[];
+ addStep: (step: AddStepFormValues) => void;
+ removeStep: (index: number) => void;
+ editStep: (step: AddStepFormValues, index: number) => void;
+ editedStep: EditedStep | null;
+ setEditedStep: (step: EditedStep | null) => void;
}
export const useJourneyFormStore = create((set) => ({
@@ -18,4 +41,31 @@ export const useJourneyFormStore = create((set) => ({
body?.classList.remove("overflow-hidden");
set({ isVisible: false });
},
+ bottomSheetVisible: false,
+ showBottomSheet: () => {
+ set({ bottomSheetVisible: true });
+ },
+ hideBottomSheet: () => {
+ set({ bottomSheetVisible: false });
+ },
+ steps: [],
+ addStep: (step) => {
+ set((state) => ({ steps: [...state.steps, step] }));
+ },
+ removeStep: (index) => {
+ set((state) => ({
+ steps: state.steps.filter((_, loopIndex) => loopIndex !== index),
+ }));
+ },
+ editStep: (step, index) => {
+ set((state) => ({
+ steps: state.steps.map((currentStep, loopIndex) =>
+ loopIndex === index ? step : currentStep
+ ),
+ }));
+ },
+ editedStep: null,
+ setEditedStep: (step) => {
+ set({ editedStep: step });
+ },
}));
diff --git a/src/validators/journeyFormSchema.ts b/src/validators/journeyFormSchema.ts
index 2ced4ca..8a713bf 100644
--- a/src/validators/journeyFormSchema.ts
+++ b/src/validators/journeyFormSchema.ts
@@ -1,19 +1,28 @@
import { z } from "zod";
+import { addStepArraySchema, addStepAPISchema } from "./stepFormSchema";
+
+type addStepAPISchemaType = z.infer;
export const firstStepSchema = z.object({
- title: z.string({ required_error: "Ce champ est requis" }),
- description: z.string({ required_error: "Ce champ est requis" }),
+ title: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ description: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
// image: z.string(),
});
export const secondStepSchema = z.object({
- requirement: z.string({ required_error: "Ce champ est requis" }),
- physicalDifficulty: z.enum(["easy", "medium", "hard"], {
- required_error: "Vous devez sélectionner une option",
- }),
- cluesDifficulty: z.enum(["easy", "medium", "hard"], {
- required_error: "Vous devez sélectionner une option",
+ requirement: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
}),
+ // physicalDifficulty: z.enum(["easy", "medium", "hard"], {
+ // required_error: "Vous devez sélectionner une option",
+ // }),
+ // cluesDifficulty: z.enum(["easy", "medium", "hard"], {
+ // required_error: "Vous devez sélectionner une option",
+ // }),
mobilityImpaired: z.enum(
["undefined", "unaccessible", "partiallyAccessible", "accessible"],
{
@@ -41,11 +50,28 @@ export const secondStepSchema = z.object({
});
export const thirdStepSchema = z.object({
- // to do: add journey steps schema
+ steps: z.string().refine((value) => {
+ const parsedSteps = addStepArraySchema.parse(value);
+ const formatedSteps: addStepAPISchemaType = parsedSteps.map((step) => {
+ return {
+ answer: step.answer,
+ hint: step.hint,
+ puzzle: step.puzzle,
+ latitude: step.coordinates.latitude,
+ longitude: step.coordinates.longitude,
+ };
+ });
+
+ console.log(formatedSteps, "formattedSteps");
+
+ return formatedSteps.length > 0;
+ }),
});
export const forthStepSchema = z.object({
- treasure: z.string(),
+ treasure: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
});
export const journeyFormSchema = z.object({
diff --git a/src/validators/stepFormSchema.ts b/src/validators/stepFormSchema.ts
new file mode 100644
index 0000000..90af453
--- /dev/null
+++ b/src/validators/stepFormSchema.ts
@@ -0,0 +1,90 @@
+import { z } from "zod";
+
+export const addStepSchema = z.object({
+ puzzle: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ answer: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ hint: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ // picturePuzzle: z.string({ required_error: "Ce champ est requis" }).optional(),
+ // pictureHint: z.string({ required_error: "Ce champ est requis" }).optional(),
+ coordinates: z
+ .string({ required_error: "Ce champ est requis" })
+ .min(1, { message: "Ce champ est requis" }),
+});
+
+export const addStepArraySchema = z
+ .object({
+ puzzle: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ answer: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ hint: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ // picturePuzzle: z.string({ required_error: "Ce champ est requis" }).optional(),
+ // pictureHint: z.string({ required_error: "Ce champ est requis" }).optional(),
+ coordinates: z.object({
+ latitude: z.number({ required_error: "Ce champ est requis" }),
+ longitude: z.number({ required_error: "Ce champ est requis" }),
+ }),
+ })
+ .array();
+
+export const addStepAPISchema = z
+ .object({
+ puzzle: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ answer: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ hint: z.string({ required_error: "Ce champ est requis" }).min(1, {
+ message: "Ce champ est requis",
+ }),
+ // picturePuzzle: z.string({ required_error: "Ce champ est requis" }).optional(),
+ // pictureHint: z.string({ required_error: "Ce champ est requis" }).optional(),
+ latitude: z
+ .number({ required_error: "Ce champ est requis" })
+ .min(-90, { message: "La latitude doit être entre -90 et 90" })
+ .max(90, { message: "La latitude doit être entre -90 et 90" }),
+ longitude: z
+ .number({ required_error: "Ce champ est requis" })
+ .min(-180, { message: "La longitude doit être entre -180 et 180" })
+ .max(180, { message: "La longitude doit être entre -180 et 180" }),
+ })
+ .array();
+
+// const coordinates = data.coordinates.split(";");
+
+// // check if we have exactly two parts
+// if (coordinates.length !== 2) {
+// return false;
+// }
+
+// // parse float
+// const latitude = parseFloat(coordinates[0]);
+// const longitude = parseFloat(coordinates[1]);
+
+// // check if both are numbers
+// if (isNaN(latitude) || isNaN(longitude)) {
+// return false;
+// }
+
+// // check latitude range
+// if (latitude < -90 || latitude > 90) {
+// return false;
+// }
+
+// // check longitude range
+// if (longitude < -180 || longitude > 180) {
+// return false;
+// }
+
+// return true;
From 97ddb4e5eda453f1c9817ed005a5c0141a3b7f21 Mon Sep 17 00:00:00 2001
From: alexandre-kakal-akarah
Date: Wed, 22 May 2024 14:27:21 +0200
Subject: [PATCH 3/4] WIP: test push build github action
---
.github/workflows/push-build.yml | 24 ++++++++++++++++++++++++
src/app/layout.tsx | 1 +
2 files changed, 25 insertions(+)
create mode 100644 .github/workflows/push-build.yml
diff --git a/.github/workflows/push-build.yml b/.github/workflows/push-build.yml
new file mode 100644
index 0000000..53f98cb
--- /dev/null
+++ b/.github/workflows/push-build.yml
@@ -0,0 +1,24 @@
+name: Check build when pushing
+run-name: ${{ github.actor }} is pushing
+on: [push]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ - name: Install dependencies
+ run: npm install
+ - name: Build
+ run: npm run build
+ - name: Notify Discord on failure
+ if: failure()
+ uses: containrrr/shoutrrr-action@v1
+ with:
+ url: ${{ secrets.NOTIFICATION_URL }}
+ title: "Build failed for ${{ github.actor }}"
+ message: "Build failed. Commit message: ${{ github.event.head_commit.message }}"
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 0784457..da8b22c 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -15,6 +15,7 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
+ throw new Error("test");
return (
{children}
From f44612ab5ec109236117e5dc4184465872a03e89 Mon Sep 17 00:00:00 2001
From: alexandre-kakal-akarah
Date: Wed, 22 May 2024 15:23:30 +0200
Subject: [PATCH 4/4] feat: journey form
---
.github/workflows/push-build.yml | 5 ++++-
src/app/layout.tsx | 1 -
src/components/form/journey/JourneyForm.tsx | 4 +++-
.../form/journey/steps/ThirdStep.tsx | 12 ++++------
src/validators/journeyFormSchema.ts | 22 +++----------------
5 files changed, 14 insertions(+), 30 deletions(-)
diff --git a/.github/workflows/push-build.yml b/.github/workflows/push-build.yml
index 53f98cb..7ba773a 100644
--- a/.github/workflows/push-build.yml
+++ b/.github/workflows/push-build.yml
@@ -21,4 +21,7 @@ jobs:
with:
url: ${{ secrets.NOTIFICATION_URL }}
title: "Build failed for ${{ github.actor }}"
- message: "Build failed. Commit message: ${{ github.event.head_commit.message }}"
+ message: |
+ Build failed
+ Commit message: ${{ github.event.head_commit.message }}
+ Commit SHA: ${{ github.sha }}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index da8b22c..0784457 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -15,7 +15,6 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
- throw new Error("test");
return (
{children}
diff --git a/src/components/form/journey/JourneyForm.tsx b/src/components/form/journey/JourneyForm.tsx
index 8d0a493..499c8c4 100644
--- a/src/components/form/journey/JourneyForm.tsx
+++ b/src/components/form/journey/JourneyForm.tsx
@@ -50,10 +50,12 @@ const JourneyForm = () => {
defaultValues: {
title: "",
description: "",
+ requirement: "",
mobilityImpaired: "undefined",
partiallySighted: "undefined",
partiallyDeaf: "undefined",
cognitivelyImpaired: "undefined",
+ steps: "",
treasure: "",
},
});
@@ -65,7 +67,7 @@ const JourneyForm = () => {
const next = async () => {
const fields = steps[currentStep].fields;
const output = await form.trigger(fields as FieldName[]);
- // console.log(output, "output");
+ console.log(output, "output", fields, "fields");
if (!output) return;
if (currentStep < steps.length) {
if (currentStep === steps.length - 1) {
diff --git a/src/components/form/journey/steps/ThirdStep.tsx b/src/components/form/journey/steps/ThirdStep.tsx
index b131972..40e8e60 100644
--- a/src/components/form/journey/steps/ThirdStep.tsx
+++ b/src/components/form/journey/steps/ThirdStep.tsx
@@ -11,7 +11,6 @@ import {
FormItem,
FormMessage,
} from "@/components/ui/form";
-import { Input } from "@/components/ui/input";
import { useEffect } from "react";
type ThirdStepProps = {
@@ -25,7 +24,8 @@ const ThirdStep = ({ form, next, prev }: ThirdStepProps) => {
useJourneyFormStore();
useEffect(() => {
- form.setValue("steps", JSON.stringify(steps));
+ form.setValue("steps", steps.length ? JSON.stringify(steps) : "");
+ form.trigger("steps");
}, [steps]);
return (
@@ -49,12 +49,8 @@ const ThirdStep = ({ form, next, prev }: ThirdStepProps) => {
control={form.control}
name="steps"
render={({ field }) => (
-
-
- <>
-
- >
-
+
+
)}
diff --git a/src/validators/journeyFormSchema.ts b/src/validators/journeyFormSchema.ts
index 8a713bf..28dcde2 100644
--- a/src/validators/journeyFormSchema.ts
+++ b/src/validators/journeyFormSchema.ts
@@ -1,7 +1,4 @@
import { z } from "zod";
-import { addStepArraySchema, addStepAPISchema } from "./stepFormSchema";
-
-type addStepAPISchemaType = z.infer;
export const firstStepSchema = z.object({
title: z.string({ required_error: "Ce champ est requis" }).min(1, {
@@ -50,22 +47,9 @@ export const secondStepSchema = z.object({
});
export const thirdStepSchema = z.object({
- steps: z.string().refine((value) => {
- const parsedSteps = addStepArraySchema.parse(value);
- const formatedSteps: addStepAPISchemaType = parsedSteps.map((step) => {
- return {
- answer: step.answer,
- hint: step.hint,
- puzzle: step.puzzle,
- latitude: step.coordinates.latitude,
- longitude: step.coordinates.longitude,
- };
- });
-
- console.log(formatedSteps, "formattedSteps");
-
- return formatedSteps.length > 0;
- }),
+ steps: z
+ .string()
+ .min(1, { message: "Vous devez ajouter au moins une étape" }),
});
export const forthStepSchema = z.object({