diff --git a/assets/css/custom.css b/assets/css/custom.css index e711a99ccecb03db035e138feae7340b0f4441ef..3759def6f461a9de1f5bbbf07a39ce8b996b45b2 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -13,6 +13,10 @@ color:black; } +.item-list { + word-wrap: break-word; + width: 100%; +} .ui.card.register{ background-color: #EEEEEE; @@ -155,6 +159,8 @@ card .formRegis{ .ui.pointing.secondary.menu{ background-color: #ffffff; + margin-bottom: 10px; + margin-top: 0px; } .extra.extra-company h3{ @@ -237,3 +243,13 @@ card .formRegis{ margin-left: 10%; margin-right: 10%; } + + +.admin-bar{ + margin: 0; + padding: 0 10px 0 0; + width: 100%; + text-align: right; + background-color: #304D8A; + color: white; +} \ No newline at end of file diff --git a/assets/js/ApplicantPage.jsx b/assets/js/ApplicantPage.jsx index 2931c861f6ace5619a6db83b6bbb741e542e17c3..7367ab1ca3f1f90fe8c1695b04d94cf6ffa9a5b6 100644 --- a/assets/js/ApplicantPage.jsx +++ b/assets/js/ApplicantPage.jsx @@ -1,7 +1,5 @@ import React from 'react'; import Tabs from './components/Tabs'; -import Pane from './components/Pane'; -import Storage from './lib/Storage'; import ApplicantList from './components/ApplicantList'; import Applicant from './components/Applicant'; @@ -20,30 +18,17 @@ export default class ApplicantPage extends React.Component { errorFlag: false, company: { id: 1 }, }; - this.handleItemClick = this.handleItemClick.bind(this); } - handleItemClick = (e, { name }) => this.setState({ activeItem: name }); - render() { const company = this.props.user.data.company; return ( <Tabs selected={0}> - <Pane label="Lamaran Baru" > - <ApplicantList key={1} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.NEW}`} status={Applicant.APPLICATION_STATUS.NEW} /> - </Pane> - <Pane label="Lamaran Dibaca" > - <ApplicantList key={2} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.READ}`} status={Applicant.APPLICATION_STATUS.READ} /> - </Pane> - <Pane label="Lamaran Ditandai" > - <ApplicantList key={3} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.BOOKMARKED}`} status={Applicant.APPLICATION_STATUS.BOOKMARKED} /> - </Pane> - <Pane label="Lamaran Diterima" > - <ApplicantList key={4} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.ACCEPTED}`} status={Applicant.APPLICATION_STATUS.ACCEPTED} /> - </Pane> - <Pane label="Lamaran Ditolak" > - <ApplicantList key={5} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.REJECTED}`} status={Applicant.APPLICATION_STATUS.REJECTED} /> - </Pane> + <ApplicantList label="Lamaran Baru" key={1} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.NEW}`} status={Applicant.APPLICATION_STATUS.NEW} /> + <ApplicantList label="Lamaran Dibaca" key={2} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.READ}`} status={Applicant.APPLICATION_STATUS.READ} /> + <ApplicantList label="Lamaran Ditandai" key={3} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.BOOKMARKED}`} status={Applicant.APPLICATION_STATUS.BOOKMARKED} /> + <ApplicantList label="Lamaran Diterima" key={4} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.ACCEPTED}`} status={Applicant.APPLICATION_STATUS.ACCEPTED} /> + <ApplicantList label="Lamaran Ditolak" key={5} companyId={company.id} url={`/companies/${company.id}/applications/?status=${Applicant.APPLICATION_STATUS.REJECTED}`} status={Applicant.APPLICATION_STATUS.REJECTED} /> </Tabs> ); } diff --git a/assets/js/CompanyPage.jsx b/assets/js/CompanyPage.jsx new file mode 100644 index 0000000000000000000000000000000000000000..daec7e97d566d2bea428af5034f47f89acd9943e --- /dev/null +++ b/assets/js/CompanyPage.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Button } from 'semantic-ui-react'; +import Tabs from './components/Tabs'; +import CompanyList from './components/CompanyList'; +import Company from './components/Company'; + +export default class CompanyPage extends React.Component { + + static propTypes = { + user: React.PropTypes.object.isRequired, + }; + + handleClick = () => window.open('/admin/'); + + render() { + return ( + <div> + <div style={{ paddingLeft: '10px' }}> + <Button onClick={this.handleClick} icon="dashboard" labelPosition="left" color="facebook" content="Buka Menu Administrasi" /> + </div> + <Tabs selected={0}> + <CompanyList label="Baru" key={1} url={`/companies/?status=${Company.COMPANY_STATUS.NEW}`} status={Company.COMPANY_STATUS.NEW} /> + <CompanyList label="Terverifikasi" key={2} url={`/companies/?status=${Company.COMPANY_STATUS.VERIFIED}`} status={Company.COMPANY_STATUS.VERIFIED} /> + <CompanyList label="Ditolak" key={3} url={`/companies/?status=${Company.COMPANY_STATUS.UNVERIFIED}`} status={Company.COMPANY_STATUS.UNVERIFIED} /> + <CompanyList label="Semua Perusahaan" key={4} url={'/companies/'} status={Company.COMPANY_STATUS.ALL} /> + </Tabs> + </div> + ); + } +} diff --git a/assets/js/CreateVacancy.jsx b/assets/js/CreateVacancy.jsx index c1b7fe979e6e4f1e2c2191f9ec6635a2cc5be902..7fbf853fbc6abfee5999bfb9c9896dbbbb8c377f 100644 --- a/assets/js/CreateVacancy.jsx +++ b/assets/js/CreateVacancy.jsx @@ -5,7 +5,6 @@ import DatePicker from 'react-datepicker'; import moment from 'moment'; import ModalAlert from './components/ModalAlert'; import Server from './lib/Server'; -import Storage from './lib/Storage'; import Dumper from './lib/Dumper'; export default class CreateVacancy extends React.Component { @@ -20,6 +19,8 @@ export default class CreateVacancy extends React.Component { /* istanbul ignore next */ this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); + this.setCloseTime = this.setCloseTime.bind(this); + this.setOpenTime = this.setOpenTime.bind(this); this.state = { formLoading: false, @@ -41,6 +42,14 @@ export default class CreateVacancy extends React.Component { }); } + setOpenTime(date) { + this.setState({ open_time: date }); + } + + setCloseTime(date) { + this.setState({ close_time: date }); + } + handleChange = (e) => { this.setState({ [e.target.name]: e.target.value }); }; @@ -60,7 +69,7 @@ export default class CreateVacancy extends React.Component { Server.sendRequest(url, method, data).then(() => { browserHistory.push('/lowongan'); }, error => error.then((r) => { - this.modalAlert.open('Gagal Membuat Lowongan', Dumper.dump(r, ' ')); + this.modalAlert.open('Gagal Membuat Lowongan', r.detail); this.setState({ formLoading: false }); })); }; @@ -90,7 +99,7 @@ export default class CreateVacancy extends React.Component { control={DatePicker} label="Waktu Buka Lowongan" selected={this.state.open_time} - onChange={date => this.setState({ open_time: date })} + onChange={this.setOpenTime} required /> <Form.Field @@ -98,7 +107,7 @@ export default class CreateVacancy extends React.Component { control={DatePicker} label="Waktu Tutup Lowongan" selected={this.state.close_time} - onChange={date => this.setState({ close_time: date })} + onChange={this.setCloseTime} required /> </Form.Group> diff --git a/assets/js/Dashboard.jsx b/assets/js/Dashboard.jsx index c317fa5a8aa4b22997fddec9c9483602f1a79e08..ca0dbdecc22c16094c15f02833ee02df3f9bcf63 100755 --- a/assets/js/Dashboard.jsx +++ b/assets/js/Dashboard.jsx @@ -1,6 +1,6 @@ import React from 'react'; import TopMenu from './components/TopMenu'; -import Footer from './components/Footer' +import Footer from './components/Footer'; export default class Dashboard extends React.Component { static propTypes = { @@ -8,11 +8,12 @@ export default class Dashboard extends React.Component { React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node, ]).isRequired, + user: React.PropTypes.object.isRequired, }; render = () => ( <div> - <TopMenu /> + <TopMenu user={this.props.user} /> <div className="content"> {this.props.children} </div> <Footer /> </div> diff --git a/assets/js/ProfilePage.jsx b/assets/js/ProfilePage.jsx index f3a512b6756d4064c1068601323a58c44bb7045c..fb1fe596bcbc8ef9ec429d7b502f25dac1b9f0d8 100644 --- a/assets/js/ProfilePage.jsx +++ b/assets/js/ProfilePage.jsx @@ -89,7 +89,7 @@ export default class ProfilePage extends React.Component { const form = this.state.form; form[e.target.name] = e.target.files[0]; this.setState({ form }); - } + }; handleChange = (e) => { const form = this.state.form; diff --git a/assets/js/VacancyPage.jsx b/assets/js/VacancyPage.jsx index e7b1b8a6e6e301b9deccdf1bf330bd7e1239a03d..afe6de749a12cffb92d4b29cf0ac5ccca7210fc7 100644 --- a/assets/js/VacancyPage.jsx +++ b/assets/js/VacancyPage.jsx @@ -15,7 +15,7 @@ export default class VacancyPage extends React.Component { const role = user.role; if (role === 'student') { return user.data.student.id; - } else if (role === 'company') { + } else if (role === 'company' || role === 'admin') { return user.data.company.id; } @@ -64,7 +64,7 @@ export default class VacancyPage extends React.Component { </Pane> </Tabs> ); - } else if (this.props.user.role === 'company') { + } else if (this.props.user.role === 'company' || this.props.user.role === 'admin') { return ( <VacancyList key={1} userId={this.state.id} url={`/companies/${this.state.id}/vacancies/`} type="company" /> ); diff --git a/assets/js/__test__/ApplicantPage-test.jsx b/assets/js/__test__/ApplicantPage-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..d8d03185cf80da8327500226d6887327caca49e8 --- /dev/null +++ b/assets/js/__test__/ApplicantPage-test.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import ApplicantPage from '../ApplicantPage'; + +describe('ApplicantPage', () => { + fetchMock.get('*', { data: 'value' }); + + const companyUser = { + role: 'company', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + verified: true, + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }, + supervisor: null, + student: null, + }, + }; + + it('renders for companies without problem', () => { + const applicantPage = ReactTestUtils.renderIntoDocument( + <ApplicantPage user={companyUser} />); + expect(applicantPage).to.exist; + }); +}); diff --git a/assets/js/__test__/CompanyPage-test.jsx b/assets/js/__test__/CompanyPage-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..04dfdcaa8c24bab3b7be2c0aa5f5bf13df59c004 --- /dev/null +++ b/assets/js/__test__/CompanyPage-test.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import CompanyPage from '../CompanyPage'; + +describe('CompanyPage', () => { + fetchMock.get('*', { data: 'value' }); + + const adminUser = { + role: 'admin', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + verified: true, + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }, + supervisor: null, + student: null, + }, + }; + + it('renders for admin without problem', () => { + const companyPage = ReactTestUtils.renderIntoDocument( + <CompanyPage user={adminUser} />); + expect(companyPage).to.exist; + }); + + it('click dashboard button problem', () => { + const companyPage = ReactTestUtils.renderIntoDocument( + <CompanyPage user={adminUser} />); + const dashboardButton = ReactTestUtils.findRenderedDOMComponentWithTag(companyPage, 'Button'); + ReactTestUtils.Simulate.click(dashboardButton); + expect(companyPage).to.exist; + expect(dashboardButton).to.exist; + }); +}); diff --git a/assets/js/__test__/CreateVacancy-test.jsx b/assets/js/__test__/CreateVacancy-test.jsx index 7ee5db78c97d9caa3014e2d97aa9c2d5ccca9a49..7cdc39c75abcbfd52dbf2d134c2755f92acc724b 100644 --- a/assets/js/__test__/CreateVacancy-test.jsx +++ b/assets/js/__test__/CreateVacancy-test.jsx @@ -1,6 +1,7 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; import fetchMock from 'fetch-mock'; +import moment from 'moment'; import CreateVacancy from '../CreateVacancy'; import Storage from '../lib/Storage'; @@ -137,6 +138,8 @@ describe('CreateVacancy', () => { ReactTestUtils.Simulate.keyDown(openField, { key: 'Enter', keyCode: 13, which: 13 }); ReactTestUtils.Simulate.click(closeField); ReactTestUtils.Simulate.keyDown(closeField, { key: 'Enter', keyCode: 13, which: 13 }); + createVacancy.setOpenTime(moment()); + createVacancy.setCloseTime(moment()); expect(createVacancy.state.formLoading).to.equal(false); createVacancy.handleSubmit(new Event('click')); diff --git a/assets/js/__test__/Dashboard-test.jsx b/assets/js/__test__/Dashboard-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..892fbabf98466949ad103034a99a90d3deff1a90 --- /dev/null +++ b/assets/js/__test__/Dashboard-test.jsx @@ -0,0 +1,149 @@ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import Dashboard from '../Dashboard'; + +describe('Dashboard', () => { + fetchMock.get('*', { data: 'value' }); + + const studentUser = { + role: 'student', + data: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + company: null, + supervisor: null, + student: { + id: 3, + user: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + }, + name: 'Muhammad R.', + created: '2017-03-28T13:33:46.147241Z', + updated: '2017-03-28T13:33:46.148248Z', + npm: 1406543593, + resume: null, + phone_number: null, + bookmarked_vacancies: [ + 3, + ], + applied_vacancies: [ + 3, + 1, + ], + }, + }, + }; + + const companyUser = { + role: 'company', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + verified: true, + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }, + supervisor: null, + student: null, + }, + }; + + const adminUser = { + role: 'admin', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + const supervisorUser = { + role: 'supervisor', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + const errorUser = { + role: 'error', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + it('renders for company without problem', () => { + const dashboard = ReactTestUtils.renderIntoDocument( + <Dashboard user={companyUser} />); + expect(dashboard).to.exist; + }); + + it('renders for supervisor without problem', () => { + const dashboard = ReactTestUtils.renderIntoDocument( + <Dashboard user={supervisorUser}> + <div> test </div> + </Dashboard>); + expect(dashboard).to.exist; + }); + + it('renders for admin without problem', () => { + const dashboard = ReactTestUtils.renderIntoDocument( + <Dashboard user={adminUser}> + <div> test </div> + </Dashboard>); + expect(dashboard).to.exist; + }); + + it('renders for student without problem', () => { + const dashboard = ReactTestUtils.renderIntoDocument( + <Dashboard user={studentUser}> + <div> test </div> + </Dashboard>); + expect(dashboard).to.exist; + }); + + it('renders for error without problem', () => { + const dashboard = ReactTestUtils.renderIntoDocument( + <Dashboard user={errorUser}> + <div> test </div> + </Dashboard>); + expect(dashboard).to.exist; + }); +}); + diff --git a/assets/js/__test__/Login.jsx b/assets/js/__test__/Login.jsx new file mode 100644 index 0000000000000000000000000000000000000000..b562c29070ef12b17468e801f2b152aa0a26beac --- /dev/null +++ b/assets/js/__test__/Login.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import Login from '../Login'; + +describe('Login', () => { + it('renders for login without problem', () => { + const login = ReactTestUtils.renderIntoDocument(<Login><div>test</div></Login>); + expect(login).to.exist; + }); +}); + diff --git a/assets/js/__test__/TranscriptPage-test.jsx b/assets/js/__test__/TranscriptPage-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..00eb2b2064940845245076b771aa3060d7b96617 --- /dev/null +++ b/assets/js/__test__/TranscriptPage-test.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import TranscriptPage from '../TranscriptPage'; + +describe('TranscriptPage', () => { + const data = { + transcript: [ + { kelas: { nm_kls: 'kelas1' }, nilai: 'A' }, + { kelas: { nm_kls: 'kelas2' }, nilai: 'B' }, + { nilai: 'B' }, + ], + name: 'Badak Terbang', + }; + + fetchMock.get('*', data); + + it('renders for admin without problem', () => { + const transcriptPage = ReactTestUtils.renderIntoDocument( + <TranscriptPage params={{ id: 1 }} />); + transcriptPage.setState({data}) + expect(transcriptPage).to.exist; + }); +}); diff --git a/assets/js/__test__/VacancyPage-test.jsx b/assets/js/__test__/VacancyPage-test.jsx index fd2d07c2b726c4c37a3e8a493e540a7de93145b2..dcccff16e1a09c5f74f18bfdc43281185bbb13ea 100644 --- a/assets/js/__test__/VacancyPage-test.jsx +++ b/assets/js/__test__/VacancyPage-test.jsx @@ -3,7 +3,6 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; import fetchMock from 'fetch-mock'; import VacancyPage from '../VacancyPage'; -import Storage from '../lib/Storage'; describe('VacancyPage', () => { fetchMock.get('*', { data: 'value' }); diff --git a/assets/js/__test__/components/Applicant-test.jsx b/assets/js/__test__/components/Applicant-test.jsx index 4935bbe12be6343477a89f1de0f3f70920f3290b..d9db8170b088fb87a80b90795feb1049b08f1944 100644 --- a/assets/js/__test__/components/Applicant-test.jsx +++ b/assets/js/__test__/components/Applicant-test.jsx @@ -52,31 +52,4 @@ describe('Applicant', () => { expect(applicant).to.exist; fetchMock.restore(); }); - - // it('test apply without problem', () => { - // const applyModal = ReactTestUtils.renderIntoDocument( - // <Applicant />, - // ); - // const response = { student: { id: 1, name: 2 } }; - // - // Storage.set('user-data', response); - // fetchMock.post('*', { data: 'value' }); - // applyModal.open('Menghubungkan ke Server'); - // expect(applyModal.state.header).to.equal('Menghubungkan ke Server'); - // fetchMock.restore(); - // }); - // - // it('test apply with problem', () => { - // const applyModal = ReactTestUtils.renderIntoDocument( - // <Applicant />, - // ); - // const response = { student: { id: 1, name: 2 } }; - // - // Storage.set('user-data', response); - // fetchMock.post('*', 404); - // applyModal.open('Menghubungkan ke Server', '', '', () => {}); - // applyModal.handleYes(); - // expect(applyModal.state.header).to.equal('Menghubungkan ke Server'); - // fetchMock.restore(); - // }); }); diff --git a/assets/js/__test__/components/ApplicantList-test.jsx b/assets/js/__test__/components/ApplicantList-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0575be3ab327aa3d81b835afbdd07283e60d4d58 --- /dev/null +++ b/assets/js/__test__/components/ApplicantList-test.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import ApplicantList from '../../components/ApplicantList'; +import Applicant from '../../components/Applicant'; + +describe('ApplicantList', () => { + + const vacancy = { + close_time: '2019-03-28T05:55:42Z', + company: { + address: 'kebayoran baru', + id: 1, + logo: null, + name: 'tutup lapak', + }, + created: '2017-03-28T07:05:47.128672Z', + description: 'Lorem ipsum dolbh.', + id: 1, + name: 'Software Engineer', + open_time: '2017-03-28T05:55:38Z', + updated: '2017-03-28T07:34:13.122093Z', + verified: true, + } + + const student = { + role: 'student', + data: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + company: null, + supervisor: null, + student: { + id: 3, + user: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + }, + name: 'Muhammad R.', + created: '2017-03-28T13:33:46.147241Z', + updated: '2017-03-28T13:33:46.148248Z', + npm: 1406543593, + resume: null, + phone_number: null, + bookmarked_vacancies: [ + 3, + ], + applied_vacancies: [ + 3, + 1, + ], + }, + }, + }; + + const response = [ + { id: 1, status: Applicant.APPLICATION_STATUS.ACCEPTED, student, vacancy }, + { id: 2, status: Applicant.APPLICATION_STATUS.BOOKMARKED, student, vacancy }, + { id: 3, status: Applicant.APPLICATION_STATUS.NEW, student, vacancy }, + { id: 4, status: Applicant.APPLICATION_STATUS.REJECTED, student, vacancy }, + ]; + + fetchMock.restore(); + fetchMock.get('*', response); + + it('renders without problem', () => { + const applicantList = ReactTestUtils.renderIntoDocument( + <ApplicantList status={Applicant.APPLICATION_STATUS.ACCEPTED} url="test" />); + expect(applicantList).to.exist; + }); + + it('can update status', () => { + const applicantList = ReactTestUtils.renderIntoDocument( + <ApplicantList status={Applicant.APPLICATION_STATUS.ACCEPTED} url="test" />); + applicantList.setState({applications : response }); + + expect(applicantList.state).to.not.equal(response); + applicantList.updateStatus(1, Applicant.APPLICATION_STATUS.BOOKMARKED); + expect(applicantList.state).to.not.equal(response); + }); +}); + diff --git a/assets/js/__test__/components/ApplyModal-test.jsx b/assets/js/__test__/components/ApplyModal-test.jsx index f06675e173670ac571d3b40a5f81e99f731c6584..1119ff7caf68e2043b4803570fb7d64bdd3f3a76 100644 --- a/assets/js/__test__/components/ApplyModal-test.jsx +++ b/assets/js/__test__/components/ApplyModal-test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; import ApplyModal from '../../components/ApplyModal'; +import fetchMock from 'fetch-mock'; describe('ApplyModal', () => { it('renders without problem', () => { @@ -11,12 +12,25 @@ describe('ApplyModal', () => { }); it('open without problem', () => { + fetchMock.post('*', {}); const modalPendaftaran = ReactTestUtils.renderIntoDocument( <ApplyModal id={4} data={{ key: 'value' }} buttonTitle="submit" apply={() => {}}/>); const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalPendaftaran, 'Button'); modalPendaftaran.handleApply(); ReactTestUtils.Simulate.click(modal); expect(modalPendaftaran).to.exist; + fetchMock.restore(); + }); + + it('open with problem', () => { + fetchMock.post('*', 404); + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <ApplyModal id={4} data={{ key: 'value' }} buttonTitle="submit" apply={() => {}}/>); + const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalPendaftaran, 'Button'); + modalPendaftaran.handleApply(); + ReactTestUtils.Simulate.click(modal); + expect(modalPendaftaran).to.exist; + fetchMock.restore(); }); it('change without problem', () => { diff --git a/assets/js/__test__/components/ApproveModal-test.jsx b/assets/js/__test__/components/ApproveModal-test.jsx index 739192afbadc615a999a6a84ee95b2e848fea0f6..2f0a3bf025769cd70c5cdf2159939e661b759209 100644 --- a/assets/js/__test__/components/ApproveModal-test.jsx +++ b/assets/js/__test__/components/ApproveModal-test.jsx @@ -3,6 +3,7 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; import fetchMock from 'fetch-mock'; import ApproveModal from '../../components/ApproveModal'; +import Applicant from '../../components/Applicant'; describe('ApproveModal', () => { it('renders without problem', () => { @@ -14,18 +15,34 @@ describe('ApproveModal', () => { it('open without problem', () => { fetchMock.get('*', { student: { resume: 'asdasd' } }); const modalApproval = ReactTestUtils.renderIntoDocument( - <ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' } }} />); + <ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW }} />); const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalApproval, 'Button'); ReactTestUtils.Simulate.click(modal); fetchMock.restore(); }); + 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 }} />); + modalApproval.gotoStudentResume(); + fetchMock.restore(); + }); + + 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 }} />); + modalApproval.gotoStudentTranscript(); + fetchMock.restore(); + }); it('close without problem', () => { fetchMock.get('*', { student: { resume: 'asdasd' } }); + fetchMock.patch('*', { student: { resume: 'asdasd' } }); const modalApproval = ReactTestUtils.renderIntoDocument( - <ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' } }} />); + <ApproveModal updateStatus={() => {}} data={{ key: 'value', student: { resume: 'asdasd' }, status: Applicant.APPLICATION_STATUS.NEW }} />); modalApproval.handleClose(); expect(modalApproval.state.modalOpen).to.equal(false); diff --git a/assets/js/__test__/components/Company-test.jsx b/assets/js/__test__/components/Company-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..640fb2d51b43ff35c0b628c350a2f228cac96b82 --- /dev/null +++ b/assets/js/__test__/components/Company-test.jsx @@ -0,0 +1,93 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import Company from '../../components/Company'; +import fetchMock from 'fetch-mock'; + +describe('Company', () => { + const stub = { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }; + + const company1 = {}; + const company2 = {}; + Object.assign(company1, stub); + Object.assign(company2, stub); + company1.status = Company.COMPANY_STATUS.NEW; + company2.status = Company.COMPANY_STATUS.VERIFIED; + company2.logo = null; + + it('renders without problem', () => { + fetchMock.get('*', company1); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company1} updateStatus={() => {}} />, + ); + expect(company).to.exist; + fetchMock.restore(); + }); + + it('accept without problem', () => { + fetchMock.get('*', company1); + fetchMock.patch('*', {}); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company2} updateStatus={() => {}} />, + ); + company.accept(); + expect(company).to.exist; + fetchMock.restore(); + }); + + it('accept with problem', () => { + fetchMock.get('*', company1); + fetchMock.patch('*', 404); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company1} updateStatus={() => {}} />, + ); + company.accept(); + expect(company).to.exist; + fetchMock.restore(); + }); + + it('reject without problem', () => { + fetchMock.get('*', company1); + fetchMock.patch('*', {}); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company2} updateStatus={() => {}} />, + ); + company.reject(); + expect(company).to.exist; + fetchMock.restore(); + }); + + it('reject with problem', () => { + fetchMock.get('*', company1); + fetchMock.patch('*', 404); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company1} updateStatus={() => {}} />, + ); + company.reject(); + expect(company).to.exist; + fetchMock.restore(); + }); + + it('go to company home click with problem', () => { + fetchMock.get('*', company1); + fetchMock.patch('*', 404); + const company = ReactTestUtils.renderIntoDocument( + <Company data={company1} updateStatus={() => {}} />, + ); + company.goToCompanyHome(); + fetchMock.restore(); + }); +}); diff --git a/assets/js/__test__/components/CompanyList-test.jsx b/assets/js/__test__/components/CompanyList-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0fb2ddab6d7caf6ee935318963e79c3cef31b339 --- /dev/null +++ b/assets/js/__test__/components/CompanyList-test.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import CompanyList from '../../components/CompanyList'; +import Company from '../../components/Company'; + +describe('CompanyList', () => { + const company = { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }; + + const company1 = {}; + const company2 = {}; + const company3 = {}; + Object.assign(company1, company); + Object.assign(company2, company); + Object.assign(company3, company); + company1.status = Company.COMPANY_STATUS.NEW; + company2.status = Company.COMPANY_STATUS.VERIFIED; + company3.status = Company.COMPANY_STATUS.ALL; + + const response = [company1, company2, company3]; + + fetchMock.restore(); + fetchMock.get('*', response); + + it('renders without problem', () => { + const companyList = ReactTestUtils.renderIntoDocument( + <CompanyList status={Company.COMPANY_STATUS.VERIFIED} url="test" />); + expect(companyList).to.exist; + }); + + it('can update status', () => { + const companyList = ReactTestUtils.renderIntoDocument( + <CompanyList status={Company.COMPANY_STATUS.VERIFIED} url="test" />); + companyList.setState({ companies: response }); + companyList.generateCompanies(); + + expect(companyList.state).to.not.equal(response); + companyList.updateStatus(3, Company.COMPANY_STATUS.ALL); + expect(companyList.state).to.not.equal(response); + }); +}); + diff --git a/assets/js/__test__/components/TopMenu-test.jsx b/assets/js/__test__/components/TopMenu-test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..7f841e107bb7c79274fbd1312fbe4d5e1116d399 --- /dev/null +++ b/assets/js/__test__/components/TopMenu-test.jsx @@ -0,0 +1,197 @@ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import fetchMock from 'fetch-mock'; +import TopMenu from '../../components/TopMenu'; + +describe('TopMenu', () => { + fetchMock.restore(); + fetchMock.get('*', { data: 'value' }); + + const studentUser = { + role: 'student', + data: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + company: null, + supervisor: null, + student: { + id: 3, + user: { + url: 'http://localhost:8000/api/users/9/', + username: 'muhammad.reza42', + email: 'muhammad.reza42@ui.ac.id', + is_staff: false, + }, + name: 'Muhammad R.', + created: '2017-03-28T13:33:46.147241Z', + updated: '2017-03-28T13:33:46.148248Z', + npm: 1406543593, + resume: null, + phone_number: null, + bookmarked_vacancies: [ + 3, + ], + applied_vacancies: [ + 3, + 1, + ], + }, + }, + }; + + const companyUser = { + role: 'company', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: true, + company: { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + verified: true, + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }, + supervisor: null, + student: null, + }, + }; + + const adminUser1 = { + role: 'admin', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: true, + supervisor: null, + student: null, + company: { + id: 3, + user: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + }, + name: 'Tutuplapak', + created: '2017-03-28T07:30:10.535000Z', + updated: '2017-03-28T07:30:10.535000Z', + description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquet semper neque a fermentum. Duis ac tellus vitae augue iaculis ultrices. Curabitur commodo et neque nec feugiat. Morbi ac diam vel nunc commodo cursus. Phasellus nulla sapien, hendrerit vitae bibendum at, sollicitudin eu ante. Maecenas maximus, ante eu sollicitudin convallis, mauris nunc posuere risus, eu porttitor diam lacus vitae enim. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse at lectus a elit sollicitudin tempor. Nullam condimentum, justo nec tincidunt maximus, neque mi vulputate leo, sit amet lacinia massa ex eget sem. Duis ac erat facilisis, fringilla mauris in, consequat neque. In et neque consequat, vehicula magna at, efficitur ante. Mauris ac lacinia nibh.\r\n\r\nProin sagittis, lectus quis maximus varius, libero justo sollicitudin augue, non lacinia risus orci a enim. Curabitur iaculis enim quis ullamcorper commodo. Vivamus id nisi rhoncus, dignissim tellus quis, interdum est. Fusce sollicitudin eu libero ac feugiat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper posuere ex, sed accumsan libero iaculis faucibus. Fusce laoreet ac ligula ut consectetur. Donec tortor mauris, rutrum at sodales et, viverra in dolor. Sed bibendum elit et maximus volutpat. Phasellus justo ipsum, laoreet sit amet faucibus eu, ultricies suscipit mauris. Nullam aliquam libero eu ante ultrices mattis. Donec non justo hendrerit neque volutpat placerat. Ut euismod est nec sem mollis, sit amet porttitor massa rhoncus. Aenean id erat sit amet nunc ultrices scelerisque non in ipsum. Curabitur sollicitudin nulla id mi accumsan venenatis.', + verified: true, + logo: 'http://localhost:8001/files/company-logo/8a258a48-3bce-4873-b5d1-538b360d0059.png', + address: 'Jl. Kebayoran Baru nomor 13, Jakarta Barat', + }, + }, + }; + + const adminUser2 = { + role: 'admin', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + const supervisorUser = { + role: 'supervisor', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + const errorUser = { + role: 'error', + data: { + url: 'http://localhost:8001/api/users/8/', + username: 'Tutuplapak', + email: '', + is_staff: false, + company: null, + supervisor: null, + student: null, + }, + }; + + it('renders for company without problem', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={companyUser} />); + expect(topmenu).to.exist; + }); + + it('renders for supervisor without problem', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={supervisorUser}> + <div> test </div> + </TopMenu>); + expect(topmenu).to.exist; + }); + + it('renders for admin without problem', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={adminUser1}> + <div> test </div> + </TopMenu>); + expect(topmenu).to.exist; + }); + + it('renders for student without problem', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={studentUser}> + <div> test </div> + </TopMenu>); + expect(topmenu).to.exist; + }); + + it('renders for error without problem', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={errorUser}> + <div> test </div> + </TopMenu>); + expect(topmenu).to.exist; + }); + + it('renders for user to logout without problem', () => { + fetchMock.get('*', { data: 'value' }); + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={adminUser1}> + <div> test </div> + </TopMenu>); + topmenu.logout(new Event('click')); + expect(topmenu).to.exist; + }); + + it('changes the activeItem when item is clicked', () => { + const topmenu = ReactTestUtils.renderIntoDocument( + <TopMenu user={adminUser2}> + <div> test </div> + </TopMenu>); + topmenu.handleItemClick(new Event('click'), 'undefined'); + expect(topmenu.state.activeItem).to.equal(undefined); + }); +}); + diff --git a/assets/js/__test__/components/Vacancy-test.jsx b/assets/js/__test__/components/Vacancy-test.jsx index 7d99368d8b6d03d53cd547a1fe5c31d58512ed27..e84553845d70e148809e8cffc22929901811a60d 100644 --- a/assets/js/__test__/components/Vacancy-test.jsx +++ b/assets/js/__test__/components/Vacancy-test.jsx @@ -6,7 +6,7 @@ import Storage from '../../lib/Storage'; describe('Vacancy', () => { const fetchMock = require('fetch-mock'); - const response ={ + const response = { close_time: '2019-03-28T05:55:42Z', company: { address: 'kebayoran baru', @@ -15,12 +15,12 @@ describe('Vacancy', () => { name: 'tutup lapak', }, created: '2017-03-28T07:05:47.128672Z', - description: 'Lorem ipsum dolbh.', - id: 3, - name: 'Software Engineer', - open_time: '2017-03-28T05:55:38Z', - updated: '2017-03-28T07:34:13.122093Z', - verified: true, + description: 'Lorem ipsum dolbh.', + id: 3, + name: 'Software Engineer', + open_time: '2017-03-28T05:55:38Z', + updated: '2017-03-28T07:34:13.122093Z', + verified: true, }; const response2 = { @@ -78,10 +78,11 @@ describe('Vacancy', () => { it('cancel bookmarks without problem', () => { fetchMock.delete('*', { data: 'value' }); const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy status="Daftar" data={response2} bookmarked={1}/>); + <Vacancy status="Daftar" data={response2} bookmarked={1} />); const response3 = { student: { id: 1, name: 2 } }; lowongan.removeVacancyApplication(); lowongan.openConfirmationModal(); + lowongan.updateStatus(); expect(lowongan.props.data.id).to.equal(3); Storage.set('user-data', response3); expect(lowongan.bookmark()).to.be.undefined; @@ -91,37 +92,12 @@ describe('Vacancy', () => { it('cancel bookmarks with problem', () => { fetchMock.delete('*', 404); const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy status="Daftar" data={response2} bookmarked={1}/>); + <Vacancy status="Daftar" data={response2} bookmarked={1} />); const response3 = { student: { id: 1, name: 2 } }; lowongan.removeVacancyApplication(); expect(lowongan.props.data.id).to.equal(3); Storage.set('user-data', response3); - expect(lowongan.bookmark()).to.be.undefined; - fetchMock.restore(); - }); - - it('apply without problem', () => { - fetchMock.post('*', response); - fetchMock.delete('*', response); - const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy status="Daftar" data={response2} bookmarked={1}/>); - const response3 = { student: { id: 1, name: 2 } }; - lowongan.apply(1); - expect(lowongan.props.data.id).to.equal(3); - Storage.set('user-data', response3); - expect(lowongan.bookmark()).to.be.undefined; - fetchMock.restore(); - }); - - it('apply with problem', () => { - fetchMock.post('*', 404); - fetchMock.delete('*', response); - const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy status="Daftar" data={response2} bookmarked={1}/>); - const response3 = { student: { id: 1, name: 2 } }; - lowongan.apply(1); - expect(lowongan.props.data.id).to.equal(3); - Storage.set('user-data', response3); + lowongan.updateStatus('failed'); expect(lowongan.bookmark()).to.be.undefined; fetchMock.restore(); }); diff --git a/assets/js/__test__/components/VacancyList-test.jsx b/assets/js/__test__/components/VacancyList-test.jsx index 5cc07d5e7433d34a435a5fbe022827a304d99bd8..73e7494d647f64d0203a1bd5797e90ea71db5b4d 100644 --- a/assets/js/__test__/components/VacancyList-test.jsx +++ b/assets/js/__test__/components/VacancyList-test.jsx @@ -71,34 +71,47 @@ describe('VacancyList', () => { fetchMock.restore(); }); + it('renders with problem for company', () => { + fetchMock.get('*', 403); + const vacancyList = ReactTestUtils.renderIntoDocument( + <VacancyList type="company" userId={1} url="test" />); + vacancyList.state.vacancies = response; + expect(vacancyList.generateVacancies()).to.exist; + fetchMock.restore(); + }); + it('update bookmarks without problem', () => { fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( <VacancyList userId={1} url="test" />); - vacancyList.state.appliedList = [{id:1},{id:3}]; - vacancyList.updateStatusList(); - expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; - fetchMock.restore(); + vacancyList.state.appliedList = [{ id: 1 }, { id: 3 }]; + vacancyList.updateStatusList().then(() => { + expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; + fetchMock.restore(); + }); }); it('check applied vacancies without problem', () => { fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( <VacancyList userId={1} url="test" />); - vacancyList.updateStatusList(); - expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; - fetchMock.restore(); + vacancyList.updateStatusList().then(() => { + expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; + fetchMock.restore(); + }); }); - it('renders marked bookmarked vacancies without problem', () => { + it('renders marked bookmarked vacancies without problem', (done) => { fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( <VacancyList userId={1} url="test" />); vacancyList.state.vacancies = response; vacancyList.state.bookmarkList = [{ id: 5 }, { id: 3 }, { id: 1 }]; - vacancyList.updateStatusList(); - expect(vacancyList.generateVacancies()).to.exist; - fetchMock.restore(); + vacancyList.updateStatusList().then(() => { + expect(vacancyList.generateVacancies()).to.exist; + fetchMock.restore(); + done(); + }, () => done()); }); it('renders not marked vacancies without problem', () => { @@ -135,6 +148,7 @@ describe('VacancyList', () => { }); it('fails delete vacancy', (done) => { + fetchMock.restore(); fetchMock.delete('*', 404); fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( diff --git a/assets/js/components/Applicant.jsx b/assets/js/components/Applicant.jsx index b1d87a8ee3aa8c3d96ef0de0827e7db2ed89f94f..ca999696d421f28421afbfa9f49e92cc3131f42d 100644 --- a/assets/js/components/Applicant.jsx +++ b/assets/js/components/Applicant.jsx @@ -48,7 +48,7 @@ export default class Applicant extends React.Component { <Item > <ModalAlert ref={(modal) => { this.modalAlert = modal; }} /> <Item.Image size="small" src={defaultImage} /> - <Item.Content verticalAlign="middle"> + <Item.Content verticalAlign="middle" style={{ wordWrap: 'break-word', width: '100%' }}> <Item.Extra> <Grid.Row> <Grid.Column floated="left"> diff --git a/assets/js/components/ApplyModal.jsx b/assets/js/components/ApplyModal.jsx index e061da2afeb1a456a3abbe9fa0878d925776b1bb..75ace02500030d7640340dcb8cb406dc5d977eb1 100644 --- a/assets/js/components/ApplyModal.jsx +++ b/assets/js/components/ApplyModal.jsx @@ -1,13 +1,15 @@ import React from 'react'; import { Icon, Modal, Button, TextArea, Form } from 'semantic-ui-react'; import ModalAlert from './../components/ModalAlert'; +import Server from './../lib/Server'; export default class ApplyModal extends React.Component { static propTypes = { data: React.PropTypes.object.isRequired, active: React.PropTypes.bool.isRequired, buttonTitle: React.PropTypes.string.isRequired, - apply: React.PropTypes.func.isRequired, + studentId: React.PropTypes.number.isRequired, + updateStatus: React.PropTypes.func.isRequired, }; constructor(props) { @@ -33,15 +35,19 @@ export default class ApplyModal extends React.Component { handleClose = () => this.setState({ modalOpen: false, + load: false, }); handleApply = () => { - this.load(); - this.props.apply(); + this.setState({ load: true }); + const requestData = { vacancy_id: this.props.data.id, cover_letter: this.state.coverLetter }; + Server.post(`/students/${this.props.studentId}/applied-vacancies/`, requestData).then(() => { + this.modalAlert.open('Pendaftaran Berhasil', 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n', this.handleClose); + this.props.updateStatus('registered'); + }, () => this.modalAlert.open('Pendaftaran Gagal', 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n', this.handleClose), + ); }; - load = () => this.setState({ load: true }); - render = () => ( <Modal trigger={<Button primary onClick={this.handleOpen} floated="right">{this.props.buttonTitle}</Button>} diff --git a/assets/js/components/ApproveModal.jsx b/assets/js/components/ApproveModal.jsx index b69799ee7e9ddb26fde083df8f89b947f260829b..132083a6b9dcaa27338a0b8c8e95b4af37f310a4 100644 --- a/assets/js/components/ApproveModal.jsx +++ b/assets/js/components/ApproveModal.jsx @@ -21,10 +21,12 @@ export default class ApproveModal extends React.Component { this.handleOpen = this.handleOpen.bind(this); this.reject = this.reject.bind(this); this.accept = this.accept.bind(this); + this.gotoStudentResume = this.gotoStudentResume.bind(this); + this.gotoStudentTranscript = this.gotoStudentTranscript.bind(this); } handleOpen = () => this.setState({ modalOpen: true }); - handleClose = () => this.readApplication() && this.setState({ modalOpen: false }); + handleClose = () => { this.readApplication(); this.setState({ modalOpen: false }); } readApplication = () => { const data = { status: Applicant.APPLICATION_STATUS.READ }; @@ -63,6 +65,10 @@ export default class ApproveModal extends React.Component { win.focus(); }; + gotoStudentResume = () => this.gotoLink(this.props.data.student.resume); + + gotoStudentTranscript = () => this.gotoLink(`/transcript/${this.props.data.id}`); + accept = () => { this.modal.open( 'Terima Lamaran?', @@ -85,9 +91,9 @@ export default class ApproveModal extends React.Component { <h4> Cover Letter </h4> { this.props.data.cover_letter ? this.props.data.cover_letter : 'Kosong' } <div style={{ float: 'right', textAlign: 'right' }}> - {this.props.data.student.resume ? <a onClick={() => this.gotoLink(this.props.data.student.resume)} href="#" >CV Pelamar </a> : ''} + {this.props.data.student.resume ? <a onClick={this.gotoStudentResume} href="#" >CV Pelamar </a> : ''} <br /> - {this.props.data.student.show_transcript ? <a onClick={() => this.gotoLink(`/transcript/${this.props.data.id}`)} href="#" >Transkrip Pelamar</a> : ''} + {this.props.data.student.show_transcript ? <a onClick={this.gotoStudentTranscript} href="#" >Transkrip Pelamar</a> : ''} <br /> </div> </Modal.Content> diff --git a/assets/js/components/Company.jsx b/assets/js/components/Company.jsx new file mode 100644 index 0000000000000000000000000000000000000000..273444a91fb26db772a353e50fa4dc87ccaace51 --- /dev/null +++ b/assets/js/components/Company.jsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { Item, Button, Grid, Icon } from 'semantic-ui-react'; +import Server from '../lib/Server'; +import ModalAlert from './ModalAlert'; +import Storage from './../lib/Storage'; + +const defaultImage = 'http://semantic-ui.com/images/wireframe/image.png'; + +export default class Company extends React.Component { + static propTypes = { + data: React.PropTypes.object.isRequired, + updateStatus: React.PropTypes.func.isRequired, + }; + + static COMPANY_STATUS = { + NEW: 0, + UNVERIFIED: 1, + VERIFIED: 2, + ALL: 3, + }; + + constructor(props) { + super(props); + /* istanbul ignore next */ + this.state = { + rejectLoading: false, + acceptLoading: false, + }; + this.accept = this.accept.bind(this); + this.reject = this.reject.bind(this); + this.goToCompanyHome = this.goToCompanyHome.bind(this); + } + + reject = () => { + const data = { status: Company.COMPANY_STATUS.UNVERIFIED }; + this.setState({ rejectLoading: true }); + Server.patch(`/companies/${this.props.data.id}/`, data).then((response) => { + this.setState({ rejectLoading: false }); + this.props.updateStatus(this.props.data.id, response.status); + }, () => this.setState({ rejectLoading: false })); + }; + + accept = () => { + const data = { status: Company.COMPANY_STATUS.VERIFIED }; + this.setState({ acceptLoading: true }); + Server.patch(`/companies/${this.props.data.id}/`, data).then((response) => { + this.setState({ acceptLoading: false }); + this.props.updateStatus(this.props.data.id, response.status); + }, () => this.setState({ acceptLoading: false })); + }; + + goToCompanyHome = () => { + const userData = Storage.get('user-data'); + userData.company = this.props.data; + Storage.set('user-data', userData); + const win = window.open('/lowongan'); + win.focus(); + }; + + render() { + return ( + <Item > + <ModalAlert ref={(modal) => { this.modalAlert = modal; }} /> + <Item.Image size="small" src={this.props.data.logo ? this.props.data.logo : defaultImage} /> + <Item.Content verticalAlign="middle" style={{ wordWrap: 'break-word', width: '100%' }} > + <Item.Extra> + <Grid.Row> + <Grid.Column floated="left" style={{ width: '100%' }}> + <h4> {this.props.data.name} </h4> + {this.props.data.address} + </Grid.Column> + <Grid.Column floated="right" textAlign="center"> + <div> + {this.props.data.status === Company.COMPANY_STATUS.VERIFIED || <Button loading={this.state.acceptLoading} onClick={this.accept} floated="right" color="green" > + <Icon name="checkmark" />Terima + </Button>} + {this.props.data.status === Company.COMPANY_STATUS.UNVERIFIED || <Button loading={this.state.rejectLoading} onClick={this.reject} floated="right" color="red" > + <Icon name="remove" />Tolak + </Button>} + <Button onClick={this.goToCompanyHome} floated="right" color="facebook" > + <Icon name="home" />Login + </Button> + </div> + </Grid.Column> + </Grid.Row> + </Item.Extra> + </Item.Content> + </Item> + ); + } +} diff --git a/assets/js/components/CompanyList.jsx b/assets/js/components/CompanyList.jsx new file mode 100644 index 0000000000000000000000000000000000000000..9f0796bde36ff53c7c27401bcd459ccfd10ea308 --- /dev/null +++ b/assets/js/components/CompanyList.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Item, Grid, Loader } from 'semantic-ui-react'; +import Company from '../components/Company'; +import Server from '../lib/Server'; + +export default class CompanyList extends React.Component { + + static propTypes = { + url: React.PropTypes.string.isRequired, + status: React.PropTypes.number.isRequired, + }; + + + constructor(props) { + super(props); + /* istanbul ignore next */ + this.state = { companies: [], loading: true }; + Server.get(this.props.url, false).then((data) => { + this.setState({ companies: data, loading: false }); + }); + + this.generateCompanies = this.generateCompanies.bind(this); + this.updateStatus = this.updateStatus.bind(this); + } + + updateStatus(id, status) { + const obj = []; + this.state.companies.map((company) => { + const clonedObj = {}; + Object.assign(clonedObj, company); + if (company.id === id) clonedObj.status = status; + return obj.push(clonedObj); + }); + this.setState({ companies: obj }); + } + + generateCompanies() { + return this.state.companies.map(company => + (company.status === this.props.status || Company.COMPANY_STATUS.ALL === this.props.status) && (<Company + key={company.id} data={company} + updateStatus={this.updateStatus} + />), + ); + } + + render = () => ( + <div> + <Loader active={this.state.loading} /> + <Grid> + <Item.Group style={{ width: '100%' }}> + { this.generateCompanies() } + </Item.Group> + </Grid> + </div> + ); +} diff --git a/assets/js/components/CompanyRegisterModal.jsx b/assets/js/components/CompanyRegisterModal.jsx index 7f1a6b2257a400819382b345c715775d5bb5541e..450c3cd23b544eabc6fc77513fa5153a25292451 100644 --- a/assets/js/components/CompanyRegisterModal.jsx +++ b/assets/js/components/CompanyRegisterModal.jsx @@ -10,7 +10,9 @@ export default class CompanyRegisterModal extends React.Component { constructor(props) { super(props); /* istanbul ignore next */ + this.state = { loading: false }; this.handleChange = this.handleChange.bind(this); + this.handleFile = this.handleFile.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } @@ -18,6 +20,10 @@ export default class CompanyRegisterModal extends React.Component { this.setState({ [e.target.name]: e.target.value }); }; + handleFile = (e) => { + this.setState({ [e.target.name]: e.target.files[0] }); + }; + handlePassword = (e) => { if (e.target.name === 'password') this.passwordField = e.target; else @@ -34,11 +40,12 @@ export default class CompanyRegisterModal extends React.Component { handleSubmit = (e) => { e.preventDefault(); - + this.setState({ loading: true }); Server.submit('/register/', this.state).then((response) => { Storage.set('user-data', response); browserHistory.push('/home'); }, error => error.then((r) => { + this.setState({ loading: false }); this.modalAlert.open('Gagal Membuat Akun', r.error); })); }; @@ -81,7 +88,7 @@ export default class CompanyRegisterModal extends React.Component { <Form.Field required> <label htmlFor="logo">Logo</label> <Input - onChange={this.handleChange} + onChange={this.handleFile} name="logo" icon={{ name: 'attach', circular: true, link: true }} placeholder="attach logo" @@ -97,12 +104,11 @@ export default class CompanyRegisterModal extends React.Component { <label htmlFor="address">Alamat</label> <Input onChange={this.handleChange} placeholder="Alamat" name="address" required /> </Form.Field> - + <Modal.Actions style={{ textAlign: 'right' }}> + <Button loading={this.state.loading} type="submit" color="blue"> <Icon name="checkmark" />Submit</Button> + </Modal.Actions> </Form> </Modal.Content> - <Modal.Actions> - <Button type="submit" onClick={this.handleSubmit} color="blue"> <Icon name="checkmark" />Submit</Button> - </Modal.Actions> </Modal> ) } diff --git a/assets/js/components/Course.jsx b/assets/js/components/Course.jsx index d44fb78ad7c193b2ce0b36e024d714fbeb3991d6..03f005047c240a3c51e4b5f89a422243875a01fc 100644 --- a/assets/js/components/Course.jsx +++ b/assets/js/components/Course.jsx @@ -7,13 +7,10 @@ export default class Course extends React.Component { grade: React.PropTypes.string.isRequired, }; - - render() { - return ( - <Table.Row> - <Table.Cell>{this.props.courseName}</Table.Cell> - <Table.Cell>{this.props.grade}</Table.Cell> - </Table.Row> - ); - } + render = () => ( + <Table.Row> + <Table.Cell>{this.props.courseName}</Table.Cell> + <Table.Cell>{this.props.grade}</Table.Cell> + </Table.Row> + ); } diff --git a/assets/js/components/TopMenu.jsx b/assets/js/components/TopMenu.jsx index 43319101a03cefeb8bdcadaf9be8db0b7cd4a573..b69ae9e6c4ffcb6a255daaa7589d2844c42081b5 100644 --- a/assets/js/components/TopMenu.jsx +++ b/assets/js/components/TopMenu.jsx @@ -7,33 +7,55 @@ import Storage from '../lib/Storage'; export default class TopMenu extends React.Component { + static propTypes = { + user: React.PropTypes.object.isRequired, + }; + constructor(props) { super(props); /* istanbul ignore next */ this.state = { activeItem: 'home' }; this.logout = this.logout.bind(this); + this.logoutCompany = this.logoutCompany.bind(this); } handleItemClick = (e, { name }) => this.setState({ activeItem: name }); - logout = () => { + logout = (e) => { + e.preventDefault(); Server.get('/api-auth/logout/?next=/', true).then(() => { Storage.clear(); browserHistory.push('/login'); }); }; + logoutCompany = /* istanbul ignore next */ (e) => { + e.preventDefault(); + const userData = Storage.get('user-data'); + userData.company = null; + Storage.set('user-data', userData); + window.location.replace('/home'); + }; + render() { const { activeItem } = this.state; return ( - <Menu color="blue" pointing secondary> - <Image as="a" size="small" src="/assets/img/logo.png" href="/" /> - <Menu.Menu position="right"> - <Menu.Item as={Link} to="/home" name="home" active={activeItem === 'home'} onClick={this.handleItemClick} /> - <Menu.Item as={Link} to="/profile" name="profil" active={activeItem === 'profil'} onClick={this.handleItemClick} /> - <Menu.Item as={Link} onClick={this.logout} name="logout" /> - </Menu.Menu> - </Menu> + <div> + { this.props.user.data.is_staff && this.props.user.data.company && ( + <div className="admin-bar"> + Anda login sebagai perusahaan: {this.props.user.data.company.name} (#{this.props.user.data.company.id}). + Untuk keluar, silahkan klik <a href="#" onClick={this.logoutCompany}> link ini</a> + </div> + )} + <Menu color="blue" pointing secondary> + <Image as="a" size="small" src="/assets/img/logo.png" href="/" /> + <Menu.Menu position="right"> + <Menu.Item as={Link} to="/home" name="home" active={activeItem === 'home'} onClick={this.handleItemClick} /> + <Menu.Item as={Link} to="/profile" name="profil" active={activeItem === 'profil'} onClick={this.handleItemClick} /> + <Menu.Item as={Link} onClick={this.logout} name="logout" /> + </Menu.Menu> + </Menu> + </div> ); } } diff --git a/assets/js/components/Vacancy.jsx b/assets/js/components/Vacancy.jsx index 6dc9576e5313262dc32bb3685b826f240b651745..e777d3312316bf5ef4d05ee4bfeee7e4c7033ed5 100644 --- a/assets/js/components/Vacancy.jsx +++ b/assets/js/components/Vacancy.jsx @@ -30,6 +30,7 @@ export default class Vacancy extends React.Component { deleteLoading: false, }; this.bookmark = this.bookmark.bind(this); + this.updateStatus = this.updateStatus.bind(this); this.generateAction = this.generateAction.bind(this); this.openConfirmationModal = this.openConfirmationModal.bind(this); this.removeVacancyApplication = this.removeVacancyApplication.bind(this); @@ -45,6 +46,8 @@ export default class Vacancy extends React.Component { this.state.bookmarked = 1 - this.state.bookmarked; } + updateStatus = (status = 'registered') => this.setState({ status }); + removeVacancyApplication() { this.setState({ deleteLoading: true }); Server.delete(`/students/${this.props.studentId}/applied-vacancies/${this.props.data.id}/`).then(() => { @@ -65,22 +68,13 @@ export default class Vacancy extends React.Component { ); } - apply = () => { - const requestData = { vacancy_id: this.props.data.id, cover_letter: this.state.coverLetter }; - - Server.post(`/students/${this.props.studentId}/applied-vacancies/`, requestData).then(() => { - this.modalAlert.open('Pendaftaran Berhasil', 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n', this.handleClose); - this.setState({ status: 'registered' }); - }, () => this.modalAlert.open('Pendaftaran Gagal', 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n', this.handleClose), - ); - }; - generateAction() { const applyModal = (<ApplyModal + updateStatus={this.updateStatus} active={this.state.status === 'new'} - data={{ header: this.props.data.name, description: this.props.data.description }} + data={{ header: this.props.data.name, description: this.props.data.description, id: this.props.data.id }} buttonTitle="Detail" - apply={this.apply} + studentId={this.props.studentId} />); const cancelButton = <Button loading={this.state.deleteLoading} floated="right" color="red" onClick={this.openConfirmationModal}>Batal</Button>; @@ -93,7 +87,7 @@ export default class Vacancy extends React.Component { <ConfirmationModal ref={(modal) => { this.confirmationModal = modal; }} /> <ModalAlert ref={(modal) => { this.modalAlert = modal; }} /> <Item.Image size="small" src={this.props.data.company.logo ? this.props.data.company.logo : defaultImage} /> - <Item.Content verticalAlign="middle"> + <Item.Content verticalAlign="middle" style={{ wordWrap: 'break-word', width: '100%' }} > <Item.Extra> <Grid.Row> <Grid.Column floated="left"> diff --git a/assets/js/components/VacancyList.jsx b/assets/js/components/VacancyList.jsx index e7765ed3372d8beb43dacab61e7558851b22fda7..cc84ac4c8c36ae798743bfeca6d5ab879707697e 100644 --- a/assets/js/components/VacancyList.jsx +++ b/assets/js/components/VacancyList.jsx @@ -1,11 +1,10 @@ import React from 'react'; -import { Item, Button, Grid } from 'semantic-ui-react'; +import { Item, Button, Grid, Loader } from 'semantic-ui-react'; import { Link } from 'react-router'; import Vacancy from './Vacancy'; import CompanyVacancy from './CompanyVacancy'; import Server from '../lib/Server'; import ModalAlert from '../components/ModalAlert'; -import Aplicant from './../components/Applicant'; export default class VacancyList extends React.Component { @@ -25,7 +24,7 @@ export default class VacancyList extends React.Component { constructor(props) { super(props); /* istanbul ignore next */ - this.state = { vacancies: [], bookmarkList: [], appliedList: [] }; + this.state = { vacancies: [], bookmarkList: [], appliedList: [], loading: true }; this.updateStatusList = this.updateStatusList.bind(this); this.generateVacancies = this.generateVacancies.bind(this); this.checkBookmark = this.checkBookmark.bind(this); @@ -34,8 +33,11 @@ export default class VacancyList extends React.Component { if (this.props.type === 'student') { this.updateStatusList(); } else Server.get(this.props.url, false).then((data) => { - this.setState({ vacancies: data }); - }); + this.setState({ vacancies: data, loading: false }); + }, e => e.then((error) => { + this.modalAlert.open('Akun anda belum terverfikasi', 'Mohon tunggu sampai akun perusahaan anda terverifikasi atau hubungi pihak administrasi'); + this.setState({ loading: false }); + })); } checkBookmark(id) { @@ -52,15 +54,13 @@ export default class VacancyList extends React.Component { return 'new'; } - updateStatusList() { - Server.get(`/students/${this.props.userId}/bookmarked-vacancies/`, false).then((data) => { - this.setState({ bookmarkList: data }); - }).then(() => Server.get(`/students/${this.props.userId}/applied-vacancies/`, false).then((data) => { - this.setState({ appliedList: data }); - })).then(() => Server.get(this.props.url, false).then((data) => { - this.setState({ vacancies: data }); - })); - } + updateStatusList = () => Server.get(`/students/${this.props.userId}/bookmarked-vacancies/`, false).then((data) => { + this.setState({ bookmarkList: data }); + }).then(() => Server.get(`/students/${this.props.userId}/applied-vacancies/`, false).then((data) => { + this.setState({ appliedList: data }); + })).then(() => Server.get(this.props.url, false).then((data) => { + this.setState({ vacancies: data, loading: false }); + })); deleteVacancy = id => Server.delete(`/vacancies/${id}/`, this.state).then(() => { this.modalAlert.open('Hapus Lowongan', 'Lowongan berhasil dihapus'); @@ -111,6 +111,7 @@ export default class VacancyList extends React.Component { render = () => ( <div> + <Loader active={this.state.loading} /> <ModalAlert ref={(modal) => { this.modalAlert = modal; }} /> <Grid container columns="eleven" doubling> { this.companyHeader() } diff --git a/assets/js/index.jsx b/assets/js/index.jsx index 8fa8387becf5cfe03bc9374ee249a96d815b5add..d75da841c6ab557636720b4c90bd0b2fc27524ab 100644 --- a/assets/js/index.jsx +++ b/assets/js/index.jsx @@ -5,6 +5,7 @@ import Dashboard from './Dashboard'; import Login from './Login'; import VacancyPage from './VacancyPage'; import ProfilePage from './ProfilePage'; +import CompanyPage from './CompanyPage'; import CompanyProfile from './CompanyProfile'; import CreateVacancy from './CreateVacancy'; import Server from './lib/Server'; @@ -73,12 +74,15 @@ export default class App extends React.Component { replace({ pathname: '/lowongan' }); cb(); } else if (App.getRole() === 'company') { replace({ pathname: '/lowongan' }); cb(); + } else if (App.getRole() === 'admin') { + replace({ pathname: '/perusahaan' }); cb(); } } replace({ pathname: '/login' }); cb(); }; render() { + const staff = this.authorization(['admin']); const student = this.authorization(['admin', 'student']); // const supervisor = this.authorization(['admin', 'supervisor']); const company = this.authorization(['admin', 'company']); @@ -89,7 +93,7 @@ export default class App extends React.Component { return ( <Router history={browserHistory}> <Route path="/login" component={Login} /> - <Route component={Dashboard} onEnter={this.handleAuth}> + <Route component={commonUser(Dashboard)} onEnter={this.handleAuth}> <Route path="/transcript/:id" component={company(TranscriptPage)} /> <Route path="/lowongan" component={commonUser(VacancyPage)} /> <Route path="/buat-lowongan" component={company(CreateVacancy)} /> @@ -98,6 +102,7 @@ export default class App extends React.Component { <Route path="/profil" component={App.getRole() === 'student' ? student(ProfilePage) : company(CompanyProfile)} own /> <Route path="/mahasiswa/:id" component={grownups(ProfilePage)} /> <Route path="/perusahaan/:id" component={facultyMember(CompanyProfile)} /> + <Route path="/perusahaan" component={staff(CompanyPage)} /> <Route path="/transkrip/:id" component={facultyMember(CompanyProfile)} /> </Route> <Route path="/home" onEnter={this.handleHome} /> diff --git a/core/lib/permissions.py b/core/lib/permissions.py index 2b3b549a80ab61a262d827e9aad979a6f93f2308..93994b8a9bbf97d3151f053c2d516c5bae9f9e6e 100644 --- a/core/lib/permissions.py +++ b/core/lib/permissions.py @@ -1,32 +1,39 @@ from rest_framework import permissions -from rest_framework.exceptions import APIException +from rest_framework.exceptions import PermissionDenied from core.models import Company from core.models import Student from core.models import Supervisor from core.models import Application + def is_admin_or_student(user): - return user.is_superuser or hasattr(user, "student") + return user.is_superuser or user.is_staff or hasattr(user, "student") def is_admin_or_company(user): - return user.is_superuser or hasattr(user, "company") + if user.is_superuser or user.is_staff: + return True + + if not hasattr(user, "company") or user.company != Company.VERIFIED : + raise PermissionDenied("This account is not valid company account or has not been verified") + + return True def is_admin_or_supervisor(user): - return user.is_superuser or hasattr(user, "supervisor") + return user.is_superuser or user.is_staff or hasattr(user, "supervisor") def is_admin_or_supervisor_or_company(user): - return user.is_superuser or hasattr(user, "supervisor") or hasattr(user, "company") + return user.is_superuser or user.is_staff or hasattr(user, "supervisor") or hasattr(user, "company") class IsAdminOrSelfOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True - if request.user.is_superuser: + if request.user.is_superuser or request.user.is_staff: return True # Instance must have an attribute named `user` or be `user` if hasattr(obj, "user"): @@ -40,7 +47,7 @@ class IsAdminOrStudent(permissions.BasePermission): def has_object_permission(self, request, view, obj): user = request.user - if user.is_superuser: + if user.is_superuser or user.is_staff: return True student = None if isinstance(obj, Student): @@ -48,7 +55,7 @@ class IsAdminOrStudent(permissions.BasePermission): elif hasattr(obj, "student"): student = obj.student else: - raise APIException( + raise PermissionDenied( "Checking student permission on object {} not associated with Student" .format(type(obj.__name__)) ) @@ -62,7 +69,7 @@ class IsAdminOrSupervisor(permissions.BasePermission): def has_object_permission(self, request, view, obj): user = request.user - if user.is_superuser: + if user.is_superuser or user.is_staff: return True supervisor = None if isinstance(obj, Supervisor): @@ -70,7 +77,7 @@ class IsAdminOrSupervisor(permissions.BasePermission): elif hasattr(obj, "supervisor"): supervisor = obj.supervisor else: - raise APIException( + raise PermissionDenied( "Checking supervisor permission on object {} not associated with Supervisor" .format(type(obj.__name__)) ) @@ -84,7 +91,7 @@ class IsAdminOrCompany(permissions.BasePermission): def has_object_permission(self, request, view, obj): user = request.user - if user.is_superuser: + if user.is_superuser or user.is_staff: return True company = None if isinstance(obj, Company): @@ -92,7 +99,7 @@ class IsAdminOrCompany(permissions.BasePermission): elif hasattr(obj, "company"): company = obj.company else: - raise APIException( + raise PermissionDenied( "Checking company permission on object {} not associated with Company" .format(type(obj.__name__)) ) @@ -108,7 +115,7 @@ class IsAdminOrSupervisorOrCompany(permissions.BasePermission): class IsAdminOrSupervisorOrCompanyOrSelf(permissions.IsAuthenticated): def has_object_permission(self, request, view, obj): user = request.user - if user.is_superuser or hasattr(user, "company") or hasattr(user, "supervisor"): + if user.is_superuser or user.is_staff or hasattr(user, "company") or hasattr(user, "supervisor"): return True if hasattr(user, "student"): if isinstance(obj, Student): @@ -116,7 +123,7 @@ class IsAdminOrSupervisorOrCompanyOrSelf(permissions.IsAuthenticated): elif hasattr(obj, "student"): student = obj.student else: - raise APIException( + raise PermissionDenied( "Checking student permission on object {} not associated with Student" .format(type(obj.__name__)) ) @@ -130,11 +137,11 @@ class IsAdminOrVacancyOwner(permissions.BasePermission): def has_object_permission(self, request, view, obj): user = request.user - if user.is_superuser: + if user.is_superuser or user.is_staff: return True if isinstance(obj, Application): return user.company == obj.vacancy.company else: - raise APIException( + raise PermissionDenied( "Checking owner permission on non-application object" ) diff --git a/core/migrations/0012_auto_20170502_0925.py b/core/migrations/0012_auto_20170502_0925.py new file mode 100644 index 0000000000000000000000000000000000000000..692e227970e80fba34415889ffc542d6309c3846 --- /dev/null +++ b/core/migrations/0012_auto_20170502_0925.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-05-02 02:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_merge_20170425_2214'), + ] + + operations = [ + migrations.AlterModelOptions( + name='company', + options={'ordering': ['-updated']}, + ), + migrations.AlterModelOptions( + name='student', + options={'ordering': ['-updated']}, + ), + migrations.AlterModelOptions( + name='supervisor', + options={'ordering': ['-updated']}, + ), + migrations.AlterModelOptions( + name='vacancy', + options={'ordering': ['-updated']}, + ), + migrations.RemoveField( + model_name='company', + name='verified', + ), + migrations.AddField( + model_name='company', + name='status', + field=models.IntegerField(default=0), + ), + ] diff --git a/core/models/accounts.py b/core/models/accounts.py index 7fd1d71681bf91579b31a4fa45545d884b4ed0ad..7fe9aaa98006861559f4a3a62ec196cbc8bf4b99 100644 --- a/core/models/accounts.py +++ b/core/models/accounts.py @@ -75,16 +75,23 @@ class Student(models.Model): def __unicode__(self): return u"Student {}".format(get_display_name(self.user)) + class Meta: + ordering = ['-updated'] + class Company(models.Model): """ A user subclass """ + NEW = 0 + UNVERIFIED = 1 + VERIFIED = 2 + created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) user = models.OneToOneField(User) description = models.TextField() - verified = models.BooleanField(default=False) + status = models.IntegerField(default=NEW) logo = models.FileField(upload_to=get_company_logo_file_path, null=True, blank=True) address = models.CharField(max_length=1000, blank=True, null=True) @@ -95,6 +102,9 @@ class Company(models.Model): def __unicode__(self): return u"Company {}".format(get_display_name(self.user)) + class Meta: + ordering = ['-updated'] + class Supervisor(models.Model): """ @@ -111,3 +121,6 @@ class Supervisor(models.Model): def __unicode__(self): return u"Supervisor {}".format(get_display_name(self.user)) + + class Meta: + ordering = ['-updated'] diff --git a/core/models/vacancies.py b/core/models/vacancies.py index 96335453c6a69cfe396983af798406a5ef2fe8c5..f2b5dc4956206082a74c1e656b05a698d8c01d18 100644 --- a/core/models/vacancies.py +++ b/core/models/vacancies.py @@ -13,6 +13,9 @@ class Vacancy(models.Model): updated = models.DateTimeField(auto_now=True) name = models.CharField(max_length=100, null=False) + class Meta: + ordering = ['-updated'] + class Application(models.Model): NEW = 0 diff --git a/core/tests/test_accounts.py b/core/tests/test_accounts.py index e4889f544f125743816dbbf53186f6e55b88d359..89571e2849bd040bc7b72c2b2e94c1fb40062e06 100644 --- a/core/tests/test_accounts.py +++ b/core/tests/test_accounts.py @@ -4,8 +4,8 @@ from rest_framework.test import APIClient, APITestCase from django.contrib.auth.models import User from core.models.accounts import Company, Supervisor, Student -class LoginTests(APITestCase): +class LoginTests(APITestCase): @requests_mock.Mocker() def test_succesful_student_login_relogin(self, m): m.post('https://api.cs.ui.ac.id/authentication/ldap/v2/', json={ @@ -68,7 +68,7 @@ class LoginTests(APITestCase): def test_success_company_login(self): new_user = User.objects.create_user('dummy.login.company', 'dummy.login.company@company.com', 'lalala123') - new_company = Company.objects.create(user=new_user, description="lalalala", verified=True, logo=None, address=None) + new_company = Company.objects.create(user=new_user, description="lalalala", status=Company.VERIFIED, logo=None, address=None) url = '/api/login/' response = self.client.post(url, {'username': 'dummy.login.company', 'password': 'lalala123', 'login-type': 'company'}, format='json') @@ -101,6 +101,7 @@ class RegisterTests(APITestCase): response = self.client.post(url, {'username': 'lalala'}, format='multipart') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + class ProfileUpdateTests(APITestCase): @requests_mock.Mocker() diff --git a/core/tests/test_vacancies.py b/core/tests/test_vacancies.py index a79106e84bca448cb2d50e828b05dd9475e631a6..82a1b07bb91a566814d9b7eae5ebefae8b50ae52 100644 --- a/core/tests/test_vacancies.py +++ b/core/tests/test_vacancies.py @@ -65,7 +65,7 @@ class ApplicationTests(APITestCase): student_id = response.data.get('student').get('id') new_user = User.objects.create_user('dummy.company', 'dummy.company@company.com', 'lalala123') - new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,address=None) + new_company = Company.objects.create(user=new_user, description="lalala",status=Company.VERIFIED,logo=None,address=None) new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today()) url = '/api/students/' + str(student_id) + '/applied-vacancies/' @@ -133,7 +133,7 @@ class BookmarkApplicationTests(APITestCase): student_id = response.data.get('student').get('id') new_user = User.objects.create_user('dummy.company2', 'dummy.compan2y@company.com', 'lalala123') - new_company = Company.objects.create(user=new_user, description="lalala",verified=True,logo=None,address=None) + new_company = Company.objects.create(user=new_user, description="lalala",status=Company.VERIFIED,logo=None,address=None) new_vacancy = Vacancy.objects.create(company=new_company, verified=True, open_time=datetime.fromtimestamp(0), description="lalala", close_time=datetime.today()) url = '/api/students/' + str(student_id) + '/bookmarked-vacancies/' @@ -144,6 +144,7 @@ class BookmarkApplicationTests(APITestCase): response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_200_OK) + class VacancyTest(APITestCase): def test_verified_vacancy_list(self): superuser = User.objects.create_superuser('dummy.company', 'dummy.company@company.com', 'lalala123') @@ -171,7 +172,7 @@ class CompanyListsTests(APITestCase): def test_company_vacancy_list_exist(self): new_user = User.objects.create_user('dummy.company3', 'dummy.company3@company.com', 'lalala123') - new_company = Company.objects.create(user=new_user, description="lalala", verified=True, logo=None, address=None) + new_company = Company.objects.create(user=new_user, description="lalala", status=Company.VERIFIED, logo=None, address=None) url = '/api/companies/' + str(new_company.pk) + '/vacancies' response = self.client.post(url, format='json') @@ -179,7 +180,7 @@ class CompanyListsTests(APITestCase): def test_company_application_list_exist(self): new_user = User.objects.create_user('dummy.company4', 'dummy.company4@company.com', 'lalala123') - new_company = Company.objects.create(user=new_user, description="lalala", verified=True, logo=None, + new_company = Company.objects.create(user=new_user, description="lalala", status=Company.VERIFIED, logo=None, address=None) url = '/api/companies/' + str(new_company.pk) + '/applications' diff --git a/core/views/accounts.py b/core/views/accounts.py index 225d205b46ce063419a83516533a69843f33a683..a441fc795d1a5b6383f16d4f5d4917ff8fc7286d 100644 --- a/core/views/accounts.py +++ b/core/views/accounts.py @@ -2,7 +2,6 @@ import requests from django.contrib.auth import authenticate, login from django.contrib.auth.models import User from rest_framework import viewsets, status -from rest_framework.generics import get_object_or_404 from rest_framework.decorators import list_route from rest_framework.parsers import FormParser,MultiPartParser from rest_framework.permissions import AllowAny @@ -15,6 +14,7 @@ from core.models.accounts import Student, Company, Supervisor from core.serializers.accounts import BasicUserSerializer, UserSerializer, StudentSerializer, CompanySerializer, \ SupervisorSerializer, RegisterSerializer, StudentUpdateSerializer + class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer @@ -56,6 +56,7 @@ class CompanyViewSet(viewsets.ModelViewSet): queryset = Company.objects.all() serializer_class = CompanySerializer permission_classes = [IsAdminOrSelfOrReadOnly, IsAdminOrCompany] + filter_fields = ('status',) class SupervisorViewSet(viewsets.ModelViewSet): diff --git a/core/views/vacancies.py b/core/views/vacancies.py index db0f074235eae867b89a01d7f7561817351d9a3e..354f3a84434689b87a0f4f98150c8d871e942698 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -150,6 +150,7 @@ class CompanyApplicationStatusViewSet(viewsets.GenericViewSet): class CompanyVacanciesViewSet(viewsets.GenericViewSet): queryset = Vacancy.objects.all() pagination_class = PageNumberPagination + permission_classes = [IsAdminOrCompany] def list(self, request, company_id): """ diff --git a/kape/settings.py b/kape/settings.py index 7869e068ad983022a409a9729bf00137096fae77..358fd0e13eec7312b22b336d627910cbb4b0af3b 100755 --- a/kape/settings.py +++ b/kape/settings.py @@ -37,7 +37,8 @@ INSTALLED_APPS = [ 'rest_framework', 'django_nose', 'rest_framework_swagger', - 'silk' + 'silk', + 'django_filters' ] MIDDLEWARE = [ @@ -147,7 +148,8 @@ REST_FRAMEWORK = { 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' ], 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 10 + 'PAGE_SIZE': 10, + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) } GZIP_CONTENT_TYPES = ( diff --git a/requirements.txt b/requirements.txt index 50a48df156233e4ffc530842f046e0d8c9ba01e3..aeac0dc7d6a4d7bdb0f3b35c75025e2f3046e78a 100755 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ coverage django-rest-swagger django-silk requests -requests-mock \ No newline at end of file +requests-mock +django-filter \ No newline at end of file