diff --git a/assets/css/custom.css b/assets/css/custom.css index f49838bc0f59ea2b9c01b28a4b9734c97df7ccab..952d5128d9259ef649fe13edb3b581de2d62b2f8 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -8,11 +8,6 @@ -moz-box-sizing: border-box; } -.applicant{ - margin-left: 150px; - margin-right: 150px; -} - .ui.inverted.segment.header{ background-color: #EEEEEE; color:black; diff --git a/assets/js/VacancyPage.jsx b/assets/js/VacancyPage.jsx index 551033a1c815bc24ccc5f9813dc5591b18b86ad7..9e0f4680249c4bd46866fbfbf2b462eb7f677285 100644 --- a/assets/js/VacancyPage.jsx +++ b/assets/js/VacancyPage.jsx @@ -43,7 +43,6 @@ export default class VacancyPage extends React.Component { <Pane label="Lamaran saya" > <VacancyList key={2} - status="Batal" userId={this.state.id} url={`/students/${this.state.id}/applied-vacancies/`} /> diff --git a/assets/js/__test__/components/ApplyModal-test.jsx b/assets/js/__test__/components/ApplyModal-test.jsx index f805b8dba8b9d81bfe2d2650c8f374eb41bf2ca6..f06675e173670ac571d3b40a5f81e99f731c6584 100644 --- a/assets/js/__test__/components/ApplyModal-test.jsx +++ b/assets/js/__test__/components/ApplyModal-test.jsx @@ -12,10 +12,11 @@ describe('ApplyModal', () => { it('open without problem', () => { const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <ApplyModal id={4} data={{ key: 'value' }} buttonTitle="submit" />); - + <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; }); it('change without problem', () => { diff --git a/assets/js/__test__/components/CancelModal-test.jsx b/assets/js/__test__/components/CancelModal-test.jsx index 831285bf22444338d37a0e5f6487ca1ab1bc5813..e26f1b4091ada2c3126262e4e28d9527a76384ce 100644 --- a/assets/js/__test__/components/CancelModal-test.jsx +++ b/assets/js/__test__/components/CancelModal-test.jsx @@ -1,74 +1,74 @@ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import ReactTestUtils from 'react-addons-test-utils'; -import fetchMock from 'fetch-mock'; -import CancelModal from '../../components/CancelModal'; -import Storage from '../../lib/Storage'; - -describe('CancelModal', () => { - it('renders without problem', () => { - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - expect(modalPendaftaran).to.exist; - }); - - it('open without problem', () => { - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalPendaftaran, 'Button'); - ReactTestUtils.Simulate.click(modal); - expect(modalPendaftaran.state.modalOpen).to.equal(true); - }); - - it('close without problem', () => { - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - modalPendaftaran.handleClose(); - expect(modalPendaftaran.state.modalOpen).to.equal(false); - }); - - it('remove vacancy without problem', () => { - fetchMock.delete('*', { data: 'value' }); - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - const response3 = { student: { id: 1, name: 2 } }; - Storage.set('user-data', response3); - modalPendaftaran.removeVacancy(); - expect(modalPendaftaran.state.header).to.exist; - fetchMock.restore(); - }); - - it('remove vacancy with problem', () => { - fetchMock.delete('*', 404); - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - const response3 = { student: { id: 1, name: 2 } }; - Storage.set('user-data', response3); - modalPendaftaran.removeVacancy(); - expect(modalPendaftaran.state.header).to.exist; - fetchMock.restore(); - }); - - it('confirm with problem', () => { - fetchMock.delete('*', 404); - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - modalPendaftaran.confirm(); - expect(modalPendaftaran.state.header).to.equal('Permintaan gagal'); - fetchMock.restore(); - }); - - it('render next modal without problem', () => { - const modalPendaftaran = ReactTestUtils.renderIntoDocument( - <CancelModal id={4} />); - - modalPendaftaran.state.confirmed = true; - modalPendaftaran.forceUpdate() - expect(modalPendaftaran).to.exist; - }); -}); +// /* eslint-disable no-unused-expressions */ +// import React from 'react'; +// import ReactTestUtils from 'react-addons-test-utils'; +// import fetchMock from 'fetch-mock'; +// import ConfirmationModal from '../../components/ConfirmationModal'; +// import Storage from '../../lib/Storage'; +// +// describe('ConfirmationModal', () => { +// it('renders without problem', () => { +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// expect(modalPendaftaran).to.exist; +// }); +// +// it('open without problem', () => { +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalPendaftaran, 'Button'); +// ReactTestUtils.Simulate.click(modal); +// expect(modalPendaftaran.state.modalOpen).to.equal(true); +// }); +// +// it('close without problem', () => { +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// modalPendaftaran.handleClose(); +// expect(modalPendaftaran.state.modalOpen).to.equal(false); +// }); +// +// it('remove vacancy without problem', () => { +// fetchMock.delete('*', { data: 'value' }); +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// const response3 = { student: { id: 1, name: 2 } }; +// Storage.set('user-data', response3); +// modalPendaftaran.removeVacancy(); +// expect(modalPendaftaran.state.header).to.exist; +// fetchMock.restore(); +// }); +// +// it('remove vacancy with problem', () => { +// fetchMock.delete('*', 404); +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// const response3 = { student: { id: 1, name: 2 } }; +// Storage.set('user-data', response3); +// modalPendaftaran.removeVacancy(); +// expect(modalPendaftaran.state.header).to.exist; +// fetchMock.restore(); +// }); +// +// it('confirm with problem', () => { +// fetchMock.delete('*', 404); +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// modalPendaftaran.confirm(); +// expect(modalPendaftaran.state.header).to.equal('Permintaan gagal'); +// fetchMock.restore(); +// }); +// +// it('render next modal without problem', () => { +// const modalPendaftaran = ReactTestUtils.renderIntoDocument( +// <ConfirmationModal id={4} />); +// +// modalPendaftaran.state.confirmed = true; +// modalPendaftaran.forceUpdate() +// expect(modalPendaftaran).to.exist; +// }); +// }); diff --git a/assets/js/__test__/components/ApplyConfirmationModal-test.jsx b/assets/js/__test__/components/ConfirmationModal-test.jsx similarity index 63% rename from assets/js/__test__/components/ApplyConfirmationModal-test.jsx rename to assets/js/__test__/components/ConfirmationModal-test.jsx index 7e197f730f448182847b722db650fcc7624ef7c8..7dd9ff9480920e2a9f7e9866b59a24f0432d3cb2 100644 --- a/assets/js/__test__/components/ApplyConfirmationModal-test.jsx +++ b/assets/js/__test__/components/ConfirmationModal-test.jsx @@ -1,46 +1,42 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; -import ApplyConfirmationModal from '../../components/ApplyConfirmationModal'; +import ConfirmationModal from '../../components/ConfirmationModal'; import fetchMock from 'fetch-mock'; import Storage from '../../lib/Storage'; -import Server from '../../lib/Server'; -describe('ApplyConfirmationModal', () => { +describe('ConfirmationModal', () => { it('renders without problem', () => { const applyModal = ReactTestUtils.renderIntoDocument( - <ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />, + <ConfirmationModal />, ); expect(applyModal).to.exist; }); it('test apply without problem', () => { const applyModal = ReactTestUtils.renderIntoDocument( - <ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />, + <ConfirmationModal />, ); const response = { student: { id: 1, name: 2 } }; Storage.set('user-data', response); fetchMock.post('*', { data: 'value' }); - applyModal.open(); - applyModal.handleOpen(); - applyModal.close(); + 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( - <ApplyConfirmationModal id={4} coverLetter="letter" onChangeValue={() => {}} />, + <ConfirmationModal />, ); const response = { student: { id: 1, name: 2 } }; Storage.set('user-data', response); fetchMock.post('*', 404); - applyModal.open(); - applyModal.handleOpen(); - applyModal.close(); + applyModal.open('Menghubungkan ke Server', '', '', () => {}); + applyModal.handleYes(); expect(applyModal.state.header).to.equal('Menghubungkan ke Server'); fetchMock.restore(); }); -}); \ No newline at end of file +}); diff --git a/assets/js/__test__/components/Vacancy-test.jsx b/assets/js/__test__/components/Vacancy-test.jsx index 8a08783eaa8520412da6aaaf335bef3bce6b24c3..7d99368d8b6d03d53cd547a1fe5c31d58512ed27 100644 --- a/assets/js/__test__/components/Vacancy-test.jsx +++ b/assets/js/__test__/components/Vacancy-test.jsx @@ -72,6 +72,7 @@ describe('Vacancy', () => { expect(lowongan.props.data.id).to.equal(3); Storage.set('user-data', response3); expect(lowongan.bookmark()).to.be.undefined; + fetchMock.restore(); }); it('cancel bookmarks without problem', () => { @@ -79,8 +80,49 @@ describe('Vacancy', () => { const lowongan = ReactTestUtils.renderIntoDocument( <Vacancy status="Daftar" data={response2} bookmarked={1}/>); const response3 = { student: { id: 1, name: 2 } }; + lowongan.removeVacancyApplication(); + lowongan.openConfirmationModal(); expect(lowongan.props.data.id).to.equal(3); Storage.set('user-data', response3); expect(lowongan.bookmark()).to.be.undefined; + fetchMock.restore(); + }); + + it('cancel bookmarks with problem', () => { + fetchMock.delete('*', 404); + const lowongan = ReactTestUtils.renderIntoDocument( + <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); + 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 3ecfb60898af5a895820f860fcef693582835091..7d1f6274795a3cd529a2cd78c693692bc6f6270e 100644 --- a/assets/js/__test__/components/VacancyList-test.jsx +++ b/assets/js/__test__/components/VacancyList-test.jsx @@ -15,7 +15,24 @@ describe('VacancyList', () => { }, created: '2017-03-28T07:05:47.128672Z', description: 'Lorem ipsum dolbh.', - id: 3, + id: 1, + name: 'Software Engineer', + open_time: '2017-03-28T05:55:38Z', + updated: '2017-03-28T07:34:13.122093Z', + verified: true, + }]; + + const response2 = [{ + 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: 2, name: 'Software Engineer', open_time: '2017-03-28T05:55:38Z', updated: '2017-03-28T07:34:13.122093Z', @@ -43,7 +60,7 @@ describe('VacancyList', () => { fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( <VacancyList userId={1} url="test" />); - vacancyList.updateBookmarkList(); + vacancyList.updateStatusList(); expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; fetchMock.restore(); }); @@ -54,6 +71,7 @@ describe('VacancyList', () => { <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(); }); @@ -78,7 +96,7 @@ describe('VacancyList', () => { }); it('success delete vacancy', (done) => { - fetchMock.delete('*', response); + fetchMock.delete('*', response2); fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( <VacancyList userId={1} url="test" deleteCallback={() => {}} />); diff --git a/assets/js/components/ApplyConfirmationModal.jsx b/assets/js/components/ApplyConfirmationModal.jsx deleted file mode 100644 index 6745e5ecc94cde64d6a54d11f365779750679fd4..0000000000000000000000000000000000000000 --- a/assets/js/components/ApplyConfirmationModal.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import { Modal, Button, Icon } from 'semantic-ui-react'; -import Server from '../lib/Server'; -import Storage from '../lib/Storage'; - -export default class ApplyConfirmationModal extends React.Component { - static propTypes = { - id: React.PropTypes.number.isRequired, - onChangeValue: React.PropTypes.func.isRequired, - coverLetter: React.PropTypes.string, - status: React.PropTypes.string.isRequired, - }; - - static defaultProps = { - coverLetter: '', - }; - - constructor(props) { - super(props); - /* istanbul ignore next */ - this.state = { - open: false, - header: 'Menghubungkan ke Server', - content: 'Harap menunggu, permintaan anda sedang diproses...', - }; - this.handleOpen = this.handleOpen.bind(this); - } - - open = () => this.setState({ open: true }); - close = () => this.setState({ open: false }); - - handleOpen() { - const studentId = Storage.get('user-data').student.id; - - const daftarSuccess = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n'; - const daftarFailed = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n'; - const requestData = { vacancy_id: this.props.id, cover_letter: this.props.coverLetter }; - - Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then(() => { - this.setState({ - header: 'Pendaftaran Berhasil', - content: daftarSuccess, - }); - }, () => { - this.setState({ - header: 'Pendaftaran Gagal', - content: daftarFailed, - }); - }); - } - - render() { - const { open } = this.state; - const buttonColor = this.props.status === 'Daftar' ? 'blue' : 'red'; - return ( - <Modal - open={open} - onOpen={this.open} - onClose={this.close} - size="small" - basic - trigger={<Button color={buttonColor} onClick={this.handleOpen}> {this.props.status} <Icon name="right chevron" /></Button>} - > - <Modal.Header>{this.state.header}</Modal.Header> - <Modal.Content> - <p>{this.state.content}</p> - </Modal.Content> - <Modal.Actions> - <Button icon="checkmark" color="green" content="All Done" onClick={this.props.onChangeValue} /> - </Modal.Actions> - </Modal> - ); - } -} diff --git a/assets/js/components/ApplyModal.jsx b/assets/js/components/ApplyModal.jsx index 4a06139d46404b9c0e9c1bc7cb63aea45271a48c..e061da2afeb1a456a3abbe9fa0878d925776b1bb 100644 --- a/assets/js/components/ApplyModal.jsx +++ b/assets/js/components/ApplyModal.jsx @@ -1,12 +1,13 @@ import React from 'react'; -import { Modal, Button, TextArea, Form } from 'semantic-ui-react'; -import ApplyConfirmationModal from './ApplyConfirmationModal'; +import { Icon, Modal, Button, TextArea, Form } from 'semantic-ui-react'; +import ModalAlert from './../components/ModalAlert'; export default class ApplyModal extends React.Component { static propTypes = { data: React.PropTypes.object.isRequired, - id: React.PropTypes.number.isRequired, + active: React.PropTypes.bool.isRequired, buttonTitle: React.PropTypes.string.isRequired, + apply: React.PropTypes.func.isRequired, }; constructor(props) { @@ -15,9 +16,11 @@ export default class ApplyModal extends React.Component { this.state = { modalOpen: false, coverLetter: '', + load: false, }; this.handleChange = this.handleChange.bind(this); this.handleOpen = this.handleOpen.bind(this); + this.handleApply = this.handleApply.bind(this); } handleChange(event) { @@ -32,48 +35,48 @@ export default class ApplyModal extends React.Component { modalOpen: false, }); + handleApply = () => { + this.load(); + this.props.apply(); + }; + + load = () => this.setState({ load: true }); + render = () => ( <Modal - trigger={<Button primary onClick={this.handleOpen} floated="right">{this.props.buttonTitle}</Button>} closeIcon="close" open={this.state.modalOpen} onClose={this.handleClose} > - <Modal.Header>{this.props.data.header}</Modal.Header> - <Modal.Content> - + <ModalAlert ref={(modal) => { this.modalAlert = modal; }} /> <Modal.Description> <Modal.Header> <h3> Deskripsi Lowongan </h3></Modal.Header> {this.props.data.description} </Modal.Description> - + {this.props.active && ( <div className="coverLetter"> - <div className="linkCV"> - <a> your latest CV </a> - </div> - - <h5> Write your Cover Letter </h5> - <Form > - <TextArea placeholder="Tell us more" size='big' onChange={this.handleChange}/> - </Form> - + <a> your latest CV </a> + </div> + <div> + <h5> Write your Cover Letter </h5> + <Form > + <TextArea placeholder="Tell us more" size="big" onChange={this.handleChange} /> + </Form> + </div> </div> + )} </Modal.Content> <Modal.Actions> - <ApplyConfirmationModal - id={this.props.id} - onChangeValue={this.handleClose} - coverLetter={this.state.coverLetter} - status="Daftar" - /> + <Button loading={this.state.load} color="blue" disabled={!this.props.active} onClick={this.handleApply}> + { this.props.active ? 'Daftar' : 'Sudah Terdaftar' } <Icon name="right chevron" /> + </Button> </Modal.Actions> </Modal> ) } - diff --git a/assets/js/components/CancelModal.jsx b/assets/js/components/CancelModal.jsx deleted file mode 100644 index 662847cbbf50a9f43d672032fa56e81343a88d8c..0000000000000000000000000000000000000000 --- a/assets/js/components/CancelModal.jsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { Modal, Button, Icon, Header } from 'semantic-ui-react'; -import Server from '../lib/Server'; -import Storage from '../lib/Storage'; - -export default class CancelModal extends React.Component { - - static propTypes = { - id: React.PropTypes.number.isRequired, - }; - - constructor(props) { - super(props); - /* istanbul ignore next */ - this.state = { - modalOpen: false, - confirmed: false, - header: 'Batalkan Pendaftaran?', - content: 'Aksi ini tidak dapat direka ulang. Pastikan anda benar-benar ingin membatalkan pendaftaran', - }; - this.open = this.open.bind(this); - this.confirm = this.confirm.bind(this); - this.removeVacancy = this.removeVacancy.bind(this); - } - - handleClose = () => this.setState({ - modalOpen: false, - }); - - removeVacancy() { - const studentId = Storage.get('user-data').student.id; - const batalSuccess = 'Pendaftaran anda berhasil dihapus dari sistem\n'; - const batalFailed = 'Maaf permintaan anda gagal diproses sistem. Harap ulangi pendaftaran atau hubungi administrator\n'; - - - Server.delete(`/students/${studentId}/applied-vacancies/${this.props.id}/`).then(() => { - this.setState({ - header: 'Pendaftaran Berhasil Dibatalkan', - content: batalSuccess, - confirmed: true, - }); - }, () => { - this.setState({ - header: 'Permintaan gagal', - content: batalFailed, - }); - }); - } - - confirm() { - this.setState({ - header: 'Permintaan gagal', - content: this.batalFailed, - }); - } - - open = () => this.setState({ modalOpen: true }); - - render = () => ( - <Modal - trigger={<Button floated="right">Hapus</Button>} - basic size="small" open={this.state.modalOpen} - onOpen={this.open} - > - <Header icon="trash" content={this.state.header} /> - <Modal.Content> - <p>{this.state.content}</p> - </Modal.Content> - <Modal.Actions> - <Button onClick={this.handleClose} basic color="blue" inverted> - <Icon name="remove" /> Tutup - </Button> - { !this.state.confirmed ? - (<Button onClick={this.removeVacancy} basic color="red" inverted> - <Icon name="remove" /> Hapus - </Button>) - : - null } - </Modal.Actions> - </Modal> - ); -} diff --git a/assets/js/components/ConfirmationModal.jsx b/assets/js/components/ConfirmationModal.jsx new file mode 100644 index 0000000000000000000000000000000000000000..a8a0ee47dfb2a5e0f36694a591a49cbc57233f20 --- /dev/null +++ b/assets/js/components/ConfirmationModal.jsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { Modal, Button, Icon, Header } from 'semantic-ui-react'; + +export default class ConfirmationModal extends React.Component { + + constructor(props) { + super(props); + /* istanbul ignore next */ + this.state = { + modalOpen: false, + header: '', + content: '', + icon: 'trash', + callback: () => {}, + }; + this.open = this.open.bind(this); + this.handleYes = this.handleYes.bind(this); + } + + componentWillUpdate() { + this.fixBody(); + } + + componentDidUpdate() { + this.fixBody(); + } + + fixBody = () => { + const anotherModal = document.getElementsByClassName('ui page modals').length; + if (anotherModal > 0) document.body.classList.add('scrolling', 'dimmable', 'dimmed'); + }; + + handleClose = () => this.setState({ + modalOpen: false, + }); + + handleYes = () => { this.state.callback(); this.handleClose(); } + + open = (header = this.state.header, content = this.state.content, icon = this.state.icon, callback = this.state.callback()) => { + this.setState({ modalOpen: true, header, content, callback }); + }; + + render = () => ( + <Modal + basic size="small" open={this.state.modalOpen} + onOpen={this.open} + > + <Header icon={this.state.icon} content={this.state.header} /> + <Modal.Content> + <p>{this.state.content}</p> + </Modal.Content> + <Modal.Actions> + <Button onClick={this.handleClose} basic color="red" inverted> + <Icon name="remove" /> Tidak + </Button> + <Button onClick={this.handleYes} basic color="green" inverted> + <Icon name="checkmark" /> Ya + </Button> + </Modal.Actions> + </Modal> + ); +} diff --git a/assets/js/components/ModalAlert.jsx b/assets/js/components/ModalAlert.jsx index 10b7e0e6f0e709e271277fb093a2042001658355..5cf19450611081a01b96c11e7c1f3c923360e1ab 100644 --- a/assets/js/components/ModalAlert.jsx +++ b/assets/js/components/ModalAlert.jsx @@ -5,7 +5,7 @@ export default class ModalAlert extends React.Component { constructor(props) { super(props); /* istanbul ignore next */ - this.state = { open: false, header: '', content: '' }; + this.state = { open: false, header: '', content: '', callback: () => {} }; this.open = this.open.bind(this); this.close = this.close.bind(this); } @@ -23,12 +23,13 @@ export default class ModalAlert extends React.Component { if (anotherModal > 0) document.body.classList.add('scrolling', 'dimmable', 'dimmed'); }; - open = (header = this.state.header, content = this.state.content) => { - this.setState({ open: true, header, content }); + open = (header = this.state.header, content = this.state.content, callback = this.state.callback) => { + this.setState({ open: true, header, content, callback }); }; close = () => { this.setState({ open: false }); + this.state.callback(); }; render = () => { diff --git a/assets/js/components/Vacancy.jsx b/assets/js/components/Vacancy.jsx index 65eff411bd82965f92536858560a83590cdaf9cf..80069414adf64f8b7e48199ca05bf4ee409efd77 100644 --- a/assets/js/components/Vacancy.jsx +++ b/assets/js/components/Vacancy.jsx @@ -1,14 +1,15 @@ import React from 'react'; -import { Item, Rating } from 'semantic-ui-react'; +import { Item, Rating, Button } from 'semantic-ui-react'; import ApplyModal from './ApplyModal'; -import Storage from '../lib/Storage'; import Server from '../lib/Server'; -import CancelModal from './CancelModal'; +import ConfirmationModal from './ConfirmationModal'; +import ModalAlert from './ModalAlert'; const defaultImage = 'http://semantic-ui.com/images/wireframe/image.png'; export default class Vacancy extends React.Component { static propTypes = { + studentId: React.PropTypes.number.isRequired, data: React.PropTypes.object.isRequired, bookmarked: React.PropTypes.number, status: React.PropTypes.string.isRequired, @@ -21,35 +22,73 @@ export default class Vacancy extends React.Component { constructor(props) { super(props); /* istanbul ignore next */ - this.state = { bookmarked: this.props.bookmarked }; + this.state = { + bookmarked: this.props.bookmarked, + status: this.props.status, + deleteLoading: false + }; this.bookmark = this.bookmark.bind(this); this.generateAction = this.generateAction.bind(this); + this.openConfirmationModal = this.openConfirmationModal.bind(this); + this.removeVacancyApplication = this.removeVacancyApplication.bind(this); } bookmark() { - const studentId = Storage.get('user-data').student.id; const data = { vacancy_id: this.props.data.id }; if (this.state.bookmarked < 1) { - Server.post(`/students/${studentId}/bookmarked-vacancies/`, data); + Server.post(`/students/${this.props.studentId}/bookmarked-vacancies/`, data); } else { - Server.delete(`/students/${studentId}/bookmarked-vacancies/${this.props.data.id}/`); + Server.delete(`/students/${this.props.studentId}/bookmarked-vacancies/${this.props.data.id}/`); } this.state.bookmarked = 1 - this.state.bookmarked; } + removeVacancyApplication() { + this.setState({ deleteLoading: true }); + Server.delete(`/students/${this.props.studentId}/applied-vacancies/${this.props.data.id}/`).then(() => { + this.modalAlert.open('Pendaftaran Berhasil Dibatalkan', 'Pendaftaran anda berhasil dihapus dari sistem\n'); + this.setState({ status: 'new' }); + }, () => { + this.modalAlert.open('Permintaan Gagal', 'Maaf permintaan anda gagal diproses sistem. Harap ulangi pendaftaran atau hubungi administrator\n'); + }); + } + + openConfirmationModal() { + this.confirmationModal.open( + 'Batalkan Pendaftaran?', + 'Aksi ini tidak dapat direka ulang. Pastikan anda benar-benar ingin membatalkan pendaftaran', + 'trash', + this.removeVacancyApplication, + ); + } + + 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 - id={this.props.data.id} data={{ header: this.props.data.name, - description: this.props.data.description }} buttonTitle="Detail" + active={this.state.status === 'new'} + data={{ header: this.props.data.name, description: this.props.data.description }} + buttonTitle="Detail" + apply={ this.apply } />); - const cancelModal = <CancelModal id={this.props.data.id} />; - return this.props.status === 'Daftar' ? applyModal : cancelModal; + const cancelButton = <Button loading={this.state.deleteLoading} floated="right" color="red" onClick={this.openConfirmationModal}>Batal</Button>; + return this.state.status === 'new' ? applyModal : cancelButton; } render() { return ( <Item className="applicantItems"> + <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.Extra> diff --git a/assets/js/components/VacancyList.jsx b/assets/js/components/VacancyList.jsx index e3603949dae305e277197f0760dc9560c4f2c1b6..363b0a3ccef3ae18e5c3e448bb7d8d73440d18ea 100644 --- a/assets/js/components/VacancyList.jsx +++ b/assets/js/components/VacancyList.jsx @@ -12,12 +12,10 @@ export default class VacancyList extends React.Component { static propTypes = { url: React.PropTypes.string.isRequired, userId: React.PropTypes.number.isRequired, - status: React.PropTypes.string, type: React.PropTypes.string, }; static defaultProps = { - status: 'Daftar', type: 'student', }; @@ -25,15 +23,16 @@ export default class VacancyList extends React.Component { super(props); /* istanbul ignore next */ if (this.props.type === 'student') { - this.updateBookmarkList(); + this.updateStatusList(); } - this.state = { vacancies: [], bookmarkList: [] }; + this.state = { vacancies: [], bookmarkList: [], appliedList: [] }; Server.get(this.props.url, false).then((data) => { this.setState({ vacancies: data }); }); - this.updateBookmarkList = this.updateBookmarkList.bind(this); + this.updateStatusList = this.updateStatusList.bind(this); this.generateVacancies = this.generateVacancies.bind(this); this.checkBookmark = this.checkBookmark.bind(this); + this.checkApplied = this.checkApplied.bind(this); this.companyHeader = this.companyHeader.bind(this); } @@ -44,10 +43,20 @@ export default class VacancyList extends React.Component { return 0; } - updateBookmarkList() { + checkApplied(id) { + for (let i = 0; i < this.state.appliedList.length; i += 1) { + if (id === this.state.appliedList[i].id) { return 'registered'; } + } + return 'new'; + } + + updateStatusList() { Server.get(`/students/${this.props.userId}/bookmarked-vacancies/`, false).then((data) => { this.setState({ bookmarkList: data }); }); + Server.get(`/students/${this.props.userId}/applied-vacancies/`, false).then((data) => { + this.setState({ appliedList: data }); + }); } deleteVacancy = id => Server.delete(`/vacancies/${id}/`, this.state).then(() => { @@ -65,9 +74,10 @@ export default class VacancyList extends React.Component { ( <Vacancy key={vacancy.id} - status={this.props.status} + status={this.checkApplied(vacancy.id)} bookmarked={this.checkBookmark(vacancy.id)} data={vacancy} + studentId={this.props.userId} /> ), );