From 189086bbf2b0596f1fa96cc9077e36555b869df9 Mon Sep 17 00:00:00 2001 From: "CORP\\dimitar.kirilovyank" Date: Mon, 11 Nov 2024 13:23:30 +0100 Subject: [PATCH] Session 8 - Dimitar --- frontend/package-lock.json | 186 ++++++++++++++++++++++++- frontend/package.json | 2 + frontend/src/App.js | 2 + frontend/src/App.tsx | 35 ++--- frontend/src/components/Position.tsx | 157 +++++++++++++++++++++ frontend/src/components/Positions.tsx | 5 +- frontend/src/index.tsx | 4 +- frontend/src/react-beautiful-dnd.d.ts | 2 + prompts/prompts-dky.md | 191 ++++++++++++++++++++++++++ 9 files changed, 554 insertions(+), 30 deletions(-) create mode 100644 frontend/src/components/Position.tsx create mode 100644 frontend/src/react-beautiful-dnd.d.ts create mode 100644 prompts/prompts-dky.md diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 75428c1..b938039 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@hello-pangea/dnd": "github:hello-pangea/dnd", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -18,6 +19,7 @@ "bootstrap": "^5.3.3", "dotenv": "^16.4.5", "react": "^18.3.1", + "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.10.2", "react-bootstrap-icons": "^1.11.4", "react-datepicker": "^6.9.0", @@ -2178,10 +2180,9 @@ "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", - "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2684,6 +2685,55 @@ "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==", "license": "MIT" }, + "node_modules/@hello-pangea/dnd": { + "version": "17.0.0", + "resolved": "git+ssh://git@github.com/hello-pangea/dnd.git#77bec3fe1fb67265a9c7fac915d78ff4ecd940c0", + "dependencies": { + "@babel/runtime": "^7.25.6", + "css-box-model": "^1.2.1", + "memoize-one": "^6.0.0", + "raf-schd": "^4.0.3", + "react-redux": "^9.1.2", + "redux": "^5.0.1", + "use-memo-one": "^1.1.3" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@hello-pangea/dnd/node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/@hello-pangea/dnd/node_modules/react-redux": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "dependencies": { + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/@hello-pangea/dnd/node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -4020,6 +4070,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4163,6 +4222,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.34", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", + "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.10", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", @@ -4253,6 +4323,11 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/warning": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", @@ -6347,6 +6422,14 @@ "postcss": "^8.4" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -9275,6 +9358,19 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/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==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -11736,6 +11832,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -14176,6 +14277,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14265,6 +14371,25 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT" }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-bootstrap": { "version": "2.10.2", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.2.tgz", @@ -14474,6 +14599,30 @@ "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" } }, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -14673,6 +14822,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -16535,6 +16692,11 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16991,6 +17153,22 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "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/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3856c00..35a73b9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@hello-pangea/dnd": "github:hello-pangea/dnd", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -13,6 +14,7 @@ "bootstrap": "^5.3.3", "dotenv": "^16.4.5", "react": "^18.3.1", + "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.10.2", "react-bootstrap-icons": "^1.11.4", "react-datepicker": "^6.9.0", diff --git a/frontend/src/App.js b/frontend/src/App.js index 3f27809..6b21091 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -4,6 +4,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; import RecruiterDashboard from './components/RecruiterDashboard'; import AddCandidate from './components/AddCandidateForm'; import Positions from './components/Positions'; +import Position from './components/Position'; const App = () => { return ( @@ -12,6 +13,7 @@ const App = () => { } /> } /> {/* Agrega esta línea */} } /> + } /> ); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a53698a..472bb8a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,26 +1,17 @@ import React from 'react'; -import logo from './logo.svg'; -import './App.css'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import Positions from './components/Positions'; +import Position from './components/Position'; -function App() { - return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- ); -} +const App: React.FC = () => { + return ( + + + } /> + } /> + + + ); +}; export default App; diff --git a/frontend/src/components/Position.tsx b/frontend/src/components/Position.tsx new file mode 100644 index 0000000..e4d086b --- /dev/null +++ b/frontend/src/components/Position.tsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect } from 'react'; +import { Card, Container, Row, Col, Button } from 'react-bootstrap'; +import { useNavigate, useParams } from 'react-router-dom'; +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; + +type Candidate = { + id: number; + fullName: string; + averageScore: number; + phase: string; +}; + +const Position: React.FC = () => { + const navigate = useNavigate(); + const { id } = useParams<{ id: string }>(); + const [candidates, setCandidates] = useState([]); + const [positionTitle, setPositionTitle] = useState(''); + const [phases, setPhases] = useState([]); + const [phasesIds, setPhasesIds] = useState([]); + + useEffect(() => { + const fetchInterviewFlow = async () => { + try { + const response = await fetch(`http://localhost:3010/position/${id}/interviewFlow`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + setPositionTitle(data.positionName); + + if (data.interviewFlow && Array.isArray(data.interviewFlow.interviewFlow.interviewSteps)) { + setPhases(data.interviewFlow.interviewFlow.interviewSteps.map((step: any) => step.name)); + setPhasesIds(data.interviewFlow.interviewFlow.interviewSteps.map((step: any) => step.interviewTypeId)); + } else { + console.error("interviewFlow or interviewSteps is undefined or not an array"); + } + } catch (error) { + console.error("Failed to fetch interview flow:", error); + } + }; + + const fetchCandidates = async () => { + try { + const response = await fetch(`http://localhost:3010/position/${id}/candidates`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + const candidatesWithPhases = data.map((candidate: any, index: number) => ({ + id: index + 1, + fullName: candidate.fullName, + averageScore: candidate.averageScore, + phase: candidate.currentInterviewStep, + })); + setCandidates(candidatesWithPhases); + } catch (error) { + console.error("Failed to fetch candidates:", error); + } + }; + + fetchInterviewFlow(); + fetchCandidates(); + }, [id]); + + const getScoreEmojis = (score: number) => { + const maxEmojis = 5; + const filledEmojis = Math.round((score / 100) * maxEmojis); + return '🟢'.repeat(filledEmojis) + '⚪'.repeat(maxEmojis - filledEmojis); + }; + + const onDragEnd = async (result: any) => { + const { destination, source } = result; + + if (!destination) { + return; + } + + if (destination.droppableId === source.droppableId && destination.index === source.index) { + return; + } + + const updatedCandidates = Array.from(candidates); + const [movedCandidate] = updatedCandidates.splice(source.index, 1); + + movedCandidate.phase = destination.droppableId; + + updatedCandidates.splice(destination.index, 0, movedCandidate); + setCandidates(updatedCandidates); + + try { + const response = await fetch(`http://localhost:3010/candidates/${movedCandidate.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + applicationId: movedCandidate.id.toString(), + currentInterviewStep: destination.droppableId, + }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log("Candidate stage updated successfully:", data.message); + } catch (error) { + console.error("Failed to update candidate stage:", error); + } + }; + + return ( + +
+ +

{positionTitle}

+
+ + + {phases.map((phase, index) => ( + + {(provided: any) => ( + +

{phase}

+
+ {candidates + .filter(candidate => candidate.phase === phase) + .map((candidate, index) => ( + + {(provided: any) => ( + + + {candidate.fullName} + + {getScoreEmojis(candidate.averageScore)} + + + + )} + + ))} +
+ {provided.placeholder} + + )} +
+ ))} +
+
+
+ ); +}; + +export default Position; \ No newline at end of file diff --git a/frontend/src/components/Positions.tsx b/frontend/src/components/Positions.tsx index 822dccd..8625724 100644 --- a/frontend/src/components/Positions.tsx +++ b/frontend/src/components/Positions.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Card, Container, Row, Col, Form, Button } from 'react-bootstrap'; +import { useNavigate } from 'react-router-dom'; type Position = { title: string; @@ -15,6 +16,8 @@ const mockPositions: Position[] = [ ]; const Positions: React.FC = () => { + const navigate = useNavigate(); + return (

Posiciones

@@ -57,7 +60,7 @@ const Positions: React.FC = () => { {position.status}
- +
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 032464f..d0f88f2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -8,9 +8,7 @@ const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( - - - + ); // If you want to start measuring performance in your app, pass a function diff --git a/frontend/src/react-beautiful-dnd.d.ts b/frontend/src/react-beautiful-dnd.d.ts new file mode 100644 index 0000000..4731974 --- /dev/null +++ b/frontend/src/react-beautiful-dnd.d.ts @@ -0,0 +1,2 @@ +// src/react-beautiful-dnd.d.ts +declare module 'react-beautiful-dnd'; \ No newline at end of file diff --git a/prompts/prompts-dky.md b/prompts/prompts-dky.md new file mode 100644 index 0000000..fe39d66 --- /dev/null +++ b/prompts/prompts-dky.md @@ -0,0 +1,191 @@ + +## IDE USED: CURSOR + +### 1: +You are an excellent React frontend developer, with good Javascript knowledge also. You know how to apply best practices for frontend development, keep it simple code and well organized. + +In @Positions.tsx , when I click "Ver Proceso" I want to see the detail view for each position, named "position." Your task is to create the "position" interface, a page where you can view and manage the different candidates for a specific position. + +The interface is to be Kanban-style, displaying candidates as cards in different columns representing the phases of the hiring process, with the ability to update the phase of a candidate simply by dragging their card. + +Some design team requirements visible in the example are: + +- The position title should be displayed at the top for context. +- Add an arrow to the left of the title to return to the list of positions. +- Columns should be displayed for each phase in the process. +- Each candidate card should be placed in the corresponding phase and show their full name and average score. +- If possible, it should be properly displayed on mobile (phases vertically occupying the full width). + +Some observations: + +- Assume you can find the positions page. +- Assume the global page structure includes common elements like the top menu and footer. What you are creating is the internal content of the page. + + +Create me the source code needed to implement the interface only, we will do the logic later. + +### 2: + +For the code present, we get this error: +``` +Module '"react-router-dom"' has no exported member 'useHistory'. +``` +How can I resolve this? If you propose a fix, please make it concise. + +### 3: + +Thanks, now make that when I click "Ver proceso" button in @Positions.tsx I get shown this new interface. + +### 4: + +Dont use /position/:title as its a bad practice, just send me to the UI with a generic URL and show me the interface. + +### 5: + +When I access /position I get an empty page, fix this + +### 6: + +You did not change any line of code, and its still not working, I get a white page. + +### 7: + +In @Position.tsx do the following changes: +- Instead of 'Applied' change to 'Phone call' +- Instead of 'Interviewing' change to 'Technical interview' +- Instead of 'Offer' change to 'Cultural interview' +- Instead of 'Hired' change to 'Manager interview' +- Instead of showing 'Average Score:', show the emoji 'green circle' that also depends on the average score value to a maximum of 5 emoji's depending on the score (maximum 100). + +### 8: + +Now, for each candidate's card in @Position.tsx , I want it to be droppable around the different phases. + +### 9: + +For the code present, we get this error: +``` +Parameter 'provided' implicitly has an 'any' type. +``` +How can I resolve this? If you propose a fix, please make it concise. + +### 10: + + +For the code present, we get this error: +``` +Cannot use namespace 'DroppableProvided' as a type. +``` +How can I resolve this? If you propose a fix, please make it concise. + +### 11: + +Can you not use DroppableProvided? Find an alternative + +### 12: + +I get 'Module not found: Error: Can't resolve 'react-beautiful-dnd' in 'C:\Users\dimitar.kirilovyank\Desktop\curso\AI4Devs-frontend-main\frontend\src\components' when building. + +### 13: + +I get the following error when I try to drag and drop: Unable to find draggable with id: 1 + +### 14: + +Disable Strict Mode in @index.tsx + +### 15: + + +@Position.tsx +Now do the following changes. +- URL format should be /position/:id +- On page load, call the following API: GET /position/:id/interviewFlow + +Example service response: +{ + "positionName": "Senior backend engineer", + "interviewFlow": { + + "id": 1, + "description": "Standard development interview process", + "interviewSteps": [ + { + "id": 1, + "interviewFlowId": 1, + "interviewTypeId": 1, + "name": "Initial Screening", + "orderIndex": 1 + }, + { + "id": 2, + "interviewFlowId": 1, + "interviewTypeId": 2, + "name": "Technical Interview", + "orderIndex": 2 + }, + { + "id": 3, + "interviewFlowId": 1, + "interviewTypeId": 3, + "name": "Manager Interview", + "orderIndex": 2 + } + ] + } + } + +Make positionTitle use the positionName field from the service. +Make phases use the data from the service, which is interviewSteps. + + +### 16: + +i get error 'VM206:1 Uncaught (in promise) SyntaxError: Unexpected token '<', "