Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update routes and components for joinretropage and retropage #30

Merged
merged 3 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions packages/client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ import { RetroManagementPage } from './containers/RetroManagement/RetroManagemen
import CreateTeam from './containers/CreateTeamPage/CreateTeam';
import { PastRetroPage } from './containers/PastRetroPage/PastRetroPage';
import IndexPage from './containers/IndexPage/IndexPage';
import JoinRetroPage from './containers/JoinRetroPage/JoinRetroPage';
import { RetroCodeProvider } from './containers/Contexts/RetroCodeProvider';

function App() {
return (
<div className="app">
<Router>
<Navbar />
<Routes>
<Route path="/retro" element={<RetroPage />} />
<Route path="/" element={<IndexPage />} />
<Route path="/CreateNewTeamPage" element={<CreateTeam />} />
<Route path="*" element={<PageNotFound />} />
<Route path="/retromanagement" element={<RetroManagementPage />} />
<Route path="/retros/past" element={<PastRetroPage />} />
<Route path="/retros/:retroID" element={<RetroPage />} />
</Routes>
</Router>
<RetroCodeProvider>
<Router>
<Navbar />
<Routes>
<Route path="/" element={<IndexPage />} />
<Route path="/CreateNewTeamPage" element={<CreateTeam />} />
<Route path="/joinretro" element={<JoinRetroPage />} />
<Route path="/retro" element={<RetroPage />} />
<Route path="/retros/past" element={<PastRetroPage />} />
<Route path="/retromanagement" element={<RetroManagementPage />} />
<Route path="/retro/:retroID" element={<RetroPage />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</Router>
</RetroCodeProvider>
</div>
);
}
Expand Down
23 changes: 23 additions & 0 deletions packages/client/src/containers/Contexts/RetroCodeContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RetroCodeContext.js
import React, { createContext, useState, useMemo } from 'react';
import PropTypes from 'prop-types';

export const RetroCodeContext = createContext();

export const RetroCodeProvider = ({ children }) => {
const [retroCode, setRetroCode] = useState('');
const value = useMemo(
() => ({ retroCode, setRetroCode }),
[retroCode, setRetroCode],
);

return (
<RetroCodeContext.Provider value={value}>
{children}
</RetroCodeContext.Provider>
);
};

RetroCodeProvider.propTypes = {
children: PropTypes.node.isRequired,
};
25 changes: 25 additions & 0 deletions packages/client/src/containers/Contexts/RetroCodeProvider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState, useMemo } from 'react';
import { RetroCodeContext } from './RetroCodeContext';
import { PropTypes } from 'prop-types';

export function RetroCodeProvider({ children }) {
const [retroCode, setRetroCode] = useState('');
const value = useMemo(
() => ({ retroCode, setRetroCode }),
[retroCode, setRetroCode],
);

return (
<RetroCodeContext.Provider value={value}>
{children}
</RetroCodeContext.Provider>
);
}

RetroCodeProvider.propTypes = {
children: PropTypes.node,
};

RetroCodeProvider.defaultProps = {
children: null,
};
45 changes: 22 additions & 23 deletions packages/client/src/containers/JoinRetroPage/JoinRetroPage.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { apiURL } from '../../apiURL';
import './JoinRetroPage.css';
import { RetroCodeContext } from '../Contexts/RetroCodeContext';

export default function JoinRetroPage() {
const navigate = useNavigate();
Expand All @@ -10,22 +11,21 @@ export default function JoinRetroPage() {
const [error, setError] = useState(null);
const [retroCodeValue, setRetroCode] = useState('');
const [isValidRetroCode, setIsValidRetroCode] = useState(false);
const { setRetroCode: setSharedRetroCode } = useContext(RetroCodeContext);

const updateRetro = () => {
navigate('/UpdateTeam');
const updateTeam = () => {
navigate('*');
};

const pastRetro = () => {
navigate('/PastRetros');
navigate('/retros/past');
};

const initializeRetroSession = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(`${apiURL()}/generateRetroCode`, {
// Call the backend endpoint to generate retro code

const response = await fetch(`${apiURL()}/retro/generateRetroCode`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand All @@ -34,6 +34,7 @@ export default function JoinRetroPage() {

if (response.ok) {
const retroCode = await response.text();
setSharedRetroCode(retroCode);
setRetroCode(retroCode);
setCurrentDate(new Date().toLocaleDateString());
setIsValidRetroCode(true);
Expand All @@ -56,24 +57,18 @@ export default function JoinRetroPage() {
setLoading(true);
setError(null);
try {
const response = await fetch(`${apiURL()}/validateRetroCode`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ retroCode: retroCodeValue }),
});
await initializeRetroSession(); // generate and validate a new retro code

if (response.ok) {
// Retro code is valid, navigate to the next page or perform further actions
navigate(`/retropage/${retroCodeValue}`);
if (isValidRetroCode) {
navigate(`/retro/${retroCodeValue}`);
} else {
// Retro code is invalid, handle accordingly
const errorData = await response.json();
throw new Error(errorData.error || 'Invalid retro code');
throw new Error('Invalid retro code');
}
} catch (catchError) {
setError(catchError.message);
} finally {
setLoading(false);
}
};

Expand All @@ -96,9 +91,9 @@ export default function JoinRetroPage() {
<button
className="retro-button update-button"
type="button"
onClick={updateRetro}
onClick={updateTeam}
>
Update Retro
Update Team
</button>
<button
className="retro-button past-button"
Expand All @@ -112,13 +107,17 @@ export default function JoinRetroPage() {
type="text"
placeholder="Enter Retro Code"
value={retroCodeValue}
onChange={(e) => setRetroCode(e.target.value)}
onChange={(e) => {
const newRetroCode = e.target.value;
setRetroCode(newRetroCode);
setSharedRetroCode(newRetroCode);
}}
/>
<button
className="retro-button submit-button"
type="button"
onClick={handleSubmit}
disabled={!retroCodeValue || loading || !isValidRetroCode}
disabled={!retroCodeValue.trim() || loading || !isValidRetroCode}
>
Submit
</button>
Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/containers/RetroPage/RetroPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ body {
border-radius: 10px;
}

.error {
color: red;
}

.comment {
margin-right: 10px;
margin-top: 5px;
Expand Down
81 changes: 47 additions & 34 deletions packages/client/src/containers/RetroPage/RetroPage.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useState } from 'react';
import React, { useState, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { RetroCodeContext } from '../Contexts/RetroCodeContext';
import { apiURL } from '../../apiURL';
import './RetroPage.css';

const questions = [
Expand All @@ -10,18 +12,30 @@ const questions = [
];

function RetroPage() {
const [retroCode, setRetroCode] = useState(null);
const [selectedQuestions, setSelectedQuestions] = useState([]);
const [comments, setComments] = useState({});
const [joinCode, setJoinCode] = useState('');
const [inputValues, setInputValues] = useState({});
const [selectedRoles, setSelectedRoles] = useState({});
const [errorMessage, setErrorMessage] = useState('');
const navigate = useNavigate();
const { retroCode, setRetroCode } = useContext(RetroCodeContext);

const handleNewRetro = async () => {
const newRetroCode = Math.random().toString(36).substring(2, 10);
setRetroCode(newRetroCode);
setSelectedQuestions(questions);
const response = await fetch(`${apiURL()}/retro/generateRetroCode`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});

if (response.ok) {
const newRetroCode = await response.text();
setRetroCode(newRetroCode);
setSelectedQuestions(questions);
} else {
throw new Error('Invalid retro code');
}
};

const handleAddComment = async (questionId) => {
Expand All @@ -41,38 +55,36 @@ function RetroPage() {
};

const handleComplete = () => {
navigate('/retro');
navigate('/joinretro');
};

const handleJoin =
(async () => {
try {
if (!joinCode) {
return;
}

const response = await fetch(`/api/retros/${joinCode}`);

if (!response.ok) {
throw new Error(
`Failed to fetch retro session: ${response.statusText}`,
);
}

const retroSession = await response.json();

setRetroCode(retroSession.retroCode);
setSelectedQuestions(retroSession.questions);
setComments(retroSession.comments);
setJoinCode('');
} catch (error) {
// eslint-disable-next-line no-alert
alert(
'Failed to join the retro session. Please check the code and try again.',
const handleJoin = async () => {
try {
if (!joinCode) {
return;
}

const response = await fetch(`${apiURL()}/retro/join/${joinCode}`);

if (!response.ok) {
throw new Error(
`Failed to fetch retro session: ${response.statusText}`,
);
}
},
[joinCode]);

const retroSession = await response.json();

setRetroCode(retroSession.retroCode);
setSelectedQuestions(retroSession.questions);
setComments(retroSession.comments || {});
setJoinCode('');
setErrorMessage('');
} catch (error) {
setErrorMessage(
'Failed to join the retro session. Please check the code and try again.',
);
}
};

const handleCommentInput = (questionId, e) => {
if (e.key === 'Enter') {
Expand All @@ -89,13 +101,14 @@ function RetroPage() {
<div>
<div className="retroContainer">
<h2 className="retroTitle">Retro</h2>
{errorMessage && <div className="errorMessage">{errorMessage}</div>}
<div className="newRetroContainer">
<button
className="newRetroButton"
type="button"
onClick={handleNewRetro}
>
Start Retro
New Retro
</button>
</div>
{retroCode && (
Expand Down
28 changes: 26 additions & 2 deletions packages/server/api/controllers/retroController.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,37 @@ const completeRetro = async (routeParams, body) => {
}
};

const joinRetro = async (routeParams) => {
const generateRetroCode = async () => {
// Generate a single 8-character retro code without been inside a double-quoted string
const retroCode = String(randomUUID()).substring(0, 8);
return retroCode;
};

const joinRetro = async (routeParams, userId) => {
// Check if the user has already joined the retro session
const alreadyJoined = await knex('RetroParticipants')
.where({
retro_id: routeParams.retroCode,
user_id: userId,
})
.first();

if (alreadyJoined) {
throw new HttpError('You have already joined this retro session', 400);
}

const results = await knex('Retro')
.select('Retro.id', 'Retro.team_id', 'Retro.title', 'Retro.date')
.where({
retro_code: routeParams.retroCode,
status: 'in-progress',
});

// Add the user to the retro session
await knex('RetroParticipants').insert({
retro_id: results[0].id,
user_id: userId,
});

return results[0];
};

Expand All @@ -119,4 +142,5 @@ module.exports = {
deleteRetro,
completeRetro,
joinRetro,
generateRetroCode,
};
Loading
Loading