From 6299bee8ddeedd8649a3c27f7d92c6bd1e204183 Mon Sep 17 00:00:00 2001 From: Zamil Majdy <z.majdy1996@gmail.com> Date: Mon, 24 Apr 2017 20:35:52 +0700 Subject: [PATCH] [#140652771] [#21] [Refactor] fix update vacancy bug + add testcases --- assets/css/custom.css | 25 +++------- assets/js/CreateVacancy.jsx | 34 +++++++++++-- assets/js/VacancyPage.jsx | 2 +- assets/js/__test__/CreateVacancy-test.jsx | 61 +++++++++++++++++++++-- assets/js/components/CompanyVacancy.jsx | 19 +++++-- assets/js/components/VacancyList.jsx | 16 +++--- assets/js/index.jsx | 1 + assets/js/lib/Server.jsx | 2 +- core/views/vacancies.py | 4 +- 9 files changed, 121 insertions(+), 43 deletions(-) diff --git a/assets/css/custom.css b/assets/css/custom.css index 706d9ddf..395990f1 100755 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,5 +1,3 @@ - - .center{ text-align: center; } @@ -26,10 +24,7 @@ background-color: #EEEEEE; } .create-lowongan{ - margin-top: 60px; - margin-left: 19%; - margin-right: 19%; - margin-bottom: 100px; + margin: 60px 19% 100px; } .ui.segment.form-segment{ @@ -37,13 +32,10 @@ background-color: #EEEEEE; } .tabs { - margin-top: 100px; - margin-left: 100px; - margin-right: 100px; background: #fff; border: 1px solid #e5e5e5; border-radius: 3px; - margin-bottom: 30px; + margin: 100px 100px 30px; } .tabs__labels { margin: 0; @@ -67,10 +59,10 @@ background-color: #EEEEEE; } .halamanLogin{ - background-image: url("../img/bw-blur.png"); + width: 1000px; + height: 646px; background-size: cover; - background-position: center; - background-attachment: fixed; + background: url("../img/bw-blur.png") fixed center; padding-bottom: 80px; } @@ -80,9 +72,8 @@ background-color: #EEEEEE; } .formLogin{ -margin: 0 auto; -margin-top: 80px; -width: 430px; + margin: 80px auto 0; + width: 430px; } .registerModal{ @@ -171,7 +162,7 @@ card .formRegis{ margin-top: 100px; } -itemLowongan{ +.itemLowongan{ color: black; } diff --git a/assets/js/CreateVacancy.jsx b/assets/js/CreateVacancy.jsx index 8989c3ba..e5015d6f 100644 --- a/assets/js/CreateVacancy.jsx +++ b/assets/js/CreateVacancy.jsx @@ -10,17 +10,34 @@ import Dumper from './lib/Dumper'; export default class CreateVacancy extends React.Component { + static propTypes = { + params: React.PropTypes.object.isRequired, + }; + constructor(props) { super(props); /* istanbul ignore next */ this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); + this.state = { formLoading: false, company: Storage.get('user-data').company, + vacancyId: this.props.params.id, open_time: moment(), close_time: moment(), + name: '', + description: '', }; + + this.state.vacancyId && Server.get(`/vacancies/${this.state.vacancyId}/`).then((r) => { + this.setState({ + description: r.description, + name: r.name, + open_time: moment(r.open_time), + close_time: moment(r.close_time), + }); + }); } handleChange = (e) => { @@ -36,10 +53,13 @@ export default class CreateVacancy extends React.Component { data.close_time = data.close_time.format(); data.company = this.state.company.id; - Server.post('/vacancies/', data).then(() => { + const url = this.state.vacancyId ? `/vacancies/${this.state.vacancyId}/` : '/vacancies/'; + const method = this.state.vacancyId ? 'PATCH' : 'POST'; + + 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', Dumper.dump(r, ' ')); this.setState({ formLoading: false }); })); }; @@ -55,8 +75,14 @@ export default class CreateVacancy extends React.Component { </Header.Content> </Header> <Form loading={this.state.formLoading} onSubmit={this.handleSubmit}> - <Form.Field label="Posisi" name="name" control={Input} onChange={this.handleChange} required /> - <Form.TextArea label="Deskripsi" placeholder="Deskripsi Lowongan..." required /> + <Form.Field label="Posisi" name="name" control={Input} onChange={this.handleChange} value={this.state.name} required /> + <Form.TextArea + name="description" + label="Deskripsi" + placeholder="Deskripsi Lowongan..." + onChange={this.handleChange} + value={this.state.description} required + /> <Form.Group widths="equal"> <Form.Field className="open-time-field" diff --git a/assets/js/VacancyPage.jsx b/assets/js/VacancyPage.jsx index 1a9383a7..551033a1 100644 --- a/assets/js/VacancyPage.jsx +++ b/assets/js/VacancyPage.jsx @@ -59,7 +59,7 @@ export default class VacancyPage extends React.Component { ); } else if (this.state.role === 'company') { return ( - <VacancyList key={1} userId={this.state.id} url="/vacancies/?verified=false" type="company" /> + <VacancyList key={1} userId={this.state.id} url={`/companies/${this.state.id}/vacancies/`} type="company" /> ); } diff --git a/assets/js/__test__/CreateVacancy-test.jsx b/assets/js/__test__/CreateVacancy-test.jsx index d85e10ee..82c28816 100644 --- a/assets/js/__test__/CreateVacancy-test.jsx +++ b/assets/js/__test__/CreateVacancy-test.jsx @@ -41,26 +41,31 @@ describe('CreateVacancy', () => { }; it('renders for companies without problem', () => { + fetchMock.get('*', {}); Storage.set('user-data', companySession); const createVacancy = ReactTestUtils.renderIntoDocument( - <CreateVacancy />, + <CreateVacancy params={{ id: 1 }} />, ); expect(createVacancy).to.exist; + fetchMock.restore(); }); it('renders without problem for error case', () => { + fetchMock.get('*', {}); Storage.set('user-data', errorSession); const createVacancy = ReactTestUtils.renderIntoDocument( - <CreateVacancy />, + <CreateVacancy params={{ id: 1 }} />, ); expect(createVacancy).to.exist; + fetchMock.restore(); }); it('support handle change', () => { + fetchMock.get('*', {}); Storage.set('user-data', companySession); const createVacancy = ReactTestUtils.renderIntoDocument( - <CreateVacancy />, + <CreateVacancy params={{ id: 1 }} />, ); createVacancy.setState({ name: 'stub', @@ -68,13 +73,15 @@ describe('CreateVacancy', () => { }); createVacancy.handleChange({ target: { name: 'test', value: 'hue' } }); expect(createVacancy.state.test).to.equal('hue'); + fetchMock.restore(); }); it('submit vacancy properly (loading)', () => { fetchMock.post('*', 404); + fetchMock.get('*', {}); Storage.set('user-data', companySession); const createVacancy = ReactTestUtils.renderIntoDocument( - <CreateVacancy />, + <CreateVacancy params={{ id: undefined }} />, ); createVacancy.setState({ name: 'stub', @@ -96,9 +103,53 @@ describe('CreateVacancy', () => { it('submit vacancy properly (success)', () => { fetchMock.post('*', { data: 'value' }); + fetchMock.get('*', {}); + Storage.set('user-data', companySession); + const createVacancy = ReactTestUtils.renderIntoDocument( + <CreateVacancy params={{ id: undefined }} />, + ); + createVacancy.setState({ + name: 'stub', + description: 'stub', + }); + + expect(createVacancy.state.formLoading).to.equal(false); + createVacancy.handleSubmit(new Event('click')); + expect(createVacancy.state.formLoading).to.equal(true); + fetchMock.restore(); + }); + + it('submit vacancy properly (loading)', () => { + fetchMock.patch('*', 404); + fetchMock.get('*', {}); + Storage.set('user-data', companySession); + const createVacancy = ReactTestUtils.renderIntoDocument( + <CreateVacancy params={{ id: 1 }} />, + ); + createVacancy.setState({ + name: 'stub', + description: 'stub', + }); + + const openField = ReactTestUtils.findRenderedDOMComponentWithClass(createVacancy, 'open-time-field'); + const closeField = ReactTestUtils.findRenderedDOMComponentWithClass(createVacancy, 'close-time-field'); + ReactTestUtils.Simulate.click(openField); + ReactTestUtils.Simulate.keyDown(openField, { key: 'Enter', keyCode: 13, which: 13 }); + ReactTestUtils.Simulate.click(closeField); + ReactTestUtils.Simulate.keyDown(closeField, { key: 'Enter', keyCode: 13, which: 13 }); + + expect(createVacancy.state.formLoading).to.equal(false); + createVacancy.handleSubmit(new Event('click')); + expect(createVacancy.state.formLoading).to.equal(true); + fetchMock.restore(); + }); + + it('submit vacancy properly (success)', () => { + fetchMock.patch('*', { data: 'value' }); + fetchMock.get('*', {}); Storage.set('user-data', companySession); const createVacancy = ReactTestUtils.renderIntoDocument( - <CreateVacancy />, + <CreateVacancy params={{ id: 1 }} />, ); createVacancy.setState({ name: 'stub', diff --git a/assets/js/components/CompanyVacancy.jsx b/assets/js/components/CompanyVacancy.jsx index d5be3a71..57b1ad8f 100644 --- a/assets/js/components/CompanyVacancy.jsx +++ b/assets/js/components/CompanyVacancy.jsx @@ -1,5 +1,7 @@ import React from 'react'; -import { Segment, Button, Icon, Item } from 'semantic-ui-react'; +import moment from 'moment'; +import { Button, Icon, Item } from 'semantic-ui-react'; +import { Link } from 'react-router'; const defaultImage = 'http://semantic-ui.com/images/wireframe/image.png'; @@ -8,6 +10,13 @@ export default class ApplicantList extends React.Component { data: React.PropTypes.object.isRequired, }; + constructor(props) { + super(props); + moment.locale('id'); + } + + getLink = `/buat-lowongan/${this.props.data.id}`; + render() { return ( <Item className="applicantItems"> @@ -15,13 +24,13 @@ export default class ApplicantList extends React.Component { <Item.Content> <Item.Header as="a">{this.props.data.name}</Item.Header> - <Segment padded basic floated="right"> - <Button primary floated="right" >Ubah<Icon name="right chevron" /></Button> - <Segment basic> 5 jam lalu</Segment> - </Segment> + <Button color="blue" floated="right" as={Link} to={this.getLink}> + Ubah <Icon name="right chevron" /> + </Button> <Item.Extra> <h3> 105 Pendaftar </h3> + Ditutup {moment(moment(this.props.data.close_time)).fromNow()} </Item.Extra> </Item.Content> diff --git a/assets/js/components/VacancyList.jsx b/assets/js/components/VacancyList.jsx index 5e479049..5f57b217 100644 --- a/assets/js/components/VacancyList.jsx +++ b/assets/js/components/VacancyList.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Item, Button } from 'semantic-ui-react'; +import { Item, Button, Grid } from 'semantic-ui-react'; import { Link } from 'react-router'; import Vacancy from './Vacancy'; import CompanyVacancy from './CompanyVacancy'; @@ -73,10 +73,10 @@ export default class VacancyList extends React.Component { companyHeader() { if (this.props.type === 'company') { return ( - <div> - <Button icon="eye" secondary labelPosition="left" content="Lihat Semua Pendaftar" /> - <Button as={Link} to="/buat-lowongan" icon="add" secondary labelPosition="left" content="Tambah Lowongan Baru" /> - </div> + <Grid style={{ padding: '10px' }}> + <Button fluid size="mini" icon="eye" labelPosition="left" color="facebook" content="Lihat Semua Pendaftar" /> + <Button fluid size="mini" as={Link} to="/buat-lowongan" icon="add" labelPosition="left" content="Tambah Lowongan Baru" color="teal" /> + </Grid> ); } @@ -84,11 +84,11 @@ export default class VacancyList extends React.Component { } render = () => ( - <div> + <Grid container columns="eleven" doubling> { this.companyHeader() } - <Item.Group relaxed> + <Item.Group relaxed style={{ width: '100%' }}> { this.generateVacancies() } </Item.Group> - </div> + </Grid> ); } diff --git a/assets/js/index.jsx b/assets/js/index.jsx index 0636c719..127d2917 100644 --- a/assets/js/index.jsx +++ b/assets/js/index.jsx @@ -39,6 +39,7 @@ export default class App extends React.Component { <Route path="/lowongan" component={VacancyPage} /> <Route path="/perusahaan" component={HomeCompany} /> <Route path="/buat-lowongan" component={CreateVacancy} /> + <Route path="/buat-lowongan/:id" component={CreateVacancy} /> </Route> <Route path="/home" onEnter={this.handleHome} /> <Redirect from="*" to="/home" /> diff --git a/assets/js/lib/Server.jsx b/assets/js/lib/Server.jsx index bf53cf6f..030bba1c 100644 --- a/assets/js/lib/Server.jsx +++ b/assets/js/lib/Server.jsx @@ -76,7 +76,7 @@ export default class Server { }); } - static get(path, useCache = true) { + static get(path, useCache = false) { return (useCache && Storage.get(path)) ? Promise.resolve(Storage.get(path)) : this.sendRequest(path, 'GET', null, useCache); } diff --git a/core/views/vacancies.py b/core/views/vacancies.py index 4a257966..ff761057 100644 --- a/core/views/vacancies.py +++ b/core/views/vacancies.py @@ -96,8 +96,8 @@ class CompanyVacanciesViewSet(viewsets.GenericViewSet): Get list of company {company_id}'s vacancies --- """ - company = get_object_or_404(Company.objects.all(), pk=company_id) - vacancies = Vacancy.objects.filter(company = company) + company = get_object_or_404(Company.objects.all().order_by('-updated'), pk=company_id) + vacancies = Vacancy.objects.filter(company=company) return Response(VacancySerializer(vacancies, many=True, context={'request': request}).data) -- GitLab