Fakultas Ilmu Komputer UI

Commit a0b5785d authored by Zamil Majdy's avatar Zamil Majdy
Browse files

Merge branch 'Personal/Zamil' of https://gitlab.com/PPL2017csui/PPLA1 into...

Merge branch 'Personal/Zamil' of https://gitlab.com/PPL2017csui/PPLA1 into Features/create-vacancy-company

# Conflicts:
#	assets/js/VacancyPage.jsx
#	assets/js/components/RegisterModal.jsx
#	assets/js/index.jsx
parents 7e737ac2 65d0c67b
......@@ -11,7 +11,7 @@ export default class Dashboard extends React.Component {
render = () => (
<div>
<TopMenu />
<TopMenu />
{this.props.children}
</div>
)
......
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import ApplyConfirmationModal from '../../components/ApplyConfirmationModal';
import Storage from '../../lib/Storage';
import Server from '../../lib/Server';
describe('ApplyConfirmationModal', () => {
const fetchMock = require('fetch-mock');
it('renders without problem', () => {
const applyModal = ReactTestUtils.renderIntoDocument(
<ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />,
);
expect(applyModal).to.exist;
});
it('test apply without problem', () => {
const applyModal = ReactTestUtils.renderIntoDocument(
<ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />,
);
const response = { student: { id: 1, name: 2 } };
Storage.set('user-data', response);
fetchMock.post('*', { data: 'value' });
applyModal.open();
applyModal.handleOpen();
applyModal.close();
expect(applyModal.state.header).to.equal('Menghubungkan ke Server');
});
it('test apply with problem', () => {
const applyModal = ReactTestUtils.renderIntoDocument(
<ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />,
);
const response = { student: { id: 1, name: 2 } };
Storage.set('user-data', response);
fetchMock.post('*', { status: 404, body: response });
applyModal.open();
applyModal.handleOpen();
applyModal.close();
expect(applyModal.state.header).to.equal('Menghubungkan ke Server');
});
});
\ No newline at end of file
......@@ -29,6 +29,8 @@ describe('CancelModal', () => {
});
it('remove vacancy without problem', () => {
const fetchMock = require('fetch-mock');
fetchMock.delete('*', { data: 'value' });
const modalPendaftaran = ReactTestUtils.renderIntoDocument(
<CancelModal id={4} />);
......
/* eslint-disable no-unused-expressions */
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import ModalAlert from '../../components/ModalAlert';
describe('ModalAlert', () => {
it('renders without problem', () => {
const modalAlert = ReactTestUtils.renderIntoDocument(
<ModalAlert id={4} coverLetter="letter" onChangeValue={() => {}}/>);
const modalAlert = ReactTestUtils.renderIntoDocument(<ModalAlert />);
expect(modalAlert).to.exist;
});
});
\ No newline at end of file
it('renders with open state set to false', () => {
const modalAlert = ReactTestUtils.renderIntoDocument(<ModalAlert />);
expect(modalAlert.state.open).to.equal(false);
});
it('modal state should be true when opened', () => {
const modalAlert = ReactTestUtils.renderIntoDocument(<ModalAlert />);
modalAlert.open();
expect(modalAlert.state.open).to.equal(true);
});
it('modal closed properly with open state should be false', () => {
const modalAlert = ReactTestUtils.renderIntoDocument(<ModalAlert />);
modalAlert.close();
expect(modalAlert.state.open).to.equal(false);
});
});
......@@ -94,6 +94,11 @@ describe('Server get test', () => {
Server.sendRequest('/test', 'GET', {}, true).then((data) => { expect(data).to.exist; });
});
it('Check submit method', () => {
fetchMock.get('*', {hue : "hue", hui : "hui"});
Server.submit('/test', {hue : "hue", hui : "hui"}, 'GET', true).then((data) => { expect(data).to.exist; });
});
it('Check isloggedin method', () => {
expect(Server.isLoggedIn()).to.not.exist;
});
......
/* eslint-disable no-unused-expressions */
/* eslint-disable */
import Storage from './../../lib/Storage';
describe('Storage get key', () => {
......@@ -30,4 +30,21 @@ describe('Storage clear key', () => {
expect(Storage.clear()).to.be.not.exist;
expect(Storage.get('test')).to.be.not.exist;
});
});
\ No newline at end of file
});
describe('Storage getUserData test', () => {
const fetchMock = require('fetch-mock');
it('Check Storage when there is no userdata', () => {
fetchMock.get('*', 'hue');
Storage.set('user-data', null); // clear to make sure storage empty
expect(Storage.get('user-data')).to.equal(null);
Storage.getUserData().then(r => {expect(r).to.equal('hue');}); // Storage call
});
it('Check Storage when there is already userdata', () => {
Storage.set('user-data','hue'); // set to make sure storage not empty
Storage.getUserData().then(r => {expect(r).to.equal('hue');});
});
});
import React from 'react';
import { Modal, Button, Icon } from 'semantic-ui-react';
import Server from '../lib/Server';
import Storage from '../lib/Storage';
export default class ApplyConfirmationModal extends React.Component {
static propTypes = {
id: React.PropTypes.number.isRequired,
onChangeValue: React.PropTypes.func.isRequired,
coverLetter: React.PropTypes.string,
status: React.PropTypes.string.isRequired,
};
static defaultProps = {
coverLetter: '',
};
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = {
open: false,
header: 'Menghubungkan ke Server',
content: 'Harap menunggu, permintaan anda sedang diproses...',
};
this.handleOpen = this.handleOpen.bind(this);
}
open = () => this.setState({ open: true });
close = () => this.setState({ open: false });
handleOpen() {
const studentId = Storage.get('user-data').student.id;
const daftarSuccess = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n';
const daftarFailed = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n';
const requestData = { vacancy_id: this.props.id, cover_letter: this.props.coverLetter };
Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then(() => {
this.setState({
header: 'Pendaftaran Berhasil',
content: daftarSuccess,
});
}, () => {
this.setState({
header: 'Pendaftaran Gagal',
content: daftarFailed,
});
});
}
render() {
const { open } = this.state;
const buttonColor = this.props.status === 'Daftar' ? 'blue' : 'red';
return (
<Modal
open={open}
onOpen={this.open}
onClose={this.close}
size="small"
basic
trigger={<Button color={buttonColor} onClick={this.handleOpen}> {this.props.status} <Icon name="right chevron" /></Button>}
>
<Modal.Header>{this.state.header}</Modal.Header>
<Modal.Content>
<p>{this.state.content}</p>
</Modal.Content>
<Modal.Actions>
<Button icon="checkmark" color="green" content="All Done" onClick={this.props.onChangeValue} />
</Modal.Actions>
</Modal>
);
}
}
import React from 'react';
import { Modal, Button, Icon, TextArea, Form } from 'semantic-ui-react';
import ModalAlert from './ModalAlert';
import { Modal, Button, TextArea, Form } from 'semantic-ui-react';
import ApplyConfirmationModal from './ApplyConfirmationModal';
export default class ApplyModal extends React.Component {
static propTypes = {
......@@ -66,7 +66,7 @@ export default class ApplyModal extends React.Component {
</Modal.Content>
<Modal.Actions>
<ModalAlert
<ApplyConfirmationModal
id={this.props.id}
onChangeValue={this.handleClose}
coverLetter={this.state.coverLetter}
......
import React from 'react';
import { Modal, Button, Icon } from 'semantic-ui-react';
import Server from '../lib/Server';
import Storage from '../lib/Storage';
import { Button, Header, Icon, Modal } from 'semantic-ui-react';
export default class ModalAlert extends React.Component {
static propTypes = {
id: React.PropTypes.number.isRequired,
onChangeValue: React.PropTypes.func.isRequired,
coverLetter: React.PropTypes.string,
status: React.PropTypes.string.isRequired,
};
static defaultProps = {
coverLetter: '',
};
constructor(props) {
super(props);
/* istanbul ignore next */
this.state = {
open: false,
header: 'Menghubungkan ke Server',
content: 'Harap menunggu, permintaan anda sedang diproses...',
};
this.handleOpen = this.handleOpen.bind(this);
this.state = { open: false, header: '', content: '' };
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
open = () => this.setState({ open: true });
close = () => this.setState({ open: false });
handleOpen() {
const studentId = Storage.get('user-data').student.id;
componentWillUpdate() {
this.fixBody();
}
const daftarSuccess = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n';
const daftarFailed = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n';
componentDidUpdate() {
this.fixBody();
}
if (this.props.status == 'Daftar') {
const requestData = { vacancy_id: this.props.id, cover_letter: this.props.coverLetter };
fixBody = () => {
const anotherModal = document.getElementsByClassName('ui page modals').length;
if (anotherModal > 0) document.body.classList.add('scrolling', 'dimmable', 'dimmed');
};
Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then(() => {
this.setState({
header: 'Pendaftaran Berhasil',
content: this.daftarSuccess,
});
}, () => {
this.setState({
header: 'Pendaftaran Gagal',
content: this.daftarFailed,
});
});
} else {
open = (header = this.state.header, content = this.state.content) => {
this.setState({ open: true, header, content });
};
}
}
close = () => {
this.setState({ open: false });
};
render() {
const { open } = this.state;
const buttonColor = this.props.status === 'Daftar' ? 'blue' : 'red';
return (
<Modal
open={open}
onOpen={this.open}
onClose={this.close}
size="small"
basic
trigger={<Button color={buttonColor} onClick={this.handleOpen}> {this.props.status} <Icon name="right chevron" /></Button>}
>
<Modal.Header>{this.state.header}</Modal.Header>
<Modal.Content>
<p>{this.state.content}</p>
</Modal.Content>
<Modal.Actions>
<Button icon="checkmark" color="green" content="All Done" onClick={this.props.onChangeValue} />
</Modal.Actions>
</Modal>
);
}
render = () => (
<Modal open={this.state.open} basic size="small">
<Header icon="warning sign" content={this.state.header} />
<Modal.Content>
<p>{this.state.content}</p>
</Modal.Content>
<Modal.Actions>
<Button color="green" inverted onClick={this.close}>
<Icon name="checkmark" /> OK
</Button>
</Modal.Actions>
</Modal>
);
}
import React from 'react';
import { browserHistory } from 'react-router';
import { Modal, Button, Form, Input, TextArea, Header, Icon } from 'semantic-ui-react';
import ModalAlert from './../components/ModalAlert';
import Server from './../lib/Server';
import Storage from './../lib/Storage';
......@@ -32,34 +33,20 @@ export default class RegisterModal extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
const form = new FormData();
Object.keys(this.state).map(k => form.append(k, this.state[k]));
const data = {
'X-CSRFToken': Server.getCookie('csrftoken'),
mode: 'no-cors',
method: 'POST',
body: form,
credentials: 'same-origin',
};
fetch('/api/register/', data)
.then((response) => {
if (response.status < 200 || response.status > 399) throw response.json();
else return response.json();
})
.then((response) => {
alert('Akun berhasil dibuat :)');
Storage.set('user-data', response);
browserHistory.push('/home');
}, error => error.then(r => alert(JSON.stringify(r))));
Server.submit('/register/', this.state).then((response) => {
Storage.set('user-data', response);
browserHistory.push('/home');
}, error => error.then((r) => {
this.modalAlert.open('Gagal Membuat Akun', r.error);
}));
};
render = () => (
<Modal trigger={<Button primary floated="right">Register</Button>} closeIcon="close">
<Header icon="archive" content="Register for More Benefits" />
<Modal.Content>
<ModalAlert ref={(modal) => { this.modalAlert = modal; }} />
<Form onSubmit={this.handleSubmit}>
<Header as="h2" icon textAlign="center">
<Icon name="signup" circular />
......@@ -90,7 +77,7 @@ export default class RegisterModal extends React.Component {
<label htmlFor="name">Nama Perusahaan</label>
<Input onChange={this.handleChange} placeholder="Nama Perusahaan" name="name" required />
</Form.Field>
<Form.Field>
<Form.Field required>
<label htmlFor="logo">Logo</label>
<Input
onChange={this.handleChange}
......@@ -98,6 +85,7 @@ export default class RegisterModal extends React.Component {
icon={{ name: 'attach', circular: true, link: true }}
placeholder="attach logo"
type="File"
required
/>
</Form.Field>
<Form.Field required>
......@@ -110,11 +98,10 @@ export default class RegisterModal extends React.Component {
</Form.Field>
</Form>
</Modal.Content>
<Modal.Actions>
<Button type="submit" color="blue"> <Icon name='checkmark' />Submit</Button>
</Modal.Actions>
</Modal.Content>
<Modal.Actions>
<Button type="submit" color="blue"> <Icon name="checkmark" />Submit</Button>
</Modal.Actions>
</Modal>
)
}
......
......@@ -5,9 +5,6 @@ import { Segment } from 'semantic-ui-react';
import Dashboard from './Dashboard';
import Login from './Login';
import VacancyPage from './VacancyPage';
import HomeCompany from './HomeCompany';
import CreateLowongan from './CreateLowongan';
import ProfilePage from './ProfilePage';
import Server from './lib/Server';
import Storage from './lib/Storage';
......@@ -27,13 +24,17 @@ export default class App extends React.Component {
this.handleHome = this.handleHome.bind(this);
}
handleAuth = (nextState, replace) => (
Server.isLoggedIn() || replace({ pathname: '/login' })
);
handleAuth = (nextState, replace) => {
if (!Server.isLoggedIn()) replace({ pathname: '/login' });
Storage.getUserData();
};
handleHome= (nextState, replace) => {
handleHome= (nextState, replace, cb) => {
if (Server.isLoggedIn()) {
Storage.get('user-data').student ? replace({ pathname: '/lowongan' }) : replace({ pathname: '/profile' });
Storage.getUserData().then((data) => {
const next = data.student ? '/lowongan' : '/profile';
replace({ pathname: next }); cb();
});
} else {
replace({ pathname: '/login' });
}
......@@ -43,12 +44,9 @@ export default class App extends React.Component {
<Router history={browserHistory}>
<Route path="/login" component={Login} />
<Route component={Dashboard} onEnter={this.handleAuth}>
<Route path="/" component={VacancyPage} />
<Route path="/profile" component={Profile} />
<Route path="/lowongan" component={VacancyPage} />
<Route path="/company" component={HomeCompany} />
<Route path="/users" component={Profile} />
<Route path="/create-lowongan" component={CreateLowongan} />
<Route path="/profile" component={ProfilePage} />
</Route>
<Route path="/home" onEnter={this.handleHome} />
<Redirect from="*" to="/home" />
......
......@@ -17,8 +17,22 @@ export default class Server {
return null;
}
static submit(path, data, method = 'POST', useCache = false) {
const form = new FormData();
Object.keys(data).map(k => form.append(k, data[k]));
const requestData = {
'X-CSRFToken': Server.getCookie('csrftoken'),
mode: 'no-cors',
method: 'POST',
body: form,
credentials: 'same-origin',
};
return this.handleFetchRequest(requestData, path, useCache);
}
static sendRequest(path, method, data, useCache = false) {
const url = `/api${path}`;
const csrftoken = this.getCookie('csrftoken');
const headers = {
'content-type': 'application/json',
......@@ -32,7 +46,11 @@ export default class Server {
credentials: 'same-origin',
};
// noinspection JSUnresolvedFunction
return this.handleFetchRequest(requestData, path, useCache);
}
static handleFetchRequest(requestData, path, useCache) {
const url = `/api${path}`;
const request = fetch(url, requestData);
/* istanbul ignore next */
......@@ -43,12 +61,8 @@ export default class Server {
const contentType = response.headers.get('content-type');
const result = contentType && contentType.indexOf('application/json') !== -1 ? response.json() : response.text();
if (response.status < 200 || response.status > 399) {
throw result;
}
else {
return result;
}
if (response.status < 200 || response.status > 399) throw result;
else return result;
});
/* istanbul ignore next */
......
import Server from './../lib/Server';
/** Session Storage Polyfill */
/* eslint-disable */
......@@ -120,14 +122,23 @@ polyfil();
export default class Storage {
static get(key) {
return JSON.parse(sessionStorage.getItem(key));
return JSON.parse(localStorage.getItem(key));
}
static set(key, value) {
return sessionStorage.setItem(key, JSON.stringify(value));
return localStorage.setItem(key, JSON.stringify(value));
}
static clear() {
return sessionStorage.clear();
return localStorage.clear();
}
static getUserData() {
const data = Storage.get('user-data');
/* istanbul ignore next */
return (data != null) ? Promise.resolve(data) : Server.get('/users/me/').then((response) => {
Storage.set(('user-data'), response);
return response;
});
}
}
......@@ -4,14 +4,14 @@ from rest_framework import serializers
from core.models.accounts import Supervisor, Company, Student
class UserSerializer(serializers.HyperlinkedModelSerializer):
class BasicUserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'is_staff')
class StudentSerializer(serializers.ModelSerializer):
user = UserSerializer()
user = BasicUserSerializer()
name = serializers.ReadOnlyField()
class Meta:
......@@ -20,7 +20,7 @@ class StudentSerializer(serializers.ModelSerializer):
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer()
user = BasicUserSerializer()
name = serializers.ReadOnlyField()
class Meta:
......@@ -29,7 +29,7 @@ class CompanySerializer(serializers.ModelSerializer):