From 937348a501a41f348fafc8fdaca69e2cfbaeded3 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Sun, 28 Nov 2021 06:32:32 +0700 Subject: [PATCH 01/19] feat(survey) - init survey page comp. - add link to navbar - add route --- src/components/survey/AddChoice.jsx | 0 src/components/survey/CreateSurvey.jsx | 128 +++++++++++++++++++++++++ src/components/utility/Navbar.jsx | 10 ++ src/routes/App.jsx | 2 + 4 files changed, 140 insertions(+) create mode 100644 src/components/survey/AddChoice.jsx create mode 100644 src/components/survey/CreateSurvey.jsx diff --git a/src/components/survey/AddChoice.jsx b/src/components/survey/AddChoice.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/survey/CreateSurvey.jsx b/src/components/survey/CreateSurvey.jsx new file mode 100644 index 0000000..e72c4ef --- /dev/null +++ b/src/components/survey/CreateSurvey.jsx @@ -0,0 +1,128 @@ +import axios from 'axios'; +import React, { useState, useEffect } from 'react'; +import { useInput } from '../../helpers/hooks/input-hook'; +import { authHeader } from '../../helpers/services/auth.service'; +import { API_URL } from '../../config/keys'; +import '../../styles/thread/Form.css'; + +export default function CreateSurvey(props) { + const { history } = props; + + const back = () => { + history.goBack(); + }; + + const redirect = (path) => { + history.push(path); + }; + + const { value: title, bind: bindTitle, reset: resetTitle } = useInput(''); + const { value: body, bind: bindBody, reset: resetBody } = useInput(''); + const { value: topicId, bind: bindTopicId, reset: resetTopicId } = useInput( + 0 + ); + + const [listTopic, setlistTopic] = useState([]); + const [message, setMessage] = useState(''); + + useEffect(() => { + async function getListTopic() { + const allTopic = await (await axios.get(`${API_URL}/topics`))?.data?.data; + setlistTopic(allTopic); + } + getListTopic(); + }, []); + + const handleSubmit = async (event) => { + event.preventDefault(); + try { + console.log(body); + const thisThread = await axios.post( + `${API_URL}/surveys`, + { + thread: { + content: body, + points: 0, + title: title, + topic_id: topicId, + }, + }, + { + headers: authHeader(), + } + ); + const { data } = thisThread?.data; + redirect(`/topic/${data?.topic_name}/${data?.id}/page/1`); + } catch (error) { + setMessage("Thread's title already exist in the selected topic"); + } + resetBody(); + resetTitle(); + resetTopicId(); + }; + + return ( + <div className="formComponentContainer"> + <div className="back" onClick={back}> + <i className="fas fa-angle-left"></i> + <h5>Back</h5> + </div> + <div className="header"> + <h1> + <b>Create a Survey</b> + </h1> + </div> + <div className="formSection"> + <div> + {message && ( + <div> + <div className="alert alert-danger" role="alert"> + <p>{message}</p> + </div> + </div> + )} + <form onSubmit={handleSubmit}> + <div className="formContainer"> + <label for="title">Title</label> + <input + type="text" + className="title" + name="title" + placeholder="Your survey's title" + required="false" + {...bindTitle} + /> + <label>Topic</label> + <select + className="topic" + name="topic" + required="false" + {...bindTopicId} + > + <option value="" /> + {listTopic.map((topic) => ( + <option value={topic.id}>{topic.name}</option> + ))} + </select> + <label for="body">Body</label> + <textarea + className="body" + name="body" + placeholder="Your thread's body" + required="false" + {...bindBody} + /> + <div className="buttonContainer"> + <input + type="submit" + className="buttonSubmit" + value="Create Thread" + /> + </div> + </div> + </form> + </div> + </div> + </div> + ); +} diff --git a/src/components/utility/Navbar.jsx b/src/components/utility/Navbar.jsx index a6f8038..3d0a64b 100644 --- a/src/components/utility/Navbar.jsx +++ b/src/components/utility/Navbar.jsx @@ -67,6 +67,16 @@ const Navbar = (props) => { <b>Threads</b> </NavLink> </li> + <li className="nav-item mr-auto"> + <NavLink + exact + activeClassName="navbar--active" + className="nav-link" + to="survey/page/1" + > + <b>Surveys</b> + </NavLink> + </li> <li className="nav-item mr-auto"> <NavLink exact diff --git a/src/routes/App.jsx b/src/routes/App.jsx index 6f72f0c..a0172f7 100644 --- a/src/routes/App.jsx +++ b/src/routes/App.jsx @@ -13,6 +13,7 @@ import EditThread from '../components/thread/EditThread'; import Footer from '../components/utility/Footer'; import UpdateProfileForm from '../components/profile/UpdateProfileForm.jsx'; import EditComment from '../components/thread/EditComment'; +import CreateSurvey from '../components/survey/CreateSurvey'; import { BrowserRouter as Router, Route, @@ -52,6 +53,7 @@ function App() { <Route exact path="/login" component={LoginForm} /> <Route exact path="/create/topic" component={CreateTopicForm} /> <Route exact path="/create/thread" component={CreateThread} /> + <Route exact path="/create/survey" component={CreateSurvey} /> <Route exact path="/search/:input/page/:pageNumber" component={Search} /> <Route exact -- GitLab From 690d6f347e9acae927d2be79435f30917f40ab55 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 07:32:26 +0700 Subject: [PATCH 02/19] feat(create-survey) - add duration widget - add html datetime to UTC converter util method --- .../{AddChoice.jsx => AddChoiceInput.jsx} | 0 src/components/survey/CreateSurvey.jsx | 34 +++++++++++++++++-- src/helpers/time-util.js | 7 +++- src/styles/survey/Form.css | 16 +++++++++ src/styles/thread/Form.css | 2 +- 5 files changed, 55 insertions(+), 4 deletions(-) rename src/components/survey/{AddChoice.jsx => AddChoiceInput.jsx} (100%) create mode 100644 src/styles/survey/Form.css diff --git a/src/components/survey/AddChoice.jsx b/src/components/survey/AddChoiceInput.jsx similarity index 100% rename from src/components/survey/AddChoice.jsx rename to src/components/survey/AddChoiceInput.jsx diff --git a/src/components/survey/CreateSurvey.jsx b/src/components/survey/CreateSurvey.jsx index e72c4ef..63edf5f 100644 --- a/src/components/survey/CreateSurvey.jsx +++ b/src/components/survey/CreateSurvey.jsx @@ -3,7 +3,10 @@ import React, { useState, useEffect } from 'react'; import { useInput } from '../../helpers/hooks/input-hook'; import { authHeader } from '../../helpers/services/auth.service'; import { API_URL } from '../../config/keys'; + import '../../styles/thread/Form.css'; +import '../../styles/survey/Form.css'; +import { datetimeToUTC } from '../../helpers/time-util'; export default function CreateSurvey(props) { const { history } = props; @@ -21,6 +24,8 @@ export default function CreateSurvey(props) { const { value: topicId, bind: bindTopicId, reset: resetTopicId } = useInput( 0 ); + const { value: endDate, bind: bindEndDate, reset: resetEndDate } = useInput(0) + const { value: endTime, bind: bindEndTime, reset: resetEndTime } = useInput(0) const [listTopic, setlistTopic] = useState([]); const [message, setMessage] = useState(''); @@ -33,6 +38,9 @@ export default function CreateSurvey(props) { getListTopic(); }, []); + useEffect(() => { + }, [endDate, endTime]) + const handleSubmit = async (event) => { event.preventDefault(); try { @@ -45,6 +53,7 @@ export default function CreateSurvey(props) { points: 0, title: title, topic_id: topicId, + datetime: datetimeToUTC(endDate, endTime), }, }, { @@ -59,6 +68,8 @@ export default function CreateSurvey(props) { resetBody(); resetTitle(); resetTopicId(); + resetEndDate(); + resetEndTime(); }; return ( @@ -104,14 +115,33 @@ export default function CreateSurvey(props) { <option value={topic.id}>{topic.name}</option> ))} </select> - <label for="body">Body</label> + <label for="choices">Choices</label> <textarea className="body" name="body" - placeholder="Your thread's body" + placeholder="Your thread's body"t required="false" {...bindBody} /> + <label for="duration">Duration</label> + <div className="d-flex justify-content-between"> + <input + className="end-date w-100" + name="end-date" + placeholder="End Date" + required="false" + type="date" + {...bindEndDate} + /> + <input + className="end-time w-100" + name="end-time" + placeholder="End Time" + required="false" + type="time" + {...bindEndTime} + /> + </div> <div className="buttonContainer"> <input type="submit" diff --git a/src/helpers/time-util.js b/src/helpers/time-util.js index 73b16ed..ca432dc 100644 --- a/src/helpers/time-util.js +++ b/src/helpers/time-util.js @@ -10,4 +10,9 @@ function translate(time) { } catch (error) {} } -export { translate }; +const datetimeToUTC = (endDate, endTime) => { + const date = new Date(`${endDate} ${endTime}`); + return date.toISOString(); +} + +export { translate, datetimeToUTC}; diff --git a/src/styles/survey/Form.css b/src/styles/survey/Form.css new file mode 100644 index 0000000..4e56ee4 --- /dev/null +++ b/src/styles/survey/Form.css @@ -0,0 +1,16 @@ +input.end-date, +input.end-time { + padding: 12px 16px; + border-radius: 8px; + border: 0; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); + margin-bottom: 32px; +} + +input.end-date { + margin-right: 12px; +} + +input.end-time { + margin-left: 12px; +} \ No newline at end of file diff --git a/src/styles/thread/Form.css b/src/styles/thread/Form.css index a8909a5..7d1aa25 100644 --- a/src/styles/thread/Form.css +++ b/src/styles/thread/Form.css @@ -57,7 +57,7 @@ label { .topic { min-width: 303px; - max-width: 400px; + max-width: 3696px; font-family: "DM Sans"; height: 45px; padding: 12px 16px; -- GitLab From 404042c011c3179f3bc235bc03754e4f9351df4e Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 12:41:51 +0700 Subject: [PATCH 03/19] feat(create-survey): finish ui impl. --- src/components/survey/CreateSurvey.jsx | 103 ++++++++++++++++++++----- src/components/survey/dummy.js | 21 +++++ src/images/add-choice.svg | 21 +++++ src/images/del-choice.svg | 21 +++++ src/styles/survey/Form.css | 40 +++++++++- 5 files changed, 187 insertions(+), 19 deletions(-) create mode 100644 src/components/survey/dummy.js create mode 100644 src/images/add-choice.svg create mode 100644 src/images/del-choice.svg diff --git a/src/components/survey/CreateSurvey.jsx b/src/components/survey/CreateSurvey.jsx index 63edf5f..2667650 100644 --- a/src/components/survey/CreateSurvey.jsx +++ b/src/components/survey/CreateSurvey.jsx @@ -8,6 +8,24 @@ import '../../styles/thread/Form.css'; import '../../styles/survey/Form.css'; import { datetimeToUTC } from '../../helpers/time-util'; +import AddChoiceSvg from '../../images/add-choice.svg'; +import DelChoiceSvg from '../../images/del-choice.svg'; + +const INITIAL_CHOICES = [ + { + "choiceNum": 1, + "choice": "", + }, + { + "choiceNum": 2, + "choice": "", + }, + { + "choiceNum": 3, + "choice": "", + } +] + export default function CreateSurvey(props) { const { history } = props; @@ -20,13 +38,11 @@ export default function CreateSurvey(props) { }; const { value: title, bind: bindTitle, reset: resetTitle } = useInput(''); - const { value: body, bind: bindBody, reset: resetBody } = useInput(''); - const { value: topicId, bind: bindTopicId, reset: resetTopicId } = useInput( - 0 - ); - const { value: endDate, bind: bindEndDate, reset: resetEndDate } = useInput(0) - const { value: endTime, bind: bindEndTime, reset: resetEndTime } = useInput(0) + const { value: topicId, bind: bindTopicId, reset: resetTopicId } = useInput(0); + const { value: endDate, bind: bindEndDate, reset: resetEndDate } = useInput(null); + const { value: endTime, bind: bindEndTime, reset: resetEndTime } = useInput(null); + const [choices, setChoices] = useState(INITIAL_CHOICES); const [listTopic, setlistTopic] = useState([]); const [message, setMessage] = useState(''); @@ -38,18 +54,34 @@ export default function CreateSurvey(props) { getListTopic(); }, []); - useEffect(() => { - }, [endDate, endTime]) + const handleChoiceInput = id => e => { + let updatedChoices = [...choices]; + updatedChoices[id].choice = e.target.value; + setChoices(updatedChoices); + } + + const handleAddChoiceBelow = id => { + let updatedChoices = [...choices]; + updatedChoices.splice(id+1, 0, { + "choiceNum": id+2, + "choice": "" + }); + setChoices(updatedChoices); + } + + const handleDeleteChoice = id => { + let updatedChoices = [...choices]; + updatedChoices.splice(id, 1); + setChoices(updatedChoices); + } const handleSubmit = async (event) => { event.preventDefault(); try { - console.log(body); const thisThread = await axios.post( `${API_URL}/surveys`, { thread: { - content: body, points: 0, title: title, topic_id: topicId, @@ -65,7 +97,6 @@ export default function CreateSurvey(props) { } catch (error) { setMessage("Thread's title already exist in the selected topic"); } - resetBody(); resetTitle(); resetTopicId(); resetEndDate(); @@ -116,13 +147,49 @@ export default function CreateSurvey(props) { ))} </select> <label for="choices">Choices</label> - <textarea - className="body" - name="body" - placeholder="Your thread's body"t - required="false" - {...bindBody} - /> + <div className="choices"> + {choices.length ? ( + choices.map((_, id) => ( + <div className="choice"> + <input + className="choice__input" + name="choice" + placeholder={`Choice ${id + 1}`} + required="false" + onChange={handleChoiceInput(id)} + value={choices[id].choice} + /> + <div className="choice__navs"> + <div + className="choice__navs-item add" + onClick={() => handleAddChoiceBelow(id)} + > + <img src={AddChoiceSvg} alt="add-choice" /> + </div> + <div + className="choice__navs-item del" + onClick={() => handleDeleteChoice(id)} + > + <img src={DelChoiceSvg} alt="del-choice" /> + </div> + </div> + </div> + )) + ) : ( + <div className="choice__navs"> + <div + className="choice__navs-item add" + > + <img src={AddChoiceSvg} alt="add-choice" /> + </div> + <div + className="choice__navs-item del" + > + <img src={DelChoiceSvg} alt="del-choice" /> + </div> + </div> + )} + </div> <label for="duration">Duration</label> <div className="d-flex justify-content-between"> <input diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js new file mode 100644 index 0000000..29ac95f --- /dev/null +++ b/src/components/survey/dummy.js @@ -0,0 +1,21 @@ +const DUMMY_SURVEYS = [ + { + "id": 251, + "title": "You snap your finger and one of the following are erased for ever, what do you choose ?", + "choices": [ + { + "id": 1, + "choice": "World Hunger" + } + ], + "inserted_at": "2021-11-26T12:34:22", + "updated_at": "2021-11-26T12:34:22", + "points": 0, + "topic_id": 22, + "topic_name": "Ok Google", + "user_id": 15, + "username": "Ahmad Haulian Yoga Pratama" + } +] + +export default DUMMY_SURVEYS; \ No newline at end of file diff --git a/src/images/add-choice.svg b/src/images/add-choice.svg new file mode 100644 index 0000000..0a054ca --- /dev/null +++ b/src/images/add-choice.svg @@ -0,0 +1,21 @@ +<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g filter="url(#filter0_d_226_946)"> +<rect x="2" width="30" height="30" fill="url(#pattern0)" shape-rendering="crispEdges"/> +</g> +<defs> +<filter id="filter0_d_226_946" x="0" y="0" width="34" height="34" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="2"/> +<feGaussianBlur stdDeviation="1"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_226_946"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_226_946" result="shape"/> +</filter> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_226_946" transform="scale(0.0333333)"/> +</pattern> +<image id="image0_226_946" width="30" height="30" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAAsElEQVRIie2VsQ3CMBBF37EDFNAwDxVDgJC8gTOAMwEIwRY0jJMUmcOmCZJd5iBRBPer85f8n2xL32CaSKLa5f0ekRspJeBIXT+GRixUYJEzsERkhchFE6EDwzqbN1OCP5aBDfx74LK5vN/15bD9MqcFHCE830Z5YpHrCFD6zHtuzOaNT0AzAqchxkNu6H6nqkrFOoTBObO5agMb+H/BbTarCkcLdkAHdMTolBmmafQCluEdELXKEHIAAAAASUVORK5CYII="/> +</defs> +</svg> diff --git a/src/images/del-choice.svg b/src/images/del-choice.svg new file mode 100644 index 0000000..e956724 --- /dev/null +++ b/src/images/del-choice.svg @@ -0,0 +1,21 @@ +<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g filter="url(#filter0_d_226_947)"> +<rect x="2" width="30" height="30" fill="url(#pattern0)" shape-rendering="crispEdges"/> +</g> +<defs> +<filter id="filter0_d_226_947" x="0" y="0" width="34" height="34" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="2"/> +<feGaussianBlur stdDeviation="1"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_226_947"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_226_947" result="shape"/> +</filter> +<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_226_947" transform="scale(0.0333333)"/> +</pattern> +<image id="image0_226_947" width="30" height="30" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAAhUlEQVRIie3SQQqAIBCF4TdzGztBRLXyLJ3SlQbdoA5g1wjbRwWmBsL8ywH9RAWkn6LYBZseFwDtZbwoY7tkeNWDI1Afe6i7AsLcGDdc55xj87cYFEobdfT4uTY9ZrkiZeytUfyNnxJYYIEFFljgmmDCnro5AT4a5oOmFJwAj8DT1/VS/Z1SDxeJl/d4WwAAAABJRU5ErkJggg=="/> +</defs> +</svg> diff --git a/src/styles/survey/Form.css b/src/styles/survey/Form.css index 4e56ee4..374180d 100644 --- a/src/styles/survey/Form.css +++ b/src/styles/survey/Form.css @@ -5,6 +5,7 @@ input.end-time { border: 0; box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); margin-bottom: 32px; + font-family: "DM Sans"; } input.end-date { @@ -13,4 +14,41 @@ input.end-date { input.end-time { margin-left: 12px; -} \ No newline at end of file +} + +.choices { + padding: 12px 24px; + border-radius: 8px; + border: 0; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); + margin-bottom: 32px; + font-family: "DM Sans"; +} + +.choice { + width: 100%; + display: flex; + align-items: center; +} + +input.choice__input { + width: 100%; + padding: 12px 16px; + margin: 12px 0px; + border-radius: 8px; + border: 0; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); +} + +.choice__navs { + display: flex; + margin-left: 24px; +} + +.choice__navs-item { + cursor: pointer; +} + +.choice__navs-item:last-child { + margin-left: 7px; +} -- GitLab From fa5e337631c73b28b912daddd69ca27a7e8a7250 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 12:49:00 +0700 Subject: [PATCH 04/19] feat(create-survey): add empty placeholder --- src/components/survey/CreateSurvey.jsx | 17 ++++------------- src/styles/survey/Form.css | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/components/survey/CreateSurvey.jsx b/src/components/survey/CreateSurvey.jsx index 2667650..31dc83f 100644 --- a/src/components/survey/CreateSurvey.jsx +++ b/src/components/survey/CreateSurvey.jsx @@ -13,15 +13,12 @@ import DelChoiceSvg from '../../images/del-choice.svg'; const INITIAL_CHOICES = [ { - "choiceNum": 1, "choice": "", }, { - "choiceNum": 2, "choice": "", }, { - "choiceNum": 3, "choice": "", } ] @@ -62,10 +59,7 @@ export default function CreateSurvey(props) { const handleAddChoiceBelow = id => { let updatedChoices = [...choices]; - updatedChoices.splice(id+1, 0, { - "choiceNum": id+2, - "choice": "" - }); + updatedChoices.splice(id+1, 0, {"choice": ""}); setChoices(updatedChoices); } @@ -176,17 +170,14 @@ export default function CreateSurvey(props) { </div> )) ) : ( - <div className="choice__navs"> + <div className="choice-empty"> + Add Choices <div className="choice__navs-item add" + onClick={() => handleAddChoiceBelow(-1)} > <img src={AddChoiceSvg} alt="add-choice" /> </div> - <div - className="choice__navs-item del" - > - <img src={DelChoiceSvg} alt="del-choice" /> - </div> </div> )} </div> diff --git a/src/styles/survey/Form.css b/src/styles/survey/Form.css index 374180d..64b937f 100644 --- a/src/styles/survey/Form.css +++ b/src/styles/survey/Form.css @@ -31,6 +31,23 @@ input.end-time { align-items: center; } +.choice-empty { + text-align: center; + font-family: "DM Sans"; + font-size: 18px; + color: #000000; + font-weight: bold; +} + +.choice-empty > .choice__navs-item.add { + width: fit-content; + padding: 5px; + margin: 12px auto; + border-radius: 8px; + border: 0; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); +} + input.choice__input { width: 100%; padding: 12px 16px; -- GitLab From f700e6c01f513d0cb4373006977a9436efea1ac7 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 14:09:17 +0700 Subject: [PATCH 05/19] feat(list-survey): add files, using thread components as reference --- src/components/survey/CreateSurvey.jsx | 2 +- src/components/survey/ListSurveys.jsx | 58 +++++++++++++++++++++++++ src/components/survey/PreviewThread.jsx | 29 +++++++++++++ src/components/survey/RecentSurveys.jsx | 55 +++++++++++++++++++++++ src/components/survey/SurveyList.jsx | 18 ++++++++ src/components/survey/TopSurveys.jsx | 54 +++++++++++++++++++++++ src/components/survey/dummy.js | 39 +++++++++++++++-- src/routes/App.jsx | 8 ++-- 8 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 src/components/survey/ListSurveys.jsx create mode 100644 src/components/survey/PreviewThread.jsx create mode 100644 src/components/survey/RecentSurveys.jsx create mode 100644 src/components/survey/SurveyList.jsx create mode 100644 src/components/survey/TopSurveys.jsx diff --git a/src/components/survey/CreateSurvey.jsx b/src/components/survey/CreateSurvey.jsx index 31dc83f..ee60635 100644 --- a/src/components/survey/CreateSurvey.jsx +++ b/src/components/survey/CreateSurvey.jsx @@ -204,7 +204,7 @@ export default function CreateSurvey(props) { <input type="submit" className="buttonSubmit" - value="Create Thread" + value="Create Survey" /> </div> </div> diff --git a/src/components/survey/ListSurveys.jsx b/src/components/survey/ListSurveys.jsx new file mode 100644 index 0000000..426435d --- /dev/null +++ b/src/components/survey/ListSurveys.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import '../../styles/thread/ListThreads.css'; +import Button from '../utility/Button'; +import TopSurveys from './TopSurveys'; +import RecentSurveys from './RecentSurveys'; +import { loggedIn } from '../../helpers/services/auth.service'; +import { Link } from 'react-router-dom'; + +export default function ListSurveys(props) { + return ( + <div className="listThreadsContainer"> + <div className="header"> + <h1> + <b>Threads</b> + </h1> + </div> + + {props.isRecent ? ( + <div className="listThreadsSection"> + <div className="subHeaderListThreads"> + <div className="tab"> + <Link to="/page/1" style={{ textDecoration: 'none' }}> + <h3 className="active">Recent</h3> + </Link> + <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <h3>Top</h3> + </Link> + </div> + {loggedIn && ( + <Button text="Create Thread" color="orange" url="create/thread" /> + )} + </div> + <RecentSurveys + pageNumber={props.pageNumber} + history={props.history} + /> + </div> + ) : ( + <div className="listThreadsSection"> + <div className="subHeaderListThreads"> + <div className="tab"> + <Link to="/page/1" style={{ textDecoration: 'none' }}> + <h3>Recent</h3> + </Link> + <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <h3 className="active">Top</h3> + </Link> + </div> + {loggedIn && ( + <Button text="Create Thread" color="orange" url="create/thread" /> + )} + </div> + <TopSurveys pageNumber={props.pageNumber} history={props.history} /> + </div> + )} + </div> + ); +} diff --git a/src/components/survey/PreviewThread.jsx b/src/components/survey/PreviewThread.jsx new file mode 100644 index 0000000..7891c1a --- /dev/null +++ b/src/components/survey/PreviewThread.jsx @@ -0,0 +1,29 @@ +import '../../styles/thread/PreviewThread.css'; +import { translate } from '../../helpers/time-util'; +import { Link } from 'react-router-dom'; + +export default function PreviewThread(props) { + const { content } = props; + const time = translate(content.inserted_at); + return ( + <div className="threadCard"> + <div className="threadCardHeader"> + <h2 className="previewThreadTitle"> + <b>{content.title}</b> + </h2> + </div> + <p className="previewThreadTopic">{content.topic_name}</p> + <div className="threadCardContent"> + <p> + By{' '} + <Link to={`/profile/${content.username}/1`}>{content.username}</Link>{' '} + {', '} + {window.innerWidth < 780 && ( + <br></br> + )} + {time} - <i className="far fa-thumbs-up" /> {content.points} + </p> + </div> + </div> + ); +} diff --git a/src/components/survey/RecentSurveys.jsx b/src/components/survey/RecentSurveys.jsx new file mode 100644 index 0000000..4db3329 --- /dev/null +++ b/src/components/survey/RecentSurveys.jsx @@ -0,0 +1,55 @@ +/* eslint-disable eqeqeq */ +import React, { useEffect, useState, Fragment } from 'react'; +import axios from 'axios'; +import SurveyList from './SurveyList'; +import { API_URL } from '../../config/keys'; +import Pagination from '../thread/Pagination'; + +export default function RecentSurveys(props) { + const [threads, setThreads] = useState([]); + const [totalItems, setTotalItems] = useState(0); + + const currentPage = props.pageNumber; + + if (currentPage != props.pageNumber) { + window.location.reload(); + } + + useEffect(() => { + const fetch = async () => { + const responseThreads = await axios.get( + `${API_URL}/threads/pages/recent/?page=${currentPage}` + ); + const { data } = responseThreads; + setThreads(data?.data); + setTotalItems(data?.metadata?.total_data); + }; + fetch(); + }, [currentPage]); + + function switchPage(pageNumber) { + props.history.push(`/page/${pageNumber}`); + window.location.reload(); + } + + return ( + <div className="recentThreads"> + {totalItems == 0 ? ( + <div className="noThreadsMessage"> + <p>There is no threads yet.</p> + </div> + ) : ( + <Fragment> + <SurveyList thread={threads} /> + <div className="paginationContainer"> + <Pagination + activePage={currentPage} + totalItems={totalItems} + switchPage={switchPage} + /> + </div> + </Fragment> + )} + </div> + ); +} diff --git a/src/components/survey/SurveyList.jsx b/src/components/survey/SurveyList.jsx new file mode 100644 index 0000000..00cb2e2 --- /dev/null +++ b/src/components/survey/SurveyList.jsx @@ -0,0 +1,18 @@ +import PreviewThread from './PreviewThread'; +import { Link } from 'react-router-dom'; + +export default function SurveyList(props) { + return ( + <div className="list_threads"> + {props.thread.map((value) => ( + <Link + key={value.id} + to={`/topic/${value.topic_name}/${value.id}/page/1`} + style={{ textDecoration: 'none' }} + > + <PreviewThread content={value} /> + </Link> + ))} + </div> + ); +} diff --git a/src/components/survey/TopSurveys.jsx b/src/components/survey/TopSurveys.jsx new file mode 100644 index 0000000..b45b91d --- /dev/null +++ b/src/components/survey/TopSurveys.jsx @@ -0,0 +1,54 @@ +/* eslint-disable eqeqeq */ +import React, { useEffect, useState, Fragment } from 'react'; +import axios from 'axios'; +import SurveyList from './SurveyList'; +import { API_URL } from '../../config/keys'; +import Pagination from './Pagination'; + +export default function TopSurveys(props) { + const [threads, setThreads] = useState([]); + const currentPage = props.pageNumber; + const [totalItems, setTotalItems] = useState(0); + + if (currentPage != props.pageNumber) { + window.location.reload(); + } + + useEffect(() => { + const fetch = async () => { + const responseThreads = await axios.get( + `${API_URL}/threads/pages/top/?page=${currentPage}` + ); + const { data } = responseThreads; + setThreads(data?.data); + setTotalItems(data?.metadata?.total_data); + }; + fetch(); + }, [currentPage]); + + function switchPage(pageNumber) { + props.history.push(`/top/page/${pageNumber}`); + window.location.reload(); + } + + return ( + <div className="topThreads"> + {totalItems == 0 ? ( + <div className="noThreadsMessage"> + <p>There is no threads yet.</p> + </div> + ) : ( + <Fragment> + <SurveyList thread={threads} /> + <div className="paginationContainer"> + <Pagination + activePage={currentPage} + totalItems={totalItems} + switchPage={switchPage} + /> + </div> + </Fragment> + )} + </div> + ); +} diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js index 29ac95f..425c406 100644 --- a/src/components/survey/dummy.js +++ b/src/components/survey/dummy.js @@ -1,12 +1,45 @@ const DUMMY_SURVEYS = [ { "id": 251, - "title": "You snap your finger and one of the following are erased for ever, what do you choose ?", + "title": "You snap your finger and one of the following are erased for ever, what do you choose?", "choices": [ { - "id": 1, "choice": "World Hunger" - } + }, + { + "choice": "Poverty" + }, + { + "choice": "Famine" + }, + ], + "inserted_at": "2021-11-26T12:34:22", + "updated_at": "2021-11-26T12:34:22", + "points": 0, + "topic_id": 22, + "topic_name": "Ok Google", + "user_id": 15, + "username": "Ahmad Haulian Yoga Pratama" + }, + { + "id": 252, + "title": "Kamu suka pakai bahasa pemrograman fungsional yang mana?", + "choices": [ + { + "choice": "Scala" + }, + { + "choice": "Haskell" + }, + { + "choice": "Mercury" + }, + { + "choice": "Clojure" + }, + { + "choice": "OCaml" + }, ], "inserted_at": "2021-11-26T12:34:22", "updated_at": "2021-11-26T12:34:22", diff --git a/src/routes/App.jsx b/src/routes/App.jsx index a0172f7..e15902a 100644 --- a/src/routes/App.jsx +++ b/src/routes/App.jsx @@ -20,6 +20,7 @@ import { Redirect, Switch, } from 'react-router-dom'; +import ListSurveys from '../components/survey/ListSurveys'; function App() { return ( @@ -28,7 +29,7 @@ function App() { <Switch> <Route exact - path="/page/:pageNumber" + path="/threads/page/:pageNumber" component={(props) => ( <ListThreads isRecent={true} @@ -39,7 +40,7 @@ function App() { /> <Route exact - path="/top/page/:pageNumber" + path="/threads/top/page/:pageNumber" component={(props) => ( <ListThreads isRecent={false} @@ -62,10 +63,11 @@ function App() { /> <Route exact path="/topic/:topic/page/:pageNumber" component={Topic} /> <Route exact path="/threads" component={ListThreads} /> + <Route exact path="/surveys" component={ListSurveys} /> <Route exact path="/thread/edit/:thread" component={EditThread} /> <Route exact path="/profile/update" component={UpdateProfileForm} /> <Route exact path="/comment/edit/:comment" component={EditComment} /> - <Redirect exact from="" to="page/1" /> + <Redirect exact from="" to="/threads/page/1" /> </Switch> <Footer /> </Router> -- GitLab From 6b6deeba3cc4774d6ba94b9a3cacbea11671a6a4 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 14:48:07 +0700 Subject: [PATCH 06/19] feat(list-survey): init PreviewChoices comp. --- src/components/survey/ListSurveys.jsx | 22 +++--- src/components/survey/PreviewChoices.jsx | 10 +++ .../{PreviewThread.jsx => PreviewSurvey.jsx} | 19 +++-- src/components/survey/RecentSurveys.jsx | 11 ++- src/components/survey/SurveyList.jsx | 4 +- src/components/survey/TopSurveys.jsx | 9 ++- src/components/survey/dummy.js | 2 + src/components/utility/Navbar.jsx | 4 +- src/styles/survey/ListSurveys.css | 78 ++++++++++++++++++ src/styles/survey/PreviewChoices.css | 0 src/styles/survey/PreviewSurvey.css | 79 +++++++++++++++++++ 11 files changed, 209 insertions(+), 29 deletions(-) create mode 100644 src/components/survey/PreviewChoices.jsx rename src/components/survey/{PreviewThread.jsx => PreviewSurvey.jsx} (54%) create mode 100644 src/styles/survey/ListSurveys.css create mode 100644 src/styles/survey/PreviewChoices.css create mode 100644 src/styles/survey/PreviewSurvey.css diff --git a/src/components/survey/ListSurveys.jsx b/src/components/survey/ListSurveys.jsx index 426435d..d55b144 100644 --- a/src/components/survey/ListSurveys.jsx +++ b/src/components/survey/ListSurveys.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import '../../styles/thread/ListThreads.css'; +import '../../styles/survey/ListSurveys.css'; import Button from '../utility/Button'; import TopSurveys from './TopSurveys'; import RecentSurveys from './RecentSurveys'; @@ -8,26 +8,26 @@ import { Link } from 'react-router-dom'; export default function ListSurveys(props) { return ( - <div className="listThreadsContainer"> + <div className="listSurveysContainer"> <div className="header"> <h1> - <b>Threads</b> + <b>Surveys</b> </h1> </div> {props.isRecent ? ( - <div className="listThreadsSection"> - <div className="subHeaderListThreads"> + <div className="listSurveysSection"> + <div className="subHeaderListSurveys"> <div className="tab"> - <Link to="/page/1" style={{ textDecoration: 'none' }}> + <Link to="surveys/page/1" style={{ textDecoration: 'none' }}> <h3 className="active">Recent</h3> </Link> - <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <Link to="surveys/top/page/1" style={{ textDecoration: 'none' }}> <h3>Top</h3> </Link> </div> {loggedIn && ( - <Button text="Create Thread" color="orange" url="create/thread" /> + <Button text="Create Surveys" color="orange" url="create/survey" /> )} </div> <RecentSurveys @@ -36,8 +36,8 @@ export default function ListSurveys(props) { /> </div> ) : ( - <div className="listThreadsSection"> - <div className="subHeaderListThreads"> + <div className="listSurveysSection"> + <div className="subHeaderListSurveys"> <div className="tab"> <Link to="/page/1" style={{ textDecoration: 'none' }}> <h3>Recent</h3> @@ -47,7 +47,7 @@ export default function ListSurveys(props) { </Link> </div> {loggedIn && ( - <Button text="Create Thread" color="orange" url="create/thread" /> + <Button text="Create Surveys" color="orange" url="create/survey" /> )} </div> <TopSurveys pageNumber={props.pageNumber} history={props.history} /> diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx new file mode 100644 index 0000000..c297330 --- /dev/null +++ b/src/components/survey/PreviewChoices.jsx @@ -0,0 +1,10 @@ +import '../../styles/survey/PreviewChoices.css'; + +export default function PreviewChoices({ choices, endTime }) { + + return ( + <div className=""> + Component + </div> + ) +} diff --git a/src/components/survey/PreviewThread.jsx b/src/components/survey/PreviewSurvey.jsx similarity index 54% rename from src/components/survey/PreviewThread.jsx rename to src/components/survey/PreviewSurvey.jsx index 7891c1a..bc430c8 100644 --- a/src/components/survey/PreviewThread.jsx +++ b/src/components/survey/PreviewSurvey.jsx @@ -1,19 +1,24 @@ -import '../../styles/thread/PreviewThread.css'; +import '../../styles/survey/PreviewSurvey.css'; import { translate } from '../../helpers/time-util'; import { Link } from 'react-router-dom'; +import PreviewChoices from './PreviewChoices'; -export default function PreviewThread(props) { +export default function PreviewSurvey(props) { const { content } = props; const time = translate(content.inserted_at); return ( - <div className="threadCard"> - <div className="threadCardHeader"> - <h2 className="previewThreadTitle"> + <div className="surveyCard"> + <div className="surveyCardHeader"> + <h2 className="previewSurveyTitle"> <b>{content.title}</b> </h2> </div> - <p className="previewThreadTopic">{content.topic_name}</p> - <div className="threadCardContent"> + <PreviewChoices + choices={content.choices} + endTime={content.endTime} + /> + <p className="previewSurveyTopic">{content.topic_name}</p> + <div className="surveyCardContent"> <p> By{' '} <Link to={`/profile/${content.username}/1`}>{content.username}</Link>{' '} diff --git a/src/components/survey/RecentSurveys.jsx b/src/components/survey/RecentSurveys.jsx index 4db3329..277b53d 100644 --- a/src/components/survey/RecentSurveys.jsx +++ b/src/components/survey/RecentSurveys.jsx @@ -4,6 +4,7 @@ import axios from 'axios'; import SurveyList from './SurveyList'; import { API_URL } from '../../config/keys'; import Pagination from '../thread/Pagination'; +import DUMMY_SURVEYS from './dummy'; export default function RecentSurveys(props) { const [threads, setThreads] = useState([]); @@ -21,8 +22,10 @@ export default function RecentSurveys(props) { `${API_URL}/threads/pages/recent/?page=${currentPage}` ); const { data } = responseThreads; - setThreads(data?.data); - setTotalItems(data?.metadata?.total_data); + console.log(data) + + setThreads(DUMMY_SURVEYS); + setTotalItems(DUMMY_SURVEYS.length); }; fetch(); }, [currentPage]); @@ -33,9 +36,9 @@ export default function RecentSurveys(props) { } return ( - <div className="recentThreads"> + <div className="recentSurveys"> {totalItems == 0 ? ( - <div className="noThreadsMessage"> + <div className="noSurveysMessage"> <p>There is no threads yet.</p> </div> ) : ( diff --git a/src/components/survey/SurveyList.jsx b/src/components/survey/SurveyList.jsx index 00cb2e2..436c587 100644 --- a/src/components/survey/SurveyList.jsx +++ b/src/components/survey/SurveyList.jsx @@ -1,4 +1,4 @@ -import PreviewThread from './PreviewThread'; +import PreviewSurvey from './PreviewSurvey'; import { Link } from 'react-router-dom'; export default function SurveyList(props) { @@ -10,7 +10,7 @@ export default function SurveyList(props) { to={`/topic/${value.topic_name}/${value.id}/page/1`} style={{ textDecoration: 'none' }} > - <PreviewThread content={value} /> + <PreviewSurvey content={value} /> </Link> ))} </div> diff --git a/src/components/survey/TopSurveys.jsx b/src/components/survey/TopSurveys.jsx index b45b91d..b5bab50 100644 --- a/src/components/survey/TopSurveys.jsx +++ b/src/components/survey/TopSurveys.jsx @@ -3,7 +3,8 @@ import React, { useEffect, useState, Fragment } from 'react'; import axios from 'axios'; import SurveyList from './SurveyList'; import { API_URL } from '../../config/keys'; -import Pagination from './Pagination'; +import Pagination from '../thread/Pagination'; +import DUMMY_SURVEYS from './dummy'; export default function TopSurveys(props) { const [threads, setThreads] = useState([]); @@ -20,8 +21,10 @@ export default function TopSurveys(props) { `${API_URL}/threads/pages/top/?page=${currentPage}` ); const { data } = responseThreads; - setThreads(data?.data); - setTotalItems(data?.metadata?.total_data); + console.log(data) + + setThreads(DUMMY_SURVEYS); + setTotalItems(DUMMY_SURVEYS.length); }; fetch(); }, [currentPage]); diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js index 425c406..f1ecba9 100644 --- a/src/components/survey/dummy.js +++ b/src/components/survey/dummy.js @@ -13,6 +13,7 @@ const DUMMY_SURVEYS = [ "choice": "Famine" }, ], + "end_time": "2021-11-30T12:34:22", "inserted_at": "2021-11-26T12:34:22", "updated_at": "2021-11-26T12:34:22", "points": 0, @@ -41,6 +42,7 @@ const DUMMY_SURVEYS = [ "choice": "OCaml" }, ], + "end_time": "2021-11-30T12:34:22", "inserted_at": "2021-11-26T12:34:22", "updated_at": "2021-11-26T12:34:22", "points": 0, diff --git a/src/components/utility/Navbar.jsx b/src/components/utility/Navbar.jsx index 3d0a64b..8971267 100644 --- a/src/components/utility/Navbar.jsx +++ b/src/components/utility/Navbar.jsx @@ -62,7 +62,7 @@ const Navbar = (props) => { exact activeClassName="navbar--active" className="nav-link" - to="/page/1" + to="/threads/page/1" > <b>Threads</b> </NavLink> @@ -72,7 +72,7 @@ const Navbar = (props) => { exact activeClassName="navbar--active" className="nav-link" - to="survey/page/1" + to="/surveys" > <b>Surveys</b> </NavLink> diff --git a/src/styles/survey/ListSurveys.css b/src/styles/survey/ListSurveys.css new file mode 100644 index 0000000..ad41acb --- /dev/null +++ b/src/styles/survey/ListSurveys.css @@ -0,0 +1,78 @@ +.listSurveysSection { + display: flex; + flex-direction: column; + justify-content: center; + margin-top: 56px; +} + +.subHeaderListSurveys { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 28px; +} + +.tab { + display: flex; + align-items: center; +} + +.tab h3 { + font-family: "DM Sans"; + font-size: 18px; + color: #000000; + margin-right: 24px; + margin-top: 4px; + opacity: 0.5; +} + +.tab .active { + color: #DE6600 !important; + font-weight: bold; + opacity: 1!important; +} + +.listSurveysContainer { + margin-top: 36px; + margin-left: 72px; + margin-right: 72px; + min-height: 100vh; + margin-bottom: 72px; +} + +@media only screen and (min-device-width : 320px) and (max-device-width : 540px) { + .listSurveysContainer { + margin-top: 24px; + margin-left: 36px; + margin-right: 36px; + } + + .listSurveysSection { + margin-top: 36px; + } + + .subHeaderListSurveys { + margin-bottom: 28px; + flex-wrap: wrap; + justify-content: center; + flex-direction: column; + justify-content: center; + } + + .tab .active { + color: #DE6600 !important; + font-weight: bold; + opacity: 1!important; + } + + .tab h3 { + font-size: 16px; + margin-right: 28px; + margin-left: 28px; + margin-top: 4px; + } + + .noSurveysMessage p{ + text-align: center; + } +} diff --git a/src/styles/survey/PreviewChoices.css b/src/styles/survey/PreviewChoices.css new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/survey/PreviewSurvey.css b/src/styles/survey/PreviewSurvey.css new file mode 100644 index 0000000..8986fb5 --- /dev/null +++ b/src/styles/survey/PreviewSurvey.css @@ -0,0 +1,79 @@ +.surveyCard { + min-width: 303px; + max-width: 3696px; + margin-bottom: 20px; + padding:20px; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); + border-radius: 8px; + font-family: "DM Sans"; +} + +.previewSurveyTopic { + background: #DE6600; + color:#ffffff; + padding: 4px; + border-radius: 4px; + width: max-content; + margin: 24px 0px 0px 0px; + border-radius: 4px; + font-size: 14px; +} + +.newSurveyButton { + margin-top: 20px; + margin-left: 20px; +} + +.fa-thumbs-up { + height: 16px; + color: #DE6600; +} + +.previewSurveyTitle { + font-size: 24px; + color: #000000; +} + +.surveyCardContent { + margin-top: 8px; +} + +.surveyCardContent p{ + margin-bottom: 0px; + color: #000000; +} + +@media only screen and (min-device-width : 320px) and (max-device-width : 540px) { + .surveyCard { + min-width: 248px; + max-width: 468px; + margin-bottom: 20px; + padding: 16px; + } + + .previewSurveyTopic { + font-size: 14px; + } + + .newSurveyButton { + margin-top: 20px; + margin-left: 20px; + } + + .previewSurveyTitle { + font-size: 24px; + color: #000000; + } + + .surveyCardContent { + margin-top: 8px; + } + + .surveyCardContent p{ + margin-bottom: 0px; + } + + .surveyCardContent p{ + text-align: left; + } +} \ No newline at end of file -- GitLab From affaf5df8cc656cb126f608d51a6486096cd2684 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 14:50:42 +0700 Subject: [PATCH 07/19] build(deps): install moment-countdown --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 12f9fb0..527dae6 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "axios": "^0.21.1", "http-proxy-middleware": "^1.0.6", "moment": "^2.29.1", + "moment-countdown": "^0.0.3", "nodemon": "^2.0.7", "prettier": "^2.2.1", "react": "^17.0.1", -- GitLab From 068b77b650205285a827b5df53a1ef0df66d4f11 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 29 Nov 2021 16:19:24 +0700 Subject: [PATCH 08/19] feat(list-survey): working on countdown datetime - build: uninstall moment-countdown - utils: separate timestamp & html datetime conversion to UTC format --- package.json | 1 - src/components/survey/PreviewChoices.jsx | 9 ++++++++- src/components/survey/PreviewSurvey.jsx | 2 +- src/components/survey/dummy.js | 2 +- src/helpers/time-util.js | 7 ++++++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 527dae6..12f9fb0 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "axios": "^0.21.1", "http-proxy-middleware": "^1.0.6", "moment": "^2.29.1", - "moment-countdown": "^0.0.3", "nodemon": "^2.0.7", "prettier": "^2.2.1", "react": "^17.0.1", diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index c297330..1c302c3 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -1,10 +1,17 @@ import '../../styles/survey/PreviewChoices.css'; +import moment from 'moment'; +import { timestampToUTC } from '../../helpers/time-util'; export default function PreviewChoices({ choices, endTime }) { + const now = moment(timestampToUTC(new Date())) + const end = moment(new Date(endTime)) + const duration = moment.duration(end.diff(now)) + + console.log(end.fromNow()) return ( <div className=""> - Component + Poll ends in 2 days and 15 hours </div> ) } diff --git a/src/components/survey/PreviewSurvey.jsx b/src/components/survey/PreviewSurvey.jsx index bc430c8..d068636 100644 --- a/src/components/survey/PreviewSurvey.jsx +++ b/src/components/survey/PreviewSurvey.jsx @@ -15,7 +15,7 @@ export default function PreviewSurvey(props) { </div> <PreviewChoices choices={content.choices} - endTime={content.endTime} + endTime={content.end_time} /> <p className="previewSurveyTopic">{content.topic_name}</p> <div className="surveyCardContent"> diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js index f1ecba9..a325c19 100644 --- a/src/components/survey/dummy.js +++ b/src/components/survey/dummy.js @@ -42,7 +42,7 @@ const DUMMY_SURVEYS = [ "choice": "OCaml" }, ], - "end_time": "2021-11-30T12:34:22", + "end_time": "2021-12-31T12:34:22", "inserted_at": "2021-11-26T12:34:22", "updated_at": "2021-11-26T12:34:22", "points": 0, diff --git a/src/helpers/time-util.js b/src/helpers/time-util.js index ca432dc..a6e0f0e 100644 --- a/src/helpers/time-util.js +++ b/src/helpers/time-util.js @@ -15,4 +15,9 @@ const datetimeToUTC = (endDate, endTime) => { return date.toISOString(); } -export { translate, datetimeToUTC}; +const timestampToUTC = timestamp => { + const date = new Date(timestamp); + return date.toISOString(); +} + +export { translate, datetimeToUTC, timestampToUTC }; -- GitLab From de0b05270a14d7f447c83499a0ebecb2848ba8a2 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Sat, 11 Dec 2021 14:38:27 +0700 Subject: [PATCH 09/19] feat: add method on displaying poll end countdown --- package.json | 1 + src/components/survey/PreviewChoices.jsx | 11 ++++------- src/helpers/time-util.js | 12 ++++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 12f9fb0..f70128a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "axios": "^0.21.1", "http-proxy-middleware": "^1.0.6", "moment": "^2.29.1", + "moment-duration-format": "^2.3.2", "nodemon": "^2.0.7", "prettier": "^2.2.1", "react": "^17.0.1", diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index 1c302c3..6294ced 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -1,17 +1,14 @@ import '../../styles/survey/PreviewChoices.css'; -import moment from 'moment'; -import { timestampToUTC } from '../../helpers/time-util'; +import { getCountdown } from '../../helpers/time-util'; + export default function PreviewChoices({ choices, endTime }) { - const now = moment(timestampToUTC(new Date())) - const end = moment(new Date(endTime)) - const duration = moment.duration(end.diff(now)) - console.log(end.fromNow()) + const countdown = getCountdown(endTime) return ( <div className=""> - Poll ends in 2 days and 15 hours + Poll ends in { countdown } </div> ) } diff --git a/src/helpers/time-util.js b/src/helpers/time-util.js index a6e0f0e..1345273 100644 --- a/src/helpers/time-util.js +++ b/src/helpers/time-util.js @@ -1,3 +1,4 @@ +import 'moment-duration-format'; import moment from 'moment'; function translate(time) { @@ -15,9 +16,12 @@ const datetimeToUTC = (endDate, endTime) => { return date.toISOString(); } -const timestampToUTC = timestamp => { - const date = new Date(timestamp); - return date.toISOString(); +const getCountdown = endTime => { + const now = moment() + const end = moment(endTime).local() + const duration = moment.duration(end.diff(now)) + + return duration.format("D [day] H [hour] M [min") } -export { translate, datetimeToUTC, timestampToUTC }; +export { translate, datetimeToUTC, getCountdown }; -- GitLab From 35152dec9229dfb134e9790ecb981a5516819664 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 10:16:25 +0700 Subject: [PATCH 10/19] feat(detail-survey): initialize components --- src/components/survey/Survey.jsx | 158 +++++++++++++++++++++++++++++++ src/routes/App.jsx | 6 ++ src/styles/survey/Survey.css | 116 +++++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 src/components/survey/Survey.jsx create mode 100644 src/styles/survey/Survey.css diff --git a/src/components/survey/Survey.jsx b/src/components/survey/Survey.jsx new file mode 100644 index 0000000..6760c91 --- /dev/null +++ b/src/components/survey/Survey.jsx @@ -0,0 +1,158 @@ +/* eslint-disable eqeqeq */ +import React, { useState, useEffect, useCallback } from 'react'; +import '../../styles/survey/Survey.css'; +import CommentList from '../thread/CommentList'; +import Post from '../thread/Post'; +import { useInput } from '../../helpers/hooks/input-hook'; +import axios from 'axios'; +import AuthService, { + authHeader, + loggedIn, +} from '../../helpers/services/auth.service'; +import { API_URL } from '../../config/keys'; +import Pagination from '../thread/Pagination'; + +export default function Survey(props) { + const currentUserId = AuthService.getCurrentUserId(); + const redirect = (url) => { + props.history.push(url); + }; + + const [survey, setSurvey] = useState({ + content: '', + id: '', + points: 0, + title: '', + topic_id: '', + user_id: '', + updated_at: '', + inserted_at: '', + username: '', + }); + + const [comment, setComment] = useState([ + { + id: '', + message: '', + points: 0, + survey_id: '', + user_id: '', + updated_at: '', + inserted_at: '', + username: '', + }, + ]); + + const { params } = props.match; + const topicParm = params.topic; + const surveyParm = params.survey; + const currentPage = params.pageNumber; + + const { value: input, bind: bindInput, reset: resetInput } = useInput(''); + const [totalComments, setTotalComments] = useState(0); + + const refreshComment = useCallback(() => { + const fetch = async () => { + const responseComment = await axios.get( + `${API_URL}/post/pages/survey/${surveyParm}/?page=${currentPage}` + ); + const { data } = responseComment; + setComment(data?.data); + setTotalComments(data?.metadata?.total_data); + }; + fetch(); + }, [surveyParm, currentPage]); + + useEffect(() => { + const fetch = async () => { + const responseSurvey = await axios.get( + `${API_URL}/surveys/${surveyParm}` + ); + setSurvey(responseSurvey?.data?.data); + refreshComment(); + }; + fetch(); + }, [surveyParm, refreshComment, currentUserId, survey]); + + const handleSubmit = async (event) => { + event.preventDefault(); + + try { + await axios.post( + `${API_URL}/post`, + { + post: { + message: input, + points: 0, + survey_id: surveyParm, + user_id: localStorage.getItem('id'), + }, + }, + { + headers: authHeader(), + } + ); + refreshComment(); + } catch (error) {} + resetInput(); + }; + + function switchPage(pageNumber) { + props.history.push(`/topic/${topicParm}/${surveyParm}/page/${pageNumber}`); + window.location.reload(); + } + + return ( + <div className="surveyContainer"> + <div className="survey_section"> + <Post type="survey" content={survey} redirect={redirect} /> + </div> + + {loggedIn && ( + <div className="addCommentSection"> + <h3>Write a Comment</h3> + <form onSubmit={handleSubmit}> + <div className="commentFormContainer"> + <textarea + className="commentBox" + placeholder="Write your comment here" + required="false" + {...bindInput} + /> + <div> + <button + className="submitComment addCommentButton" + type="submit" + > + Post Comment + </button> + </div> + </div> + </form> + </div> + )} + <h2 className="commentText">Comments</h2> + + {totalComments == 0 ? ( + <div className="noCommentLabel"> + <p>There are no comments yet.</p> + </div> + ) : ( + <div> + <CommentList + comment={comment} + survey_className={surveyParm} + topic_className={topicParm} + /> + <div className="paginationContainer"> + <Pagination + activePage={currentPage} + totalItems={totalComments} + switchPage={switchPage} + /> + </div> + </div> + )} + </div> + ); +} diff --git a/src/routes/App.jsx b/src/routes/App.jsx index e15902a..905f374 100644 --- a/src/routes/App.jsx +++ b/src/routes/App.jsx @@ -21,6 +21,7 @@ import { Switch, } from 'react-router-dom'; import ListSurveys from '../components/survey/ListSurveys'; +import Survey from '../components/survey/Survey'; function App() { return ( @@ -61,6 +62,11 @@ function App() { path="/topic/:topic/:thread/page/:pageNumber" component={Thread} /> + <Route + exact + path="/topic/:topic/:survey/page/:pageNumber" + component={Survey} + /> <Route exact path="/topic/:topic/page/:pageNumber" component={Topic} /> <Route exact path="/threads" component={ListThreads} /> <Route exact path="/surveys" component={ListSurveys} /> diff --git a/src/styles/survey/Survey.css b/src/styles/survey/Survey.css new file mode 100644 index 0000000..058ae95 --- /dev/null +++ b/src/styles/survey/Survey.css @@ -0,0 +1,116 @@ +.surveyContainer { + display: flex; + flex-direction: column; + margin-top: 36px; + font-family: "DM Sans"; + margin-left: 72px; + margin-right: 72px; + min-height: 100vh; + margin-bottom: 72px; +} + +.commentText { + color: #FEA02F; + font-size: 24px; + font-weight: bold; + margin-bottom: 12px; +} + +.addCommentSection { + margin-top: 40px; + display: flex; + flex-direction: column; + margin-top: 20px; +} + +.addCommentSection h3 { + color: #FEA02F; + font-size: 24px; + font-weight: bold; + margin-bottom: 12px; +} + +.addCommentButton { + margin-top: 20px; + margin-left: auto; + margin-right: auto; + margin-bottom: 40px; +} + +.commentBox { + min-width: 468px; + max-width: 3696px; + font-family: "DM Sans"; + height: 120px; + padding: 12px 16px; + border-radius: 8px; + border: 0; + box-shadow: 0px 0px 8px 1px rgba(0,0,0,0.25); +} + +.submitComment { + border-radius: 4px; + border-width: 0px; + padding: 8px; + font-family: "DM Sans"; + color: #ffffff; + border-style: none; + background-color: #DE6600; + border-color: #DE6600; +} + +.noCommentLabel p { + font-size: 16px; + margin-bottom: 0px; +} + +.commentFormContainer { + display: flex; + flex-direction: column; +} + +@media only screen and (min-device-width : 320px) and (max-device-width : 540px) { + .surveyContainer { + margin-top: 24px; + margin-bottom: 24px; + margin-left: 36px; + margin-right: 36px; + } + + .addCommentSection h3 { + font-size: 18px; + text-align: center; + } + + .addCommentSection div { + display: flex; + justify-content: center; + } + + .commentBox { + min-width: 248px; + max-width: 468px; + height: 120px; + font-size: 14px; + } + + .submitComment { + font-size: 16px; + } + + .commentText { + font-size: 18px; + text-align: center; + } + + .noCommentLabel p { + font-size: 14px; + margin-bottom: 0px; + text-align: center; + } + + .buttonContainer .button { + margin-top: 0px; + } +} + -- GitLab From ac4b2f0a1ebc4e4b0ab3b280a425518e930970b3 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 11:32:29 +0700 Subject: [PATCH 11/19] feat(list-survey): add UI --- src/components/survey/ChoiceBox.jsx | 33 ++++++++++++++++++ src/components/survey/PreviewChoices.jsx | 37 +++++++++++++++++--- src/components/survey/PreviewSurvey.jsx | 24 ++++++++----- src/components/survey/dummy.js | 26 +++++++++----- src/helpers/time-util.js | 2 +- src/styles/survey/ChoiceBox.css | 43 ++++++++++++++++++++++++ src/styles/survey/PreviewChoices.css | 13 +++++++ 7 files changed, 157 insertions(+), 21 deletions(-) create mode 100644 src/components/survey/ChoiceBox.jsx create mode 100644 src/styles/survey/ChoiceBox.css diff --git a/src/components/survey/ChoiceBox.jsx b/src/components/survey/ChoiceBox.jsx new file mode 100644 index 0000000..5c379f1 --- /dev/null +++ b/src/components/survey/ChoiceBox.jsx @@ -0,0 +1,33 @@ +import '../../styles/survey/ChoiceBox.css'; + +export const ChoiceBoxVoted = ({ choice }) => { + const { name, votes } = choice; + const isUserVoted = Math.random() < 0.5; + + return ( + <div + className="choice-box__voted" + style={{ + background: isUserVoted + ? '#FEA02F' + : '#FFFFFF', + color: isUserVoted + ? '#FFFFFF' + : '#000000', + }} + > + <h6 className="name">{name}</h6> + <h6 className="votes">{votes}</h6> + </div> + ); +}; + +export const ChoiceBoxUnvoted = ({ choice }) => { + const { name } = choice; + + return ( + <div className="choice-box__unvoted"> + <h6 className="name">{name}</h6> + </div> + ); +}; diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index 6294ced..0f1601b 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -1,14 +1,43 @@ import '../../styles/survey/PreviewChoices.css'; import { getCountdown } from '../../helpers/time-util'; +import { ChoiceBoxUnvoted, ChoiceBoxVoted } from './ChoiceBox'; export default function PreviewChoices({ choices, endTime }) { - - const countdown = getCountdown(endTime) + const COUNTDOWN_STRING = { + withCountdown: (countdown) => `Poll ends in ${countdown}`, + withoutCountdown: 'Poll has ended' + }; + const isOverEndTime = new Date() > new Date(endTime); + const isUserHasVoted = Math.random() < 0.5; + const countdown = getCountdown(endTime); return ( - <div className=""> - Poll ends in { countdown } + <div className="preview-choices__container"> + <div className="countdown"> + { + isOverEndTime + ? COUNTDOWN_STRING.withoutCountdown + : COUNTDOWN_STRING.withCountdown(countdown) + } + </div> + <div className="choiceboxes"> + {choices.map((choice, idx) => ( + <> + {isUserHasVoted ? ( + <ChoiceBoxVoted + choice={choice} + key={idx} + /> + ) : ( + <ChoiceBoxUnvoted + choice={choice} + key={idx} + /> + )} + </> + ))} + </div> </div> ) } diff --git a/src/components/survey/PreviewSurvey.jsx b/src/components/survey/PreviewSurvey.jsx index d068636..74ba5e8 100644 --- a/src/components/survey/PreviewSurvey.jsx +++ b/src/components/survey/PreviewSurvey.jsx @@ -3,30 +3,38 @@ import { translate } from '../../helpers/time-util'; import { Link } from 'react-router-dom'; import PreviewChoices from './PreviewChoices'; -export default function PreviewSurvey(props) { - const { content } = props; +export default function PreviewSurvey({ content }) { + const { + title, + choices, + end_time, + topic_name, + username, + points, + } = content; const time = translate(content.inserted_at); + return ( <div className="surveyCard"> <div className="surveyCardHeader"> <h2 className="previewSurveyTitle"> - <b>{content.title}</b> + <b>{title}</b> </h2> </div> <PreviewChoices - choices={content.choices} - endTime={content.end_time} + choices={choices} + endTime={end_time} /> - <p className="previewSurveyTopic">{content.topic_name}</p> + <p className="previewSurveyTopic">{topic_name}</p> <div className="surveyCardContent"> <p> By{' '} - <Link to={`/profile/${content.username}/1`}>{content.username}</Link>{' '} + <Link to={`/profile/${username}/1`}>{username}</Link>{' '} {', '} {window.innerWidth < 780 && ( <br></br> )} - {time} - <i className="far fa-thumbs-up" /> {content.points} + {time} - <i className="far fa-thumbs-up" /> {points} </p> </div> </div> diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js index a325c19..0156292 100644 --- a/src/components/survey/dummy.js +++ b/src/components/survey/dummy.js @@ -2,15 +2,19 @@ const DUMMY_SURVEYS = [ { "id": 251, "title": "You snap your finger and one of the following are erased for ever, what do you choose?", + "is_user_has_voted": true, "choices": [ { - "choice": "World Hunger" + "name": "World Hunger", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "Poverty" + "name": "Poverty", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "Famine" + "name": "Famine", + "votes": Math.floor(Math.random() * 1000) + 1 }, ], "end_time": "2021-11-30T12:34:22", @@ -25,21 +29,27 @@ const DUMMY_SURVEYS = [ { "id": 252, "title": "Kamu suka pakai bahasa pemrograman fungsional yang mana?", + "is_user_has_voted": false, "choices": [ { - "choice": "Scala" + "name": "Scala", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "Haskell" + "name": "Haskell", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "Mercury" + "name": "Mercury", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "Clojure" + "name": "Clojure", + "votes": Math.floor(Math.random() * 1000) + 1 }, { - "choice": "OCaml" + "name": "OCaml", + "votes": Math.floor(Math.random() * 1000) + 1 }, ], "end_time": "2021-12-31T12:34:22", diff --git a/src/helpers/time-util.js b/src/helpers/time-util.js index 1345273..02e1a58 100644 --- a/src/helpers/time-util.js +++ b/src/helpers/time-util.js @@ -21,7 +21,7 @@ const getCountdown = endTime => { const end = moment(endTime).local() const duration = moment.duration(end.diff(now)) - return duration.format("D [day] H [hour] M [min") + return duration.format("D [day] H [hour] M [min]") } export { translate, datetimeToUTC, getCountdown }; diff --git a/src/styles/survey/ChoiceBox.css b/src/styles/survey/ChoiceBox.css new file mode 100644 index 0000000..67f2e1b --- /dev/null +++ b/src/styles/survey/ChoiceBox.css @@ -0,0 +1,43 @@ +.choice-box__voted, +.choice-box__unvoted { + display: flex; + align-items: center; + margin: 8px 0px; + padding: 8px; + cursor: default; + + background: #FFFFFF; + border: 2px solid #FEA02F; + box-sizing: border-box; + border-radius: 25px; +} + +.choice-box__voted h6, +.choice-box__unvoted h6 { + cursor: text; + font-family: DM Sans; + font-style: normal; + font-weight: bold; + font-size: 0.75rem; + padding-bottom: 0px; + margin-bottom: 0px; +} + +.choice-box__unvoted:hover { + background: #FEA02F; +} + +.choice-box__unvoted:hover h6 { + color: #FFFFFF; +} + +.choice-box__voted { + justify-content: space-between; + padding-left: 12px; + padding-right: 12px; +} + +.choice-box__unvoted { + justify-content: center; + color: #000000 +} diff --git a/src/styles/survey/PreviewChoices.css b/src/styles/survey/PreviewChoices.css index e69de29..81c544a 100644 --- a/src/styles/survey/PreviewChoices.css +++ b/src/styles/survey/PreviewChoices.css @@ -0,0 +1,13 @@ +.preview-choices__container { + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.5); + border-radius: 8px; + padding: 0.75rem 1rem; + margin: 1.5rem 0px 1rem; + cursor: pointer; +} + +.preview-choices__container .countdown { + color: rgba(0, 0, 0, 0.5); + font-weight: bold; + font-size: 0.875rem; +} \ No newline at end of file -- GitLab From d26cbb762f4007844c98c7dfc4ab71dab3812130 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 12:27:44 +0700 Subject: [PATCH 12/19] feat(list-survey): add minor improvement to UI --- src/components/survey/PreviewChoices.jsx | 7 ++++++- src/components/survey/dummy.js | 2 +- src/helpers/time-util.js | 12 +++++++++++- src/styles/survey/ChoiceBox.css | 3 --- src/styles/survey/PreviewChoices.css | 12 ++++++++++-- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index 0f1601b..f2f771a 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -11,6 +11,7 @@ export default function PreviewChoices({ choices, endTime }) { const isOverEndTime = new Date() > new Date(endTime); const isUserHasVoted = Math.random() < 0.5; const countdown = getCountdown(endTime); + const numOfLeftoverChoices = choices.length - 3; return ( <div className="preview-choices__container"> @@ -22,7 +23,7 @@ export default function PreviewChoices({ choices, endTime }) { } </div> <div className="choiceboxes"> - {choices.map((choice, idx) => ( + {choices.slice(0, 3).map((choice, idx) => ( <> {isUserHasVoted ? ( <ChoiceBoxVoted @@ -38,6 +39,10 @@ export default function PreviewChoices({ choices, endTime }) { </> ))} </div> + <h6 className="choices-left"> + {numOfLeftoverChoices > 0 && `${numOfLeftoverChoices} More Choice`} + {numOfLeftoverChoices > 1 && 's'} + </h6> </div> ) } diff --git a/src/components/survey/dummy.js b/src/components/survey/dummy.js index 0156292..455efa7 100644 --- a/src/components/survey/dummy.js +++ b/src/components/survey/dummy.js @@ -52,7 +52,7 @@ const DUMMY_SURVEYS = [ "votes": Math.floor(Math.random() * 1000) + 1 }, ], - "end_time": "2021-12-31T12:34:22", + "end_time": "2021-12-31T18:34:22", "inserted_at": "2021-11-26T12:34:22", "updated_at": "2021-11-26T12:34:22", "points": 0, diff --git a/src/helpers/time-util.js b/src/helpers/time-util.js index 02e1a58..ba03c53 100644 --- a/src/helpers/time-util.js +++ b/src/helpers/time-util.js @@ -21,7 +21,17 @@ const getCountdown = endTime => { const end = moment(endTime).local() const duration = moment.duration(end.diff(now)) - return duration.format("D [day] H [hour] M [min]") + return ( + duration + .format( + [ + moment.duration(1, "minute"), + moment.duration(1, "hour"), + moment.duration(1, "day") + ], + "d [days], h [hours], m [minutes]" + ) + ); } export { translate, datetimeToUTC, getCountdown }; diff --git a/src/styles/survey/ChoiceBox.css b/src/styles/survey/ChoiceBox.css index 67f2e1b..1e4f451 100644 --- a/src/styles/survey/ChoiceBox.css +++ b/src/styles/survey/ChoiceBox.css @@ -4,7 +4,6 @@ align-items: center; margin: 8px 0px; padding: 8px; - cursor: default; background: #FFFFFF; border: 2px solid #FEA02F; @@ -14,11 +13,9 @@ .choice-box__voted h6, .choice-box__unvoted h6 { - cursor: text; font-family: DM Sans; font-style: normal; font-weight: bold; - font-size: 0.75rem; padding-bottom: 0px; margin-bottom: 0px; } diff --git a/src/styles/survey/PreviewChoices.css b/src/styles/survey/PreviewChoices.css index 81c544a..11c635b 100644 --- a/src/styles/survey/PreviewChoices.css +++ b/src/styles/survey/PreviewChoices.css @@ -9,5 +9,13 @@ .preview-choices__container .countdown { color: rgba(0, 0, 0, 0.5); font-weight: bold; - font-size: 0.875rem; -} \ No newline at end of file + margin-left: calc(1rem - 2px); +} + +.preview-choices__container .choices-left { + color: rgba(0, 0, 0, 0.5); + font-weight: bold; + text-align: center; + margin-top: 0.75rem; + margin-bottom: 0rem; +} -- GitLab From e30a0cdf0a2c6f8da853ff417b5f76766efa705a Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 12:35:54 +0700 Subject: [PATCH 13/19] feat(list-survey): disable hover effect on preview --- src/components/survey/ChoiceBox.jsx | 4 ++-- src/components/survey/PreviewChoices.jsx | 1 + src/styles/survey/ChoiceBox.css | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/survey/ChoiceBox.jsx b/src/components/survey/ChoiceBox.jsx index 5c379f1..3d07fb1 100644 --- a/src/components/survey/ChoiceBox.jsx +++ b/src/components/survey/ChoiceBox.jsx @@ -22,11 +22,11 @@ export const ChoiceBoxVoted = ({ choice }) => { ); }; -export const ChoiceBoxUnvoted = ({ choice }) => { +export const ChoiceBoxUnvoted = ({ choice, isPreview=false }) => { const { name } = choice; return ( - <div className="choice-box__unvoted"> + <div className={`choice-box__unvoted ${isPreview && 'is_preview'}`}> <h6 className="name">{name}</h6> </div> ); diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index f2f771a..f5abf34 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -34,6 +34,7 @@ export default function PreviewChoices({ choices, endTime }) { <ChoiceBoxUnvoted choice={choice} key={idx} + isPreview /> )} </> diff --git a/src/styles/survey/ChoiceBox.css b/src/styles/survey/ChoiceBox.css index 1e4f451..711fe34 100644 --- a/src/styles/survey/ChoiceBox.css +++ b/src/styles/survey/ChoiceBox.css @@ -28,6 +28,14 @@ color: #FFFFFF; } +.is_preview.choice-box__unvoted:hover { + background: #FFFFFF; +} + +.is_preview.choice-box__unvoted:hover h6 { + color: #000000; +} + .choice-box__voted { justify-content: space-between; padding-left: 12px; -- GitLab From 52da864247258ebcc951c025b1ee659024d9c325 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 17:05:07 +0700 Subject: [PATCH 14/19] feat(detail-survey): adjust routing to detail thread & survey --- src/components/survey/SurveyList.jsx | 2 +- src/components/thread/ThreadList.jsx | 2 +- src/routes/App.jsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/survey/SurveyList.jsx b/src/components/survey/SurveyList.jsx index 436c587..88fe5e7 100644 --- a/src/components/survey/SurveyList.jsx +++ b/src/components/survey/SurveyList.jsx @@ -7,7 +7,7 @@ export default function SurveyList(props) { {props.thread.map((value) => ( <Link key={value.id} - to={`/topic/${value.topic_name}/${value.id}/page/1`} + to={`/survey/topic/${value.topic_name}/${value.id}/page/1`} style={{ textDecoration: 'none' }} > <PreviewSurvey content={value} /> diff --git a/src/components/thread/ThreadList.jsx b/src/components/thread/ThreadList.jsx index 574a18c..7d65c15 100644 --- a/src/components/thread/ThreadList.jsx +++ b/src/components/thread/ThreadList.jsx @@ -7,7 +7,7 @@ export default function ThreadList(props) { {props.thread.map((value) => ( <Link key={value.id} - to={`/topic/${value.topic_name}/${value.id}/page/1`} + to={`/thread/topic/${value.topic_name}/${value.id}/page/1`} style={{ textDecoration: 'none' }} > <PreviewThread content={value} /> diff --git a/src/routes/App.jsx b/src/routes/App.jsx index 905f374..71e4933 100644 --- a/src/routes/App.jsx +++ b/src/routes/App.jsx @@ -59,12 +59,12 @@ function App() { <Route exact path="/search/:input/page/:pageNumber" component={Search} /> <Route exact - path="/topic/:topic/:thread/page/:pageNumber" + path="/thread/topic/:topic/:thread/page/:pageNumber" component={Thread} /> <Route exact - path="/topic/:topic/:survey/page/:pageNumber" + path="/survey/topic/:topic/:survey/page/:pageNumber" component={Survey} /> <Route exact path="/topic/:topic/page/:pageNumber" component={Topic} /> -- GitLab From 2a351fb811d4a1fda2e7dda3db3424307474c94f Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 20:40:52 +0700 Subject: [PATCH 15/19] feat(detail-survey): add detail survey UI --- src/components/survey/DetailChoices.jsx | 44 +++++++ src/components/survey/RecentSurveys.jsx | 2 +- src/components/survey/Survey.jsx | 31 +++-- src/components/survey/SurveyPost.jsx | 158 ++++++++++++++++++++++++ src/components/survey/TopSurveys.jsx | 2 +- src/styles/survey/DetailChoices.css | 10 ++ 6 files changed, 229 insertions(+), 18 deletions(-) create mode 100644 src/components/survey/DetailChoices.jsx create mode 100644 src/components/survey/SurveyPost.jsx create mode 100644 src/styles/survey/DetailChoices.css diff --git a/src/components/survey/DetailChoices.jsx b/src/components/survey/DetailChoices.jsx new file mode 100644 index 0000000..170eb0d --- /dev/null +++ b/src/components/survey/DetailChoices.jsx @@ -0,0 +1,44 @@ +import '../../styles/survey/DetailChoices.css'; +import { getCountdown } from '../../helpers/time-util'; +import { ChoiceBoxUnvoted, ChoiceBoxVoted } from './ChoiceBox'; + + +export default function DetailChoices({ choices, endTime }) { + const COUNTDOWN_STRING = { + withCountdown: (countdown) => `Poll ends in ${countdown}`, + withoutCountdown: 'Poll has ended' + }; + const isOverEndTime = new Date() > new Date(endTime); + const isUserHasVoted = Math.random() < 0.5; + const countdown = getCountdown(endTime); + + return ( + <div className="detail-choices__container"> + <div className="countdown"> + { + isOverEndTime + ? COUNTDOWN_STRING.withoutCountdown + : COUNTDOWN_STRING.withCountdown(countdown) + } + </div> + <div className="choiceboxes"> + {choices.map((choice, idx) => ( + <> + {isUserHasVoted ? ( + <ChoiceBoxVoted + choice={choice} + key={idx} + /> + ) : ( + <ChoiceBoxUnvoted + choice={choice} + key={idx} + isPreview + /> + )} + </> + ))} + </div> + </div> + ) +} diff --git a/src/components/survey/RecentSurveys.jsx b/src/components/survey/RecentSurveys.jsx index 277b53d..6b0d481 100644 --- a/src/components/survey/RecentSurveys.jsx +++ b/src/components/survey/RecentSurveys.jsx @@ -39,7 +39,7 @@ export default function RecentSurveys(props) { <div className="recentSurveys"> {totalItems == 0 ? ( <div className="noSurveysMessage"> - <p>There is no threads yet.</p> + <p>There is no surveys yet.</p> </div> ) : ( <Fragment> diff --git a/src/components/survey/Survey.jsx b/src/components/survey/Survey.jsx index 6760c91..5b0a8b4 100644 --- a/src/components/survey/Survey.jsx +++ b/src/components/survey/Survey.jsx @@ -1,16 +1,18 @@ /* eslint-disable eqeqeq */ +import axios from 'axios'; import React, { useState, useEffect, useCallback } from 'react'; + import '../../styles/survey/Survey.css'; import CommentList from '../thread/CommentList'; -import Post from '../thread/Post'; +import SurveyPost from './SurveyPost'; import { useInput } from '../../helpers/hooks/input-hook'; -import axios from 'axios'; import AuthService, { authHeader, loggedIn, } from '../../helpers/services/auth.service'; import { API_URL } from '../../config/keys'; import Pagination from '../thread/Pagination'; +import DUMMY_SURVEYS from './dummy'; export default function Survey(props) { const currentUserId = AuthService.getCurrentUserId(); @@ -18,18 +20,7 @@ export default function Survey(props) { props.history.push(url); }; - const [survey, setSurvey] = useState({ - content: '', - id: '', - points: 0, - title: '', - topic_id: '', - user_id: '', - updated_at: '', - inserted_at: '', - username: '', - }); - + const [survey, setSurvey] = useState(null); const [comment, setComment] = useState([ { id: '', @@ -51,6 +42,10 @@ export default function Survey(props) { const { value: input, bind: bindInput, reset: resetInput } = useInput(''); const [totalComments, setTotalComments] = useState(0); + useEffect(() => { + setSurvey(DUMMY_SURVEYS[1]); + }, []) + const refreshComment = useCallback(() => { const fetch = async () => { const responseComment = await axios.get( @@ -68,7 +63,11 @@ export default function Survey(props) { const responseSurvey = await axios.get( `${API_URL}/surveys/${surveyParm}` ); - setSurvey(responseSurvey?.data?.data); + + const { data } = responseSurvey; + console.log(data.data) + + setSurvey(DUMMY_SURVEYS[0]); refreshComment(); }; fetch(); @@ -105,7 +104,7 @@ export default function Survey(props) { return ( <div className="surveyContainer"> <div className="survey_section"> - <Post type="survey" content={survey} redirect={redirect} /> + {survey && <SurveyPost content={survey} redirect={redirect} />} </div> {loggedIn && ( diff --git a/src/components/survey/SurveyPost.jsx b/src/components/survey/SurveyPost.jsx new file mode 100644 index 0000000..1d33644 --- /dev/null +++ b/src/components/survey/SurveyPost.jsx @@ -0,0 +1,158 @@ +/* eslint-disable eqeqeq */ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; + +import '../../styles/thread/Post.css'; +import AuthService, { + authHeader, + loggedIn, +} from '../../helpers/services/auth.service'; +import { translate } from '../../helpers/time-util'; +import { API_URL } from '../../config/keys'; +import Button from '../utility/Button'; +import DetailChoices from './DetailChoices'; + +export default function SurveyPost({ content }) { + const [points, setPoints] = useState(0); + const [isLiked, setIsLiked] = useState(false); + const [user, setUser] = useState({ + id: '', + name: '', + username: '', + }); + + const deletePost = async () => { + try { + await axios.delete(`${API_URL}/survey/${content.id}`, { + headers: authHeader(), + }); + } catch (error) { + console.log(error); + } + }; + + useEffect(() => { + const fetch = async () => { + try { + setPoints(content.points); + if (content.username != '') { + const getUser = await axios.get( + `${API_URL}/users/name/${content.username}` + ); + setUser(getUser?.data?.data); + } + await axios.get(`${API_URL}/$survey/checklike/${content.id}`, { + headers: authHeader(), + }); + setIsLiked(true); + } catch (error) { + setIsLiked(false); + } + }; + fetch(); + }, [content.id, content.points, content.username]); + + const handleLike = async () => { + try { + await axios.get(`${API_URL}/survey/checklike/${content.id}`, { + headers: authHeader(), + }); + await axios.post( + `${API_URL}/survey/dislike/${content.id}`, + {}, + { + headers: authHeader(), + } + ); + setPoints(points - 1); + setIsLiked(false); + } catch (error) { + await axios.post( + `${API_URL}/survey/like/${content.id}`, + {}, + { + headers: authHeader(), + } + ); + setPoints(points + 1); + setIsLiked(true); + } + }; + + useEffect(() => { + console.log(content) + }, [content]) + + const time = translate(content.inserted_at); + + return ( + <div className="post"> + <div className="postHeader"> + <div className="headerData"> + <div className="userImage"> + {user.picture ? ( + <i className="far fa-user-circle" /> + ) : ( + <img alt="profile" src={user.picture} /> + )} + </div> + <div className="creator"> + <p> + By{' '} + <Link to={`/profile/${content.username}/1`}> + {content.username} + </Link>{', '} + {window.innerWidth < 780 && ( + <br></br> + )} + {time}{' '} + </p> + </div> + </div> + {content.user_id == AuthService.getCurrentUserId() && ( + <div className="headerButton"> + <div className="postButtonContainer"> + <Button + type="button" + text="Edit" + color="none-green" + url={`thread/edit/${content.id}`} + /> + <button + type="button" + className="deleteButton" + onClick={() => deletePost(user.username)} + > + Delete + </button> + </div> + </div> + )} + </div> + <div className="postContent"> + <h1 className="postTitle">{content.title}</h1> + <DetailChoices + choices={content.choices} + endTime={content.end_time} + /> + <div className="likeSection"> + {loggedIn && ( + <button className="likeButton" onClick={handleLike}> + <i + className={`far fa-thumbs-up ${ + isLiked ? 'active' : 'inactive' + }`} + /> + </button> + )} + <div className="pointContainer"> + <div className="point"> + <p>{points} likes</p> + </div> + </div> + </div> + </div> + </div> + ); +} diff --git a/src/components/survey/TopSurveys.jsx b/src/components/survey/TopSurveys.jsx index b5bab50..796a8c0 100644 --- a/src/components/survey/TopSurveys.jsx +++ b/src/components/survey/TopSurveys.jsx @@ -38,7 +38,7 @@ export default function TopSurveys(props) { <div className="topThreads"> {totalItems == 0 ? ( <div className="noThreadsMessage"> - <p>There is no threads yet.</p> + <p>There is no surveys yet.</p> </div> ) : ( <Fragment> diff --git a/src/styles/survey/DetailChoices.css b/src/styles/survey/DetailChoices.css new file mode 100644 index 0000000..61c1848 --- /dev/null +++ b/src/styles/survey/DetailChoices.css @@ -0,0 +1,10 @@ +.detail-choices__container { + border-radius: 8px; + margin: 1.5rem 0px; + cursor: pointer; +} + +.detail-choices__container .countdown { + color: rgba(0, 0, 0, 0.5); + font-weight: bold; +} -- GitLab From 202300163e25f2e9802dbc3704f819b98d59aae6 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Mon, 13 Dec 2021 21:12:45 +0700 Subject: [PATCH 16/19] feat(survey): sort choices by vote count --- src/components/survey/DetailChoices.jsx | 3 ++- src/components/survey/PreviewChoices.jsx | 3 ++- src/helpers/survey-util.js | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 src/helpers/survey-util.js diff --git a/src/components/survey/DetailChoices.jsx b/src/components/survey/DetailChoices.jsx index 170eb0d..dd2d0c0 100644 --- a/src/components/survey/DetailChoices.jsx +++ b/src/components/survey/DetailChoices.jsx @@ -1,6 +1,7 @@ import '../../styles/survey/DetailChoices.css'; import { getCountdown } from '../../helpers/time-util'; import { ChoiceBoxUnvoted, ChoiceBoxVoted } from './ChoiceBox'; +import { sortChoicesByVoteDescending } from '../../helpers/survey-util'; export default function DetailChoices({ choices, endTime }) { @@ -22,7 +23,7 @@ export default function DetailChoices({ choices, endTime }) { } </div> <div className="choiceboxes"> - {choices.map((choice, idx) => ( + {sortChoicesByVoteDescending(choices).map((choice, idx) => ( <> {isUserHasVoted ? ( <ChoiceBoxVoted diff --git a/src/components/survey/PreviewChoices.jsx b/src/components/survey/PreviewChoices.jsx index f5abf34..f403d46 100644 --- a/src/components/survey/PreviewChoices.jsx +++ b/src/components/survey/PreviewChoices.jsx @@ -1,6 +1,7 @@ import '../../styles/survey/PreviewChoices.css'; import { getCountdown } from '../../helpers/time-util'; import { ChoiceBoxUnvoted, ChoiceBoxVoted } from './ChoiceBox'; +import { sortChoicesByVoteDescending } from '../../helpers/survey-util'; export default function PreviewChoices({ choices, endTime }) { @@ -23,7 +24,7 @@ export default function PreviewChoices({ choices, endTime }) { } </div> <div className="choiceboxes"> - {choices.slice(0, 3).map((choice, idx) => ( + {sortChoicesByVoteDescending(choices).slice(0, 3).map((choice, idx) => ( <> {isUserHasVoted ? ( <ChoiceBoxVoted diff --git a/src/helpers/survey-util.js b/src/helpers/survey-util.js new file mode 100644 index 0000000..4b3090d --- /dev/null +++ b/src/helpers/survey-util.js @@ -0,0 +1,3 @@ +export const sortChoicesByVoteDescending = (choices) => ( + choices.sort((a, b) => parseFloat(b.votes) - parseFloat(a.votes)) +); -- GitLab From 819a9e78fb5023b5a8ff0176195e619bdd9468f5 Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Tue, 14 Dec 2021 22:39:26 +0700 Subject: [PATCH 17/19] fix(route): fix url hrefs on list threads & list surveys --- src/components/survey/ListSurveys.jsx | 8 ++++---- src/components/survey/RecentSurveys.jsx | 2 +- src/components/survey/TopSurveys.jsx | 2 +- src/components/thread/ListThreads.jsx | 8 ++++---- src/components/thread/RecentThreads.jsx | 2 +- src/components/thread/TopThreads.jsx | 2 +- src/routes/App.jsx | 22 ++++++++++++++++++++++ 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/components/survey/ListSurveys.jsx b/src/components/survey/ListSurveys.jsx index d55b144..85aea63 100644 --- a/src/components/survey/ListSurveys.jsx +++ b/src/components/survey/ListSurveys.jsx @@ -19,10 +19,10 @@ export default function ListSurveys(props) { <div className="listSurveysSection"> <div className="subHeaderListSurveys"> <div className="tab"> - <Link to="surveys/page/1" style={{ textDecoration: 'none' }}> + <Link to="/surveys/page/1" style={{ textDecoration: 'none' }}> <h3 className="active">Recent</h3> </Link> - <Link to="surveys/top/page/1" style={{ textDecoration: 'none' }}> + <Link to="/surveys/top/page/1" style={{ textDecoration: 'none' }}> <h3>Top</h3> </Link> </div> @@ -39,10 +39,10 @@ export default function ListSurveys(props) { <div className="listSurveysSection"> <div className="subHeaderListSurveys"> <div className="tab"> - <Link to="/page/1" style={{ textDecoration: 'none' }}> + <Link to="/surveys/page/1" style={{ textDecoration: 'none' }}> <h3>Recent</h3> </Link> - <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <Link to="/surveys/top/page/1" style={{ textDecoration: 'none' }}> <h3 className="active">Top</h3> </Link> </div> diff --git a/src/components/survey/RecentSurveys.jsx b/src/components/survey/RecentSurveys.jsx index 6b0d481..a498d50 100644 --- a/src/components/survey/RecentSurveys.jsx +++ b/src/components/survey/RecentSurveys.jsx @@ -31,7 +31,7 @@ export default function RecentSurveys(props) { }, [currentPage]); function switchPage(pageNumber) { - props.history.push(`/page/${pageNumber}`); + props.history.push(`/surveys/page/${pageNumber}`); window.location.reload(); } diff --git a/src/components/survey/TopSurveys.jsx b/src/components/survey/TopSurveys.jsx index 796a8c0..50c9b34 100644 --- a/src/components/survey/TopSurveys.jsx +++ b/src/components/survey/TopSurveys.jsx @@ -30,7 +30,7 @@ export default function TopSurveys(props) { }, [currentPage]); function switchPage(pageNumber) { - props.history.push(`/top/page/${pageNumber}`); + props.history.push(`/surveys/top/page/${pageNumber}`); window.location.reload(); } diff --git a/src/components/thread/ListThreads.jsx b/src/components/thread/ListThreads.jsx index 665c5ef..b2de847 100644 --- a/src/components/thread/ListThreads.jsx +++ b/src/components/thread/ListThreads.jsx @@ -19,10 +19,10 @@ export default function ListThreads(props) { <div className="listThreadsSection"> <div className="subHeaderListThreads"> <div className="tab"> - <Link to="/page/1" style={{ textDecoration: 'none' }}> + <Link to="/threads/page/1" style={{ textDecoration: 'none' }}> <h3 className="active">Recent</h3> </Link> - <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <Link to="/threads/top/page/1" style={{ textDecoration: 'none' }}> <h3>Top</h3> </Link> </div> @@ -39,10 +39,10 @@ export default function ListThreads(props) { <div className="listThreadsSection"> <div className="subHeaderListThreads"> <div className="tab"> - <Link to="/page/1" style={{ textDecoration: 'none' }}> + <Link to="/threads/page/1" style={{ textDecoration: 'none' }}> <h3>Recent</h3> </Link> - <Link to="/top/page/1" style={{ textDecoration: 'none' }}> + <Link to="/threads/top/page/1" style={{ textDecoration: 'none' }}> <h3 className="active">Top</h3> </Link> </div> diff --git a/src/components/thread/RecentThreads.jsx b/src/components/thread/RecentThreads.jsx index ea47f67..b1fa230 100644 --- a/src/components/thread/RecentThreads.jsx +++ b/src/components/thread/RecentThreads.jsx @@ -28,7 +28,7 @@ export default function RecentThreads(props) { }, [currentPage]); function switchPage(pageNumber) { - props.history.push(`/page/${pageNumber}`); + props.history.push(`/threads/page/${pageNumber}`); window.location.reload(); } diff --git a/src/components/thread/TopThreads.jsx b/src/components/thread/TopThreads.jsx index c33b302..e075752 100644 --- a/src/components/thread/TopThreads.jsx +++ b/src/components/thread/TopThreads.jsx @@ -27,7 +27,7 @@ export default function TopThreads(props) { }, [currentPage]); function switchPage(pageNumber) { - props.history.push(`/top/page/${pageNumber}`); + props.history.push(`/threads/top/page/${pageNumber}`); window.location.reload(); } diff --git a/src/routes/App.jsx b/src/routes/App.jsx index 71e4933..1c277d6 100644 --- a/src/routes/App.jsx +++ b/src/routes/App.jsx @@ -50,6 +50,28 @@ function App() { /> )} /> + <Route + exact + path="/surveys/page/:pageNumber" + component={(props) => ( + <ListSurveys + isRecent={true} + pageNumber={props.match.params.pageNumber} + history={props.history} + /> + )} + /> + <Route + exact + path="/surveys/top/page/:pageNumber" + component={(props) => ( + <ListSurveys + isRecent={false} + pageNumber={props.match.params.pageNumber} + history={props.history} + /> + )} + /> <Route exact path="/topic" component={TopicList} /> <Route exact path="/profile/:user/page/:pageNumber" component={Profile} /> <Route exact path="/login" component={LoginForm} /> -- GitLab From 98e83c320e31aa4c2a3773c654f2cca6f5ff2e9a Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Tue, 14 Dec 2021 22:42:24 +0700 Subject: [PATCH 18/19] fix(navbar): change survey nav-item href --- src/components/utility/Navbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/utility/Navbar.jsx b/src/components/utility/Navbar.jsx index 8971267..a295ba2 100644 --- a/src/components/utility/Navbar.jsx +++ b/src/components/utility/Navbar.jsx @@ -72,7 +72,7 @@ const Navbar = (props) => { exact activeClassName="navbar--active" className="nav-link" - to="/surveys" + to="/surveys/page/1" > <b>Surveys</b> </NavLink> -- GitLab From 9112d6f7d73e308296c92825c09646d063597b7b Mon Sep 17 00:00:00 2001 From: Fahdii Ajmalal Fikrie <fahdiaf@gmail.com> Date: Tue, 14 Dec 2021 23:08:52 +0700 Subject: [PATCH 19/19] hotfix(netlify): add _redirects file --- public/_redirects | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/_redirects diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..7797f7c --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 -- GitLab