Fakultas Ilmu Komputer UI

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • mohammad.dwikurnia/diskuy-frontend
  • diskuy-oops/diskuy-frontend
  • jonathan.nicholas/diskuy-frontend
  • functional-programming/diskuy-frontend
4 results
Select Git revision
Loading items
Show changes
Commits on Source (32)
Showing
with 442 additions and 12 deletions
REACT_APP_API_URL={{}}
REACT_APP_FAAS_URL={{}}
REACT_APP_CLIENT_ID={{}}
\ No newline at end of file
# build stage
FROM node:12.13.0-alpine as build-env
ENV NODE_ENV=production
ARG REACT_APP_CLIENT_ID
ARG REACT_APP_FAAS_URL
ENV REACT_APP_CLIENT_ID $REACT_APP_CLIENT_ID
ENV REACT_APP_FAAS_URL $REACT_APP_FAAS_URL
RUN \
apk update && \
......
......@@ -68,7 +68,13 @@ Untuk menjalankan aplikasi pada local server, perlu dilakukan langkah-langkah di
1. Jalankan perintah ``` docker-compose up``` di root project aplikasi ini
## Cara Kontribusi
1. Fork Pada repository ini.
2. Clone pada repository hasil fork pada tahap 1.
3. Set git remote upstream dengan repository original dengan command ```git remote add upstream https://gitlab.cs.ui.ac.id/functional-programming/diskuy-backend.git``` (https) atau ```git remote add upstream git@gitlab.cs.ui.ac.id:functional-programming/diskuy-backend.git``` (ssh)
4. Buatlah branch baru pada repository fork dengan penamaan branch ```feature/nama-feature```
5. Jika fitur tersebut ingin disubmit / merge ke repo upstream, push branch fitur tersebut dan lakukan merge request ke branch master. Set reviewer merge request tersebut dengan owner dari repository ini.
Kelompok 18
......
......@@ -17,6 +17,7 @@
"react-js-pagination": "^3.0.3",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"response": "^0.18.0",
"uuid": "^8.3.2",
"validator": "^13.5.2",
"web-vitals": "^0.2.4"
......
/* /index.html 200
\ No newline at end of file
public/logo192.png

5.22 KiB

public/logo512.png

9.44 KiB

......@@ -23,7 +23,7 @@ export default function LoginForm(props) {
const handleGoogleLoginFailed = () => {};
return (
<div className="loginFormContainer">
<div className="loginFormComponentContainer">
<h1>
<b>Login to discuss anything on Diskuy</b>
</h1>
......
import React, { useState } from 'react';
import PaginationComponent from 'react-js-pagination';
import '../../styles/thread/Pagination.css';
import '../styles/Pagination.css';
export default function Pagination(props) {
const [activePage, setActivePage] = useState(parseInt(props.activePage));
......
......@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
import axios from 'axios';
import ThreadList from './thread/ThreadList';
import { API_URL } from '../config/keys';
import Pagination from './thread/Pagination';
import Pagination from './Pagination';
import '../styles/Search.css';
export default function Search(props) {
......@@ -36,13 +36,15 @@ export default function Search(props) {
}
return (
<div className="searchresultContainer">
<div className="searchResultContainer">
<h1>
<b>Search Results for "{searchParam}"</b>
</h1>
<ThreadList thread={threads} />
{totalItems == 0 && (
<div className="noThreadsMessage">
<p>No threads with keyword "{searchParam}" found.</p>
</div>
)}
{totalItems != 0 && (
<div className="paginationContainer">
......
......@@ -6,7 +6,7 @@ import ThreadList from '../thread/ThreadList';
import axios from 'axios';
import AuthService from '../../helpers/services/auth.service';
import { API_URL } from '../../config/keys';
import Pagination from '../thread/Pagination';
import Pagination from '../Pagination';
export default function Profile(props) {
const { params } = props.match;
......@@ -54,7 +54,7 @@ export default function Profile(props) {
<b>Profile</b>
</h1>
</div>
<div className="profile_section">
<div className="profileSection">
<div className="userIcon">
{user.picture == 'None' ? (
<i className="far fa-user-circle" />
......@@ -62,15 +62,15 @@ export default function Profile(props) {
<img alt="profile pic" src={user.picture} />
)}
</div>
<h2>
<h2 className="usernameProfile">
<b>{user.username}</b>
</h2>
{isUser && (
<Button text="Edit Profile" color="orange" url="profile/update" />
)}
</div>
<div className="my_threads_section">
<div className="sub_header_my_threads">
<div className="myThreadsSection">
<div className="subHeaderMyThreads">
<h3>
<b>{isUser ? 'Your' : user.username}'s threads</b>
</h3>
......
......@@ -44,7 +44,7 @@ export default function FormCreateThread(props) {
};
return (
<div className="formContainer">
<div className="formComponentContainer">
<div className="back" onClick={back}>
<i className="fas fa-angle-left"></i>
<h5>Back</h5>
......@@ -62,7 +62,7 @@ export default function FormCreateThread(props) {
</div>
)}
<form onSubmit={handleSubmit}>
<div className="form_container">
<div className="formContainer">
<label htmlFor="title">Username</label>
<input
type="text"
......
import React, { useState} from 'react';
import { useHistory } from 'react-router';
import '../../styles/thread/Form.css';
export default function CreateNumber(props) {
// const [totalNumber, setTotalNumber] = useState(0);
const [input, setInput] = useState(0);
const [title, setTitle] = useState('');
const history = useHistory();
const onSubmit = (e) => {
e.preventDefault();
history.push('/create/survey?value='+ input + '&title='+title)
}
return (
<div className="formComponentContainer">
<div className="header">
<h1>
<b>Create Survey</b>
</h1>
</div>
<div class = "row">
<form onSubmit={onSubmit}>
<label htmlFor="Enter number of questions in your survey : "></label>
<div class="row">
<h5>Title Survey : </h5>
<input
type="Text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="title"
name="title"
required="true"
/>
</div>
<div class="row">
<h5>Enter number of questions in your survey : </h5>
<input
type="number"
value={input}
onChange={(e) => setInput(e.target.value)}
className="num_quest"
name="num_quest"
required="true"
/>
</div>
<div class ="row">
<button type="submit" className="btn btn-orange">
<span className="button">Next</span>
</button>
</div>
</form>
</div>
<br/>
<div>
<p>*Survey ini menggunakan skala 1-5 dengan</p>
<p>1 = Sangat Tidak Setuju</p>
<p>5 = Sangat Setuju</p>
</div>
</div>
);
}
import '../../styles/thread/Form.css';
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { authHeader } from '../../helpers/services/auth.service';
import { API_URL } from '../../config/keys';
import { useHistory } from 'react-router';
export default function CreateSurvey({ location }) {
const urlParams = new URLSearchParams(location.search);
const keywordParam = urlParams.get('value');
const title = urlParams.get('title');
const [questions, setQuestions] = useState({});
const history = useHistory();
// const ids = [...Array(keywordParam).keys()]
useEffect(() => {
let question = {}
for (let i = 0; i < keywordParam; i++) {
question[i] = {}
}
setQuestions(question)
}, [keywordParam])
const addQuestion = (id, question) => {
let newQuestions = questions;
newQuestions[id]['question'] = question;
setQuestions(newQuestions);
}
const onSubmit = async (event) => {
event.preventDefault();
console.log(questions);
try {
const result = await axios.post(
`${API_URL}/surveys`,
{
survey: {
title: title
}
},
{ headers: authHeader() }
);
const id = result.data.data.id;
Object.entries(questions).forEach(async ([_, question]) => {
let x = {
question: question
};
x['question']['survey_id'] = parseInt(id);
await axios.post(
`${API_URL}/questions`,
x,
{ headers: authHeader() }
);
})
window.alert("Submitted your questions successfully");
history.push('');
} catch (error) {
console.log(error);
}
};
return (
<div className="formComponentContainer">
<div className="header">
<h1>
<b>Create Survey</b>
</h1>
</div>
<form onSubmit={onSubmit}>
<ul>
{[...Array.from(Array(parseInt(keywordParam)).keys())].map((num, i) =>
<li key={i}>
<h5>Questions {i + 1}</h5>
<input
type="text"
className="survey"
name="survey"
placeholder="Your Survey's Question"
required="true"
onInput={(e) =>
addQuestion(i, e.target.value)
}
/>
</li>
)}
</ul>
<button type="submit" className="btn btn-orange">
<span className="button">Create</span>
</button>
</form>
</div>
);
}
import React from 'react';
import '../../styles/survey/ListSurveys.css';
import Button from '../utility/Button';
import RecentSurveys from './RecentSurveys';
import { loggedIn } from '../../helpers/services/auth.service';
export default function ListSurveys(props) {
return (
<div className="listSurveysContainer">
<div className="header">
<h1>
<b>Survey</b>
</h1>
{loggedIn && (
<Button text="Create Survey" color="orange" url="create/number" />
)}
</div>
<div className="listSurveysSection">
<RecentSurveys
pageNumber={props.pageNumber}
history={props.history}
/>
</div>
</div>
);
}
import '../../styles/survey/PreviewSurvey.css';
import { translate } from '../../helpers/time-util';
import { Link } from 'react-router-dom';
import AuthService from '../../helpers/services/auth.service';
export default function PreviewSurvey(props) {
const { content } = props;
const time = translate(content.inserted_at);
return (
<div className="surveyCard">
<div className="surveyCardHeader">
<h2 className="previewSurveyTitle">
<b>{content.title}</b>
</h2>
{AuthService.getCurrentUserId() === content.user_id &&
<button className="trashButton" onClick={() => props.deleteSurvey(content.id)}>
<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.25 0.75V2H0V4.5H1.25V20.75C1.25 21.413 1.51339 22.0489 1.98223 22.5178C2.45107 22.9866 3.08696 23.25 3.75 23.25H16.25C16.913 23.25 17.5489 22.9866 18.0178 22.5178C18.4866 22.0489 18.75 21.413 18.75 20.75V4.5H20V2H13.75V0.75H6.25ZM3.75 4.5H16.25V20.75H3.75V4.5ZM6.25 7V18.25H8.75V7H6.25ZM11.25 7V18.25H13.75V7H11.25Z" fill="#DE6600" />
</svg>
</button>
}
</div>
<Link to={`/survey/${content.id}/fill`} style={{ textDecoration: 'none' }}>
Fill
</Link>
{' | '}
<Link to={`/survey/${content.id}/answer`} style={{ textDecoration: 'none' }}>
View Results
</Link>
<div className="surveyCardContent">
<p>
By{' '}
<Link to={`/profile/${content.username}/1`}>{content.username}</Link>
{', '}
{time}
</p>
</div>
</div>
);
}
# Diskuy Survey FrontEnd
## Tentang fitur
Fitur ini adalah sebuah fitur yang ditujukan untuk memfasilitasi pengguna Diskuy apabila akan mengadakan survey. Pengguna dapat mengadakan suatu survey lalu meminta pengguna lain untuk mengisi surveynya. Pembuat survey dapat melihat hasil survey apabila masa survey telah berakhir.
## Authors
Kelompok MemePro
* Ashila Ghassani - NPM : 1806205395
* Kezia Sulami - NPM: 1806133755
* Qadzif Kamil Zahari - NPM: 1806205565
* Iqrar Agalosi Nureyza - NPM: 1806204902
* Christopher Samuel - NPM: 1806141151
\ No newline at end of file
/* eslint-disable eqeqeq */
import React, { useEffect, useState, Fragment } from 'react';
import axios from 'axios';
import SurveyCards from './SurveyCards';
import { API_URL } from '../../config/keys';
import Pagination from '../Pagination';
import { dummyData } from '../../helpers/data-util';
import { authHeader } from '../../helpers/services/auth.service';
export default function RecentSurveys(props) {
const [surveys, setSurveys] = useState([]);
const [totalItems, setTotalItems] = useState(0);
const currentPage = props.pageNumber;
if (currentPage != props.pageNumber) {
window.location.reload();
}
useEffect(() => {
const fetch = async () => {
let data, metadata;
try {
const responseSurveys = await axios.get(
`${API_URL}/surveys/pages/top/?page=${currentPage}`
);
({ data, metadata } = responseSurveys.data);
} catch (error) {
data = dummyData();
}
setSurveys(data);
setTotalItems(metadata.total_data);
};
fetch();
}, [currentPage]);
function switchPage(pageNumber) {
props.history.push(`/survey/${pageNumber}`);
window.location.reload();
}
async function deleteSurvey(id) {
setSurveys(surveys.filter(survey => survey.id !== id));
await axios.delete(
`${API_URL}/surveys/${id}`,
{ headers: authHeader() }
);
}
return (
<div className="recentSurveys">
{totalItems == undefined || totalItems == 0 ? (
<div className="noSurveysMessage">
<p>There is no surveys yet.</p>
</div>
) : (
<Fragment>
<SurveyCards survey={surveys} deleteSurvey={deleteSurvey} />
<div className="paginationContainer">
<Pagination
activePage={currentPage}
totalItems={totalItems}
switchPage={switchPage}
/>
</div>
</Fragment>
)}
</div>
);
}
import PreviewSurvey from './PreviewSurvey';
export default function SurveyCards(props) {
return (
<div className="list_surveys">
{props.survey.map((value) => (
<PreviewSurvey content={value} deleteSurvey={props.deleteSurvey} />
))}
</div>
);
}
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { API_URL } from '../../config/keys';
import { authHeader } from '../../helpers/services/auth.service';
import ShowDiagram from './diagram/ShowDiagram';
import { diagram5Color } from '../../helpers/color';
export default function ViewAnswer(props) {
const { survey } = props;
const { id, question } = props.question;
const [answer, setAnswer] = useState([]);
const total = answer.map(val => val.answer_count).reduce((a,b) => a+b, 0);
useEffect(() => {
async function getAnswer() {
const answer = (await axios.get(
`${API_URL}/answers/${survey}/${id}`,
{ headers: authHeader() }
))?.data;
const existingAnswer = answer.map(val => val.answer);
const emptyAnswer = ['1','2','3','4','5'].filter(val => !existingAnswer.includes(val))
.map(val => { return { answer:val, answer_count: 0 }; });
setAnswer(answer.concat(emptyAnswer).sort((a, b) => parseInt(a.answer) - parseInt(b.answer)));
}
getAnswer();
// eslint-disable-next-line
}, [id]);
const styleDiagram = {
display: 'flex',
};
const styleLabel = {
marginRight: '24px',
}
return (
<div>
<div>
<h5>{question}</h5>
</div>
<div style={styleDiagram}>
<div style={styleLabel}>
{ answer.map((val, idx) => <ColorLabel key={idx} label={val.answer} amount={val.answer_count} total={total} idx={idx}/>) }
</div>
<div>
<ShowDiagram data={answer}/>
</div>
</div>
</div>
)
}
function ColorLabel(props) {
const { label, amount, total } = props;
const style = {
backgroundColor: `#${diagram5Color[label]}`,
marginLeft: '24px',
paddingLeft: '48px',
marginRight: '12px',
};
const percent = total !== 0 ? amount / total * 100 : 0;
return (
<div style={{ width:'fit-content' }}>
<span>Answer: <strong>{label}</strong></span>
<span style={style}>&nbsp;</span>
<span>Amount: <strong>{amount}</strong>&nbsp;({percent.toFixed(2)}%)</span>
</div>
)
}