Fakultas Ilmu Komputer UI

Commit 2789f34b authored by KIANUTAMA RADIANUR HUDHA's avatar KIANUTAMA RADIANUR HUDHA
Browse files

Merge branch 'master' of https://gitlab.cs.ui.ac.id/pmpl/class-project/kape into 1606887516-22

parents 180fa163 dfd6c109
Pipeline #22250 passed with stages
in 94 minutes and 22 seconds
......@@ -24,6 +24,7 @@ test:
- npm install
- npm run build-production
- pip install -r requirements.txt
- python manage.py makemigrations
- python manage.py migrate
- python manage.py test
- npm run karma
......
......@@ -166,3 +166,4 @@ container. Thus, you can see the app via Web browser by opening
Copyright (c) 2017 PPLA1 Team & Faculty of Computer Science Universitas
Indonesia. This project is licensed under [ISC](LICENSE) license.
......@@ -289,4 +289,30 @@ card .formRegis{
.ui.segment.kop {
line-height: 5px;
}
.jumlahLamaran {
font-weight: bold;
}
.search-container {
width: fit-content;
margin: auto;
}
.search-form {
border: none;
border-bottom: solid #0d5aa7 2px;
text-decoration: none;
outline: none;
font-size: 14pt;
}
.search-button {
background-color: #0d5aa7;
color: white;
border: none;
padding: 10px;
font-size: 14pt;
border-radius: 5px;
margin-left: 10px;
}
\ No newline at end of file
......@@ -7,7 +7,6 @@ import Pagination from './components/Pagination';
import Server from './lib/Server';
export default class ApplicantPage extends React.Component {
static propTypes = {
user: React.PropTypes.object.isRequired,
};
......@@ -29,33 +28,39 @@ export default class ApplicantPage extends React.Component {
this.getVacancyList();
}
getVacancyList= () => Server.get(`/companies/${this.props.user.data.company.id}/vacancies/`, false).then((data) => {
const results = data.results;
const names = ['Semua Lowongan'];
const urls = [{
key: 0,
value: `/companies/${this.props.user.data.company.id}/applications/`,
text: 'Semua Lowongan',
}];
results.map((vacancy) => {
names.push(vacancy.name);
const url = `/companies/${this.props.user.data.company.id}/applications/${vacancy.id}/by_vacancy/`;
const info = {
key: vacancy.id,
value: url,
text: vacancy.name,
};
urls.push(info);
return urls;
});
this.setState({ urls });
}, error => error.then((r) => {
this.modalAlert.open('Gagal mendapatkan daftar lowongan', r.detail);
}));
getVacancyList = () =>
Server.get(`/companies/${this.props.user.data.company.id}/vacancies/`, false).then(
(data) => {
const results = data.results;
const names = ['Semua Lowongan'];
const urls = [
{
key: 0,
value: `/companies/${this.props.user.data.company.id}/applications/`,
text: 'Semua Lowongan',
},
];
results.map((vacancy) => {
names.push(vacancy.name);
const url = `/companies/${this.props.user.data.company.id}/applications/${vacancy.id}/by_vacancy/`;
const info = {
key: vacancy.id,
value: url,
text: vacancy.name,
};
urls.push(info);
return urls;
});
this.setState({ urls });
},
error =>
error.then((r) => {
this.modalAlert.open('Gagal mendapatkan daftar lowongan', r.detail);
}),
);
handleChange = (e, data) => {
this.setState({ selected: data.value, refresh: this.state.refresh + 5 });
this.setState({ selected: data.value, refresh: this.state.refresh + 6 });
};
render() {
......@@ -63,7 +68,13 @@ export default class ApplicantPage extends React.Component {
return (
<div>
<div className="dropdownApplicant">
<Dropdown placeholder="Semua Lowongan" search selection options={this.state.urls} onChange={this.handleChange} />
<Dropdown
placeholder="Semua Lowongan"
search
selection
options={this.state.urls}
onChange={this.handleChange}
/>
</div>
<Tabs selected={0}>
<Pagination
......@@ -115,6 +126,17 @@ export default class ApplicantPage extends React.Component {
/>
}
/>
<Pagination
key={6 + this.state.refresh}
url={`${this.state.selected}?status=${Applicant.APPLICATION_STATUS.FINISHED}`}
label="Lamaran Selesai"
child={
<ApplicantList
companyId={company.id}
status={Applicant.APPLICATION_STATUS.FINISHED}
/>
}
/>
</Tabs>
</div>
);
......
......@@ -22,6 +22,7 @@ export default class CompanyProfile extends React.Component {
<h2>{ data.name }</h2>
<h3>{ data.address }t</h3>
<p>{ data.category } - { data.description }</p>
<p>{ data.website }</p>
</div>
</Container>
</Segment>
......
......@@ -32,6 +32,7 @@ export default class CreateVacancy extends React.Component {
close_time: moment(),
name: '',
description: '',
salary: 0,
};
if (this.state.vacancyId) {
......@@ -39,6 +40,7 @@ export default class CreateVacancy extends React.Component {
this.setState({
description: r.description,
name: r.name,
salary: r.salary,
open_time: moment(r.open_time),
close_time: moment(r.close_time),
loading: false,
......@@ -70,6 +72,7 @@ export default class CreateVacancy extends React.Component {
const data = {};
data.name = this.state.name;
data.description = this.state.description;
data.salary = this.state.salary;
data.open_time = this.state.open_time.format();
data.close_time = this.state.close_time.format();
if (!this.state.vacancyId) {
......@@ -104,6 +107,7 @@ export default class CreateVacancy extends React.Component {
<CKEditor value={this.state.description} onChange={this.handleEditor} /> }
<script>CKEDITOR.replace( 'description' );</script>
<br />
<Form.Field label="Gaji / Insentif (Rupiah per bulan)" type="number" min="0" name="salary" control={Input} onChange={this.handleChange} value={this.state.salary} />
<Form.Group widths="equal">
<Form.Field
className="open-time-field"
......
......@@ -39,12 +39,17 @@ export default class ProfilePage extends React.Component {
resume: '',
show_transcript: '',
intro: '',
latest_work: '',
latest_work_desc: '',
},
bagikanTranskrip: '',
acceptedNo: 0,
readNo: 0,
refresh: 1,
loading: false,
linkedin_url: '',
latest_work: '',
latest_work_desc: '',
};
this.getProfile = this.getProfile.bind(this);
this.handleChange = this.handleChange.bind(this);
......@@ -75,10 +80,13 @@ export default class ProfilePage extends React.Component {
photo: data.photo,
show_transcript: data.show_transcript,
acceptedNo: data.accepted_no,
readNo: data.read_no,
bagikanTranskrip: data.show_transcript,
refresh: this.state.refresh + 1,
intro: data.intro,
linkedin_url: data.linkedin_url,
latest_work: data.latest_work,
latest_work_desc: data.latest_work_desc,
});
if (this.props.route.own) {
const newSession = this.props.user.data;
......@@ -170,7 +178,14 @@ export default class ProfilePage extends React.Component {
<label htmlFor="region">Region</label>
<input onChange={this.handleChange} placeholder="Indonesia" name="region" />
</Form.Field>
<Form.Field>
<label htmlFor="latest_work">Latest Working Experience</label>
<input onChange={this.handleChange} placeholder="Teaching Asssistant at Fasilkom UI" name="latest_work" />
</Form.Field>
<Form.Field>
<label htmlFor="latest_work_desc">Latest Working Experience Description</label>
<input onChange={this.handleChange} placeholder="Evaluate weekly assignment for 15 students" name="latest_work_desc" />
</Form.Field>
<Form.Field>
<label htmlFor="resume">Resume</label>
<input onChange={this.handleFile} placeholder="Resume" name="resume" type="File" />
......@@ -271,9 +286,7 @@ export default class ProfilePage extends React.Component {
</Grid.Column>
</Grid>
</Segment>
</div>
<Segment basic vertical>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<Icon name="map pin" size="big" />
......@@ -283,21 +296,38 @@ export default class ProfilePage extends React.Component {
</Grid.Column>
</Grid>
</Segment>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<h3>Intro</h3>
<Icon name="suitcase" size="big" />
</Grid.Column>
<Grid.Column width={13}>
<p> { this.state.intro || 'N/A' } </p>
<p>Latest working experience as: <span><b> { this.state.latest_work || 'N/A' } </b></span></p>
<p>Description: <span> { this.state.latest_work_desc || 'N/A' } </span></p>
</Grid.Column>
</Grid>
</Segment>
</div>
<Segment basic vertical>
<Grid>
<Grid.Column width={2}>
<h3>Intro</h3>
</Grid.Column>
<Grid.Column width={13}>
<p> { this.state.intro || 'N/A' } </p>
</Grid.Column>
</Grid>
</Segment>
<Segment basic vertical>
<Grid>
<p className="jumlahLamaran"> Jumlah lamaran diterima: {this.state.acceptedNo || '0' }</p>
</Grid>
<Grid>
<p className="jumlahLamaran"> Jumlah lamaran dibaca perusahaan: {this.state.readNo || '0' }</p>
</Grid>
</Segment>
<Container textAlign="center">
<div className="buttonProfile">
<Button onClick={this.gotoStudentResume} disabled={!this.state.resume} primary size="small">Resume</Button>
......
......@@ -78,6 +78,19 @@ export default class VacancyPage extends React.Component {
}
/>
</Pane>
<Pane label="Lowongan Tersedia" >
<Pagination
key={4}
url="/vacancies/opened_only=1"
child={
<VacancyList
user={this.props.user}
key={4}
userId={this.state.id}
/>
}
/>
</Pane>
</Tabs>
);
} else if ((this.props.user.role === 'admin' && this.props.user.data.company != null)
......
This diff is collapsed.
......@@ -25,6 +25,7 @@ const companyUser = {
logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png',
address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat',
category: 'Belum ada kategori perusahaan',
website: 'Belum ada link website'
},
supervisor: null,
student: null,
......
......@@ -211,6 +211,12 @@ describe('ProfilePage', () => {
profile.handleFile({ target: { name: 'photo', files: ['abc', 'def'] } });
expect(profile.state.form.photo).to.equal('abc');
});
it('renders count application without problem', () => {
fetchMock.get('*', response);
const profile = ReactTestUtils.renderIntoDocument(
<ProfilePage route={{ own: true, data: studentSession }} user={{ data: studentSession }} params={{}} />);
profile.getProfile().then(()=> expect(profile.state.acceptedNo).to.not.equal(''));
profile.getProfile().then(()=> expect(profile.state.readNo).to.not.equal(''));
fetchMock.restore();
});
});
\ No newline at end of file
......@@ -2,20 +2,33 @@
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import fetchMock from 'fetch-mock';
import { Button } from 'semantic-ui-react';
import ApproveModal from '../../components/ApproveModal';
import Applicant from '../../components/Applicant';
describe('ApproveModal', () => {
it('renders without problem', () => {
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' } }} />);
<ApproveModal
updateStatus={() => {}}
data={{ key: 'value', student: { resume: 'asdasd' } }}
/>,
);
expect(modalApproval).to.exist;
});
it('open without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW }} />);
<ApproveModal
updateStatus={() => {}}
data={{
key: 'value',
student: { resume: 'asdasd' },
status: Applicant.APPLICATION_STATUS.NEW,
}}
/>,
);
const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalApproval, 'Button');
ReactTestUtils.Simulate.click(modal);
......@@ -25,7 +38,16 @@ describe('ApproveModal', () => {
it('open resume without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW, show_transcript: true }} />);
<ApproveModal
updateStatus={() => {}}
data={{
key: 'value',
student: { resume: 'asdasd' },
status: Applicant.APPLICATION_STATUS.NEW,
show_transcript: true,
}}
/>,
);
modalApproval.gotoStudentResume();
fetchMock.restore();
});
......@@ -33,7 +55,16 @@ describe('ApproveModal', () => {
it('open transcript without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW, show_transcript: true }} />);
<ApproveModal
updateStatus={() => {}}
data={{
key: 'value',
student: { resume: 'asdasd' },
status: Applicant.APPLICATION_STATUS.NEW,
show_transcript: true,
}}
/>,
);
modalApproval.gotoStudentTranscript();
fetchMock.restore();
});
......@@ -42,7 +73,15 @@ describe('ApproveModal', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
fetchMock.patch('*', { student: { resume: 'asdasd' } });
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW }} />);
<ApproveModal
updateStatus={() => {}}
data={{
key: 'value',
student: { resume: 'asdasd' },
status: Applicant.APPLICATION_STATUS.NEW,
}}
/>,
);
modalApproval.handleClose();
expect(modalApproval.state.modalOpen).to.equal(false);
......@@ -51,9 +90,13 @@ describe('ApproveModal', () => {
it('reject without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
fetchMock.patch('*', { });
fetchMock.patch('*', {});
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, cover_letter: 'asdasd' }} />);
<ApproveModal
updateStatus={() => {}}
data={{ key: 'value', student: { resume: 'asdasd' }, cover_letter: 'asdasd' }}
/>,
);
modalApproval.modal = { open: () => {} };
modalApproval.reject();
modalApproval.rejectApplication();
......@@ -62,11 +105,32 @@ describe('ApproveModal', () => {
fetchMock.restore();
});
it('finish without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
fetchMock.patch('*', {});
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal
updateStatus={() => {}}
data={{ key: 'value', student: { resume: 'asdasd' }, cover_letter: 'asdasd' }}
/>,
);
modalApproval.modal = { open: () => {} };
modalApproval.finish();
modalApproval.finishApplication();
modalApproval.gotoLink('link random');
expect(modalApproval.state.finishLoading).to.equal(true);
fetchMock.restore();
});
it('apply without problem', () => {
fetchMock.get('*', { student: { resume: 'asdasd' } });
fetchMock.patch('*', { });
fetchMock.patch('*', {});
const modalApproval = ReactTestUtils.renderIntoDocument(
<ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd', show_transcript: true } }} />);
<ApproveModal
updateStatus={() => {}}
data={{ key: 'value', student: { resume: 'asdasd', show_transcript: true } }}
/>,
);
modalApproval.modal = { open: () => {} };
modalApproval.accept();
modalApproval.acceptApplication();
......
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import CompanyRegisterModal from '../../components/CompanyRegisterModal';
describe('CompanyRegisterModal', () => {
function validatePassword(password) {
const lowerCaseLetters = /[a-z]/g;
const upperCaseLetters = /[A-Z]/g;
const numbers = /[0-9]/g;
if(password.length < 8) return "Password less than 8"; else
if(!lowerCaseLetters.test(password)) return "Password at least one lowercase letter"; else
if(!upperCaseLetters.test(password)) return "Password at least one uppercase letter"; else
if(!numbers.test(password)) return "Password at least one number";
else return true
}
it('renders without problem', () => {
const companyRegister = ReactTestUtils.renderIntoDocument(
<CompanyRegisterModal />);
expect(companyRegister).to.exist;
});
it('handle password validation', () => {
const password = '3s24Aasd';
expect(validatePassword(password)).to.equal(true);
});
});
......@@ -7,6 +7,7 @@ import Storage from '../../lib/Storage';
describe('Vacancy', () => {
const fetchMock = require('fetch-mock');
const response = {
salary: '1',
close_time: '2019-03-28T05:55:42Z',
company: {
address: 'kebayoran baru',
......@@ -24,6 +25,7 @@ describe('Vacancy', () => {
};
const response2 = {
salary: '1',
close_time: '2019-03-28T05:55:42Z',
company: {
address: 'kebayoran baru',
......@@ -44,6 +46,7 @@ describe('Vacancy', () => {
role: 'company',
data: {
url: 'http://localhost:8001/api/users/8/',
username: 'Tutuplapak',
email: '',
is_staff: false,
......@@ -128,6 +131,7 @@ describe('Vacancy', () => {
it('cancel bookmarks without problem', () => {
fetchMock.delete('*', { data: 'value' });
fetchMock.get('*', { count: 'value' });
const lowongan = ReactTestUtils.renderIntoDocument(
<Vacancy status="Daftar" user={studentUser} data={response2} bookmarked={1} />);
const response3 = { student: { id: 1, name: 2 } };
......@@ -142,6 +146,7 @@ describe('Vacancy', () => {
it('cancel bookmarks with problem', () => {
fetchMock.delete('*', 404);
fetchMock.get('*', { count: 'value' });
const lowongan = ReactTestUtils.renderIntoDocument(
<Vacancy status="Daftar" user={studentUser} data={response2} bookmarked={1} />);
const response3 = { student: { id: 1, name: 2 } };
......
......@@ -135,6 +135,7 @@ describe('VacancyList', () => {
verified: true,
open_time: '2017-04-26T03:39:11Z',
description: 'deskripsi',
salary: '1',
close_time: '2017-04-30T03:39:11Z',
created: '2017-04-26T03:39:39.916758Z',
updated: '2017-04-26T03:41:07.157634Z',
......@@ -163,6 +164,7 @@ describe('VacancyList', () => {
verified: true,
open_time: '2017-04-26T03:39:11Z',
description: 'deskripsi',
salary: '1',
close_time: '2017-04-30T03:39:11Z',
created: '2017-04-26T03:39:39.916758Z',
updated: '2017-04-26T03:41:07.157634Z',
......@@ -191,6 +193,7 @@ describe('VacancyList', () => {
verified: true,
open_time: '2017-04-26T03:39:11Z',
description: 'deskripsi',
salary: '1',
close_time: '2017-04-30T03:39:11Z',
created: '2017-04-26T03:39:39.916758Z',
updated: '2017-04-26T03:41:07.157634Z',
......@@ -219,6 +222,7 @@ describe('VacancyList', () => {
verified: true,
open_time: '2017-04-26T03:39:11Z',
description: 'deskripsi',
salary: '1',
close_time: '2017-04-30T03:39:11Z',
created: '2017-04-26T03:39:39.916758Z',
updated: '2017-04-26T03:41:07.157634Z',
......@@ -247,6 +251,7 @@ describe('VacancyList', () => {
verified: true,
open_time: '2017-04-26T03:39:11Z',
description: 'deskripsi',
salary: '1',
close_time: '2017-04-30T03:39:11Z',
created: '2017-04-26T03:39:39.916758Z',