22f488a7 |
import React from 'react';
import Box from './Box';
import uuid from 'uuid/v4';
import {
SortableContainer,
SortableElement,
arrayMove,
} from 'react-sortable-hoc';
import DragHandle from './DragHandle';
import { FieldArray, Formik, Form as FormikForm, Field } from 'formik';
import PropTypes from 'proptypes';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import {
filter, find, flatten, identity, map, prop, propEq, reject, uniq, update
} from 'rambda';
import { insert } from 'ramda';
const allJobs = {
frontend: 'Frontend',
backend: 'Backend',
};
const nextWorkerName = (rows) =>
`${rows.length + 1}. pracovník`;
class Form extends React.PureComponent {
state = {
rows: [
{
id: uuid(),
name: 'pracovnik 1',
preempt: false,
linkedTo: null,
jobs: [
{ id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
{ id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
]
},
{
id: uuid(),
name: 'pracovnik 2',
preempt: false,
linkedTo: null,
jobs: [
{ id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
{ id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
]
}
]
};
addWorker = (setFieldValue, rows) => {
const updatedRows = [...rows, { id: uuid(), name: nextWorkerName(rows), jobs: [] } ];
setFieldValue('rows', updatedRows);
};
render() {
return (
<Formik initialValues={this.state} render={({ setFieldValue, values: { rows } }) => (
<React.Fragment>
<h1>Konfigurácia projektu</h1>
<Box flexDirection="row" marginBottom={10}>
<Box flexBasis="20%" flexShrink={0}><strong>Pracovník</strong></Box>
<Box flexBasis="60%" flexShrink={0}><strong>Úlohy</strong></Box>
<Box flexBasis="10%" flexShrink={0} align="center"><strong>Prerušenie</strong></Box>
</Box>
<FieldArray name="rows" render={arrayHelpers => (
<FormikForm>
{rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} prefix={`rows.${i}`} index={i} />)}
</FormikForm>
)} />
<Box flexDirection="row" marginBottom={10}>
<Box flexBasis="20%" flexShrink={0} onClick={() => this.addWorker(setFieldValue, rows)} style={{ cursor: 'pointer' }}>
+ Nový pracovník
</Box>
</Box>
</React.Fragment>
)}>
</Formik>
)
}
}
const SortableJob = SortableElement(
class extends React.PureComponent {
static contextTypes = {
formik: PropTypes.object
};
handleSelectChange = (type, selectedPairs) => {
const { prefix } = this.props;
const { formik: { setFieldValue } } = this.context;
const selected = map(prop('value'), selectedPairs);
setFieldValue(`${prefix}.${type}`, selected);
};
deleteJob = () => {
const { rowIndex, myIndex } = this.props;
const { formik: { setFieldValue, values: { rows } } } = this.context;
const jobs = filter(identity, update(myIndex, null, rows[rowIndex].jobs));
if (jobs.length) {
setFieldValue(`rows.${rowIndex}.jobs`, jobs);
} else {
const updatedRows = filter(identity, update(rowIndex, null, rows));
setFieldValue(`rows`, updatedRows);
}
};
render() {
const { myIndex, rowIndex, prefix, job, existingJobs: _existingJobs } = this.props;
const { formik: { values: { rows } } } = this.context;
const existingJobs = map(
value => {
const [idRow, idJob] = value.split(':');
const row = find(propEq('id', idRow), rows);
const jobName = find(propEq('id', idJob), row.jobs).name;
return ({ value, label: `${row.name} – ${jobName}` });
},
reject(
value => {
const [, idJob] = value.split(':');
return idJob === job.id;
},
_existingJobs
)
);
return (
<Box flexDirection="row" marginBottom={10} paddingBottom={10} backgroundColor="white" borderBottomColor="#1B1B3A" borderBottomWidth={1} borderStyle="solid">
<Box marginRight={10} style={{ fontSize: '1.5rem' }}>
<DragHandle />
</Box>
<Box flex={1}>
<Box width={200} marginBottom={10} flexDirection="row">
<Field component="select" name={`${prefix}.name`}>
{Object.keys(allJobs).map(key => (
<option value={key} key={key}>{allJobs[key]}</option>
))}
</Field>
<Box style={{ cursor: 'pointer' }} onClick={this.deleteJob} marginLeft={5}>✕</Box>
</Box>
<Box flexDirection="row" justifyContent="space-between" marginBottom={10}>
<Box flexBasis="30%">
<label>
Trvanie: <br />
<Field type="number" step={1} name={`${prefix}.t`} style={{ width: '100%' }} />
</label>
</Box>
<Box flexBasis="30%">
<label>
Deadline: <br />
<Field type="number" step={1} name={`${prefix}.d`} style={{ width: '100%' }} />
</label>
</Box>
<Box flexBasis="30%">
<label>
Váha: <br />
<Field type="number" min={0} max={1} step={0.1} name={`${prefix}.w`} style={{ width: '100%' }} />
</label>
</Box>
</Box>
<Box>
<Box as="label" flexDirection="row" marginBottom={5}>
<Field type="radio" name={`${prefix}.succOrAnc`} value={0} /> Bez nadväznosti
</Box>
<Box marginBottom={5}>
<Box as="label" flexDirection="row">
<Field type="radio" name={`${prefix}.succOrAnc`} value={1}/> Predchodcovia
</Box>
<Box flex={1} marginLeft={10} marginTop={5}>
<Select
disabled={false}
multi
onChange={e => this.handleSelectChange('anc', e)}
options={existingJobs}
removeSelected
value={job.anc}
/>
</Box>
</Box>
<Box>
<Box as="label" flexDirection="row">
<Field type="radio" name={`${prefix}.succOrAnc`} value={2} /> Nasledovníci
</Box>
<Box flex={1} marginLeft={10} marginTop={5}>
<Select
disabled={false}
multi
onChange={e => this.handleSelectChange('succ', e)}
options={existingJobs}
removeSelected
value={job.succ}
/>
</Box>
</Box>
</Box>
</Box>
</Box>
);
}
});
const SortableList = SortableContainer(({ jobs, existingJobs, prefix, rowIndex }) => (
<div>
{jobs.map((job, i) => (
<SortableJob
key={job.id}
index={i}
myIndex={i}
rowIndex={rowIndex}
job={job}
prefix={`${prefix}.${i}`}
existingJobs={existingJobs}
/>
))}
</div>
));
class Row extends React.PureComponent {
static contextTypes = {
formik: PropTypes.object
};
onSortEnd = ({ oldIndex, newIndex }) => {
const { prefix, index } = this.props;
const { formik: { setFieldValue, values: { rows }} } = this.context;
const updatedJobs = arrayMove(rows[index].jobs, oldIndex, newIndex);
setFieldValue(`${prefix}.jobs`, updatedJobs);
};
findExistingJobs = () => {
const { formik: { values: { rows } } } = this.context;
const existingJobs = uniq(
flatten(
map(({ id: idRow, jobs }) =>
map(({ id: idJob }) => `${idRow}:${idJob}`, jobs),
rows)
)
);
return existingJobs;
};
addJob = () => {
const { index } = this.props;
const { formik: { setFieldValue, values: { rows } } } = this.context;
const newJob = {
id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0
};
const jobs = [...rows[index].jobs, newJob];
setFieldValue(`rows.${index}.jobs`, jobs);
};
duplicate = () => {
const { id, index, name, preempt, jobs } = this.props;
const { formik: { setFieldValue, values: { rows } } } = this.context;
const jobsCopy = map(job => ({ ...job }), jobs);
const newRow = { id: uuid(), name: `${name} - kópia`, preempt, linkedTo: id, jobs: jobsCopy };
const updatedRows = insert(index + 1, newRow, rows);
setFieldValue('rows', updatedRows);
};
render() {
const { index, prefix, name, preempt, linkedTo, jobs } = this.props;
const existingJobs = this.findExistingJobs();
return (
<Box flexDirection="row" marginBottom={30}>
<Box flexBasis="20%" flexShrink={0}>{name}</Box>
<Box flexBasis="60%" flexShrink={0}>
<FieldArray render={() => (
<SortableList jobs={jobs} onSortStart={this.onSortStart} onSortEnd={this.onSortEnd} useDragHandle={true} existingJobs={existingJobs} prefix={`${prefix}.jobs`} rowIndex={index} />
)} />
<Box onClick={this.addJob} style={{ cursor: 'pointer' }}>+ Nová úloha</Box>
</Box>
<Box flexBasis="10%" flexShrink={0} alignItems="center">
<Field type="checkbox" name={`${prefix}.preempt`} />
</Box>
<Box alignItems="flex-end"><button type="button" onClick={this.duplicate}>Duplikovať a spojiť pracovníka</button></Box>
</Box>
)
}
}
export default Form; |