Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension buttons #22

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion js/components/HelpIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import React from 'react';
import {OverlayTrigger, Tooltip} from 'react-bootstrap';
import PropTypes from "prop-types";
import {FaQuestionCircle} from 'react-icons/fa';
import {FaEnvelope, FaQuestionCircle} from 'react-icons/fa';
import {FaCheck} from 'react-icons/fa';
import {FaTimes} from 'react-icons/fa';
import {FaTasks} from "react-icons/fa";

const HelpIcon = (props) => {
const tooltip = <Tooltip id='help-tooltip'>{props.text}</Tooltip>;
Expand All @@ -18,6 +19,10 @@ const HelpIcon = (props) => {
return <FaCheck className={'ok-icon ' + props.className}/>;
case "remove":
return <FaTimes className={'remove-icon ' + props.className}/>;
case "to-do":
return <FaTasks className={'to-do-icon ' + props.className}/>;
case "envelope":
return <FaEnvelope className={'publish-icon' + props.className}/>
default:
return null;
}
Expand Down
56 changes: 39 additions & 17 deletions js/components/record/Record.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import HorizontalInput from "../HorizontalInput";
import RecordForm from "./RecordForm";
import RecordProvenance from "./RecordProvenance";
import RequiredAttributes from "./RequiredAttributes";
import {ACTION_STATUS, ALERT_TYPES, ROLE} from "../../constants/DefaultConstants";
import {ACTION_STATUS, ALERT_TYPES, EXTENSION_CONSTANTS, RECORD_PHASE, ROLE} from "../../constants/DefaultConstants";
import AlertMessage from "../AlertMessage";
import {LoaderCard, LoaderSmall} from "../Loader";
import {processTypeaheadOptions} from "./TypeaheadAnswer";
import {EXTENSIONS} from "../../../config";

class Record extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -56,25 +57,25 @@ class Record extends React.Component {
}

return <div className={"record"}>
<form>
<RequiredAttributes record={record} onChange={this._onChange}
formTemplate={formTemplate}
currentUser={currentUser}
completed={record.state.isComplete()}/>
{this._showInstitution() && this._renderInstitution()}
<RecordProvenance record={record}/>
</form>
{this._renderForm()}
{this._renderButtons()}
{showAlert && recordSaved.status === ACTION_STATUS.ERROR &&
<form>
<RequiredAttributes record={record} onChange={this._onChange}
formTemplate={formTemplate}
currentUser={currentUser}
completed={record.state.isComplete()}/>
{this._showInstitution() && this._renderInstitution()}
<RecordProvenance record={record}/>
</form>
{this._renderForm()}
{this._renderButtons()}
{showAlert && recordSaved.status === ACTION_STATUS.ERROR &&
<div>
<AlertMessage type={ALERT_TYPES.DANGER}
message={this.props.formatMessage('record.save-error', {error: this.i18n(recordSaved.error.messageId)})}/>
<br/>
</div>}
{showAlert && recordSaved.status === ACTION_STATUS.SUCCESS &&
{showAlert && recordSaved.status === ACTION_STATUS.SUCCESS &&
<div><AlertMessage type={ALERT_TYPES.SUCCESS} message={this.i18n('record.save-success')}/><br/></div>}
</div>;
</div>;
}

_renderHeader() {
Expand Down Expand Up @@ -105,13 +106,34 @@ class Record extends React.Component {
const {record, recordSaved, formgen} = this.props;

return <div className="mt-3 text-center">
<Button variant='success' size='sm'
{record.phase === RECORD_PHASE.COMPLETED
|| EXTENSIONS === EXTENSION_CONSTANTS.OPERATOR
&& <Button className="mx-1" variant='danger' size='sm'
disabled={formgen.status === ACTION_STATUS.PENDING || recordSaved.status === ACTION_STATUS.PENDING
|| !this.state.isFormValid || !record.state.isComplete() || record.phase === RECORD_PHASE.REJECTED}
onClick={this.props.handlers.onReject}>
{this.i18n('reject')}{recordSaved.status === ACTION_STATUS.PENDING && <LoaderSmall/>}
</Button>}

{record.phase === RECORD_PHASE.COMPLETED
|| record.phase === RECORD_PHASE.PUBLISHED
&& <Button className="mx-1" variant='success' size='sm'
disabled={formgen.status === ACTION_STATUS.PENDING || recordSaved.status === ACTION_STATUS.PENDING
|| !this.state.isFormValid || !record.state.isComplete() || record.phase === RECORD_PHASE.COMPLETED}
onClick={this.props.handlers.onComplete}>
{this.i18n('complete')}{recordSaved.status === ACTION_STATUS.PENDING && <LoaderSmall/>}
</Button>}

<Button className="mx-1" variant='success' size='sm'
disabled={formgen.status === ACTION_STATUS.PENDING || recordSaved.status === ACTION_STATUS.PENDING
|| !this.state.isFormValid || !record.state.isComplete()}
|| !this.state.isFormValid || !record.state.isComplete()}
hidden={(record.phase === RECORD_PHASE.COMPLETED && !this._isAdmin()) ||
record.phase === RECORD_PHASE.PUBLISHED}
onClick={this.props.handlers.onSave}>
{this.i18n('save')}{recordSaved.status === ACTION_STATUS.PENDING && <LoaderSmall/>}
</Button>
<Button variant='link' size='sm'
<Button className="mx-1" variant='link' size='sm'
hidden={record.phase === RECORD_PHASE.COMPLETED || record.phase === RECORD_PHASE.PUBLISHED}
onClick={this.props.handlers.onCancel}>{this.i18n('cancel')}</Button>
</div>
}
Expand Down
27 changes: 25 additions & 2 deletions js/components/record/RecordController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import withI18n from '../../i18n/withI18n';
import Record from './Record';
import Routes from "../../constants/RoutesConstants";
import {transitionToWithOpts} from '../../utils/Routing';
import {ACTION_FLAG, ACTION_STATUS} from "../../constants/DefaultConstants";
import {ACTION_FLAG, ACTION_STATUS, RECORD_PHASE} from "../../constants/DefaultConstants";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import {
Expand Down Expand Up @@ -126,6 +126,27 @@ class RecordController extends React.Component {
this.setState({record: update});
};

_onComplete = () => {
this._handlePhaseChange(RECORD_PHASE.COMPLETED);
};

_onReject = () => {
this._handlePhaseChange(RECORD_PHASE.REJECTED);
};

_handlePhaseChange = (newPhase) => {
const currentUser = this.props.currentUser;

this.setState((prevState) => {
const update = {...prevState.record};
update.phase = newPhase;
return {record: update};
}, () => {
const updatedRecord = this.state.record;
this.props.updateRecord(updatedRecord, currentUser);
});
};

_getLocalName() {
if (EXTENSIONS.split(",").includes("kodi")) { // return name of the record based on answer of specific question
return this._getKodiLocaLName();
Expand Down Expand Up @@ -158,7 +179,9 @@ class RecordController extends React.Component {
const handlers = {
onSave: this._onSave,
onCancel: this._onCancel,
onChange: this._onChange
onChange: this._onChange,
onComplete: this._onComplete,
onReject: this._onReject
};

return <Record
Expand Down
48 changes: 36 additions & 12 deletions js/components/record/RecordRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,50 @@ import HelpIcon from "../HelpIcon";
import {Button} from "react-bootstrap";
import {injectIntl} from "react-intl";
import withI18n from "../../i18n/withI18n";
import RecordValidator from "../../validation/RecordValidator";
import {LoaderSmall} from "../Loader";
import PropTypes from "prop-types";
import {ROLE} from "../../constants/DefaultConstants";
import {RECORD_PHASE, ROLE} from "../../constants/DefaultConstants";

let RecordRow = (props) => {
const record = props.record,
formTemplateOptions = props.formTemplateOptions,
isComplete = RecordValidator.isComplete(record),
completionTooltip = props.i18n(isComplete ? 'records.completion-status-tooltip.complete' : 'records.completion-status-tooltip.incomplete'),
recordPhase = props.record.phase,
isAdmin = props.currentUser.role === ROLE.ADMIN,
deleteButton = props.disableDelete ? null :
<Button variant='warning' size='sm' title={props.i18n('records.delete-tooltip')}
onClick={() => props.onDelete(record)}>{props.i18n('delete')}{props.deletionLoading &&
<LoaderSmall/>}</Button>;

const getGlyph = () => {
switch (recordPhase) {
case RECORD_PHASE.OPEN:
return 'to-do';
case RECORD_PHASE.COMPLETED:
return 'ok';
case RECORD_PHASE.PUBLISHED:
return 'envelope';
case RECORD_PHASE.REJECTED:
return 'remove';
default:
return '';
}
};

const getCompletionStatusTooltip = () => {
switch (recordPhase) {
case RECORD_PHASE.COMPLETED:
return props.i18n('records.completion-status-tooltip.complete');
case RECORD_PHASE.OPEN:
return props.i18n('records.completion-status-tooltip.incomplete');
case RECORD_PHASE.REJECTED:
return props.i18n('records.completion-status-tooltip.rejected');
case RECORD_PHASE.PUBLISHED:
return props.i18n('records.completion-status-tooltip.published');
default:
return "";
}
};

return <tr>
{isAdmin &&
<td className='report-row'>
Expand All @@ -42,11 +70,10 @@ let RecordRow = (props) => {
<td className='report-row content-center'>
{formatDate(new Date(record.lastModified ? record.lastModified : record.dateCreated))}
</td>
{ isAdmin &&
<td className='report-row content-center'>
<HelpIcon text={completionTooltip} glyph={isComplete ? 'ok' : 'remove'}/>
</td>
}
<td className='report-row content-center'>
<HelpIcon text={getCompletionStatusTooltip()} glyph={getGlyph()}/>
</td>

<td className='report-row actions'>
<Button variant='primary' size='sm' title={props.i18n('records.open-tooltip')}
onClick={() => props.onEdit(record)}>{props.i18n('open')}</Button>
Expand All @@ -55,9 +82,6 @@ let RecordRow = (props) => {
</tr>
};

const isAdvancedView = {}


const getFormTemplateOptionName = (formTemplate, formTemplatesOptions) => {
if (!formTemplate) {
return "";
Expand Down
4 changes: 1 addition & 3 deletions js/components/record/RecordTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ class RecordTable extends React.Component {
&& <th className='w-25 content-center'>{this.i18n('records.form-template')}</th>
}
<th className='w-25 content-center'>{this.i18n('records.last-modified')}</th>
{(this._isAdmin())
&& <th className='w-15 content-center'>{this.i18n('records.completion-status')}</th>
}
<th className='w-15 content-center'>{this.i18n('records.completion-status')}</th>
<th className='w-20 content-center'>{this.i18n('actions')}</th>
</tr>
</thead>
Expand Down
20 changes: 15 additions & 5 deletions js/components/record/Records.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {Button, Card} from "react-bootstrap";
import {injectIntl} from "react-intl";
import withI18n from "../../i18n/withI18n";
import RecordTable from "./RecordTable";
import {ACTION_STATUS, ALERT_TYPES, ROLE} from "../../constants/DefaultConstants";
import {ACTION_STATUS, ALERT_TYPES, EXTENSION_CONSTANTS, ROLE} from "../../constants/DefaultConstants";
import AlertMessage from "../AlertMessage";
import {LoaderSmall} from "../Loader";
import PropTypes from "prop-types";
import {processTypeaheadOptions} from "./TypeaheadAnswer";
import {EXTENSIONS} from "../../../config";

const STUDY_CLOSED_FOR_ADDITION = false;
const STUDY_CREATE_AT_MOST_ONE_RECORD = false;
Expand All @@ -36,6 +37,9 @@ class Records extends React.Component {
const showCreateButton = STUDY_CREATE_AT_MOST_ONE_RECORD
? (!this.props.recordsLoaded.records || (this.props.recordsLoaded.records.length < 1))
: true;
const showPublishButton =
this.props.currentUser.role === ROLE.ADMIN
&& EXTENSIONS === EXTENSION_CONSTANTS.OPERATOR;
const showExportButton = !!recordsLoaded.records;
const createRecordDisabled =
STUDY_CLOSED_FOR_ADDITION
Expand All @@ -56,14 +60,20 @@ class Records extends React.Component {
<RecordTable {...this.props}/>
<div className="d-flex justify-content-between">
{showCreateButton
? <Button variant='primary' size='sm'
disabled={createRecordDisabled}
title={createRecordTooltip}
onClick={onCreateWithFormTemplate}>{this.i18n('records.create-tile')}</Button>
? <Button className="mx-1" variant='primary' size='sm'
disabled={createRecordDisabled}
title={createRecordTooltip}
onClick={onCreateWithFormTemplate}>{this.i18n('records.create-tile')}</Button>
: null}
{showExportButton ?
<Button className="mx-1" variant='primary' size='sm'>{this.i18n('export')}</Button>
: null}
{showPublishButton ?
<Button className="mx-1" variant='success' size='sm'
onClick={this.props.handlers.onPublish}>
{this.i18n('publish')}
</Button>
: null}
</div>
{showAlert && recordDeleted.status === ACTION_STATUS.ERROR &&
<AlertMessage type={ALERT_TYPES.DANGER}
Expand Down
26 changes: 24 additions & 2 deletions js/components/record/RecordsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import {injectIntl} from "react-intl";
import withI18n from "../../i18n/withI18n";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import {deleteRecord} from "../../actions/RecordActions";
import {deleteRecord, updateRecord} from "../../actions/RecordActions";
import {loadFormTemplates} from "../../actions/FormTemplatesActions";
import {extractQueryParam} from "../../utils/Utils"
import {RECORD_PHASE} from "../../constants/DefaultConstants";

class RecordsController extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -53,6 +54,25 @@ class RecordsController extends React.Component {
this.setState({showAlert: true});
};

_onPublishRecords = async () => {
const currentUser = this.props.currentUser;

this.setState({
records: this.props.recordsLoaded.records
}, async () => {
const updatedRecords = this.state.records.map(async (record) => {
if (record.phase === RECORD_PHASE.COMPLETED) {
const updatedRecord = {...record, phase: RECORD_PHASE.PUBLISHED};
await this.props.updateRecord(updatedRecord, currentUser);
return updatedRecord;
}
});

return await Promise.all(updatedRecords);
})

};

render() {
const {formTemplatesLoaded, recordsLoaded, recordDeleted, recordsDeleting, currentUser} = this.props;
const formTemplate = extractQueryParam(this.props.location.search, "formTemplate");
Expand All @@ -62,7 +82,8 @@ class RecordsController extends React.Component {
const handlers = {
onEdit: this._onEditRecord,
onCreate: this._onAddRecord,
onDelete: this._onDeleteRecord
onDelete: this._onDeleteRecord,
onPublish: this._onPublishRecords
};
return <Records recordsLoaded={recordsLoaded} showAlert={this.state.showAlert} handlers={handlers}
recordDeleted={recordDeleted} recordsDeleting={recordsDeleting} currentUser={currentUser}
Expand All @@ -86,6 +107,7 @@ function mapStateToProps(state) {
function mapDispatchToProps(dispatch) {
return {
deleteRecord: bindActionCreators(deleteRecord, dispatch),
updateRecord: bindActionCreators(updateRecord, dispatch),
loadRecords: bindActionCreators(loadRecords, dispatch),
loadFormTemplates: bindActionCreators(loadFormTemplates, dispatch),
transitionToWithOpts: bindActionCreators(transitionToWithOpts, dispatch)
Expand Down
13 changes: 13 additions & 0 deletions js/constants/DefaultConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,16 @@ export const HttpHeaders = {
export const MediaType = {
FORM_URLENCODED: "application/x-www-form-urlencoded"
}

export const EXTENSION_CONSTANTS = {
SUPPLIER: "supplier",
OPERATOR: "operator"
}

export const RECORD_PHASE = {
OPEN: 'open',
VALID: 'valid',
COMPLETED: 'completed',
PUBLISHED: 'published',
REJECTED: 'rejected'
}
7 changes: 6 additions & 1 deletion js/i18n/cs.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export default {
'please-wait': 'Prosím, čekejte...',
'actions': 'Akce',
'required': 'Políčka označená * jsou povinná',
'reject': 'Odmítnout',
'complete': 'Dokončit',
'publish': 'Publikovat',
blcham marked this conversation as resolved.
Show resolved Hide resolved
'export': 'Export',

'login.title': Constants.APP_NAME + ' - Přihlášení',
Expand Down Expand Up @@ -143,9 +146,11 @@ export default {
'records.id': 'Id',
'records.form-template': 'Šablona',
'records.local-name': 'Název',
'records.completion-status': 'Stav vyplnění',
'records.completion-status': 'Stav',
'records.completion-status-tooltip.complete': 'Všechny povinné informace ze záznamu byly vyplněny.',
'records.completion-status-tooltip.incomplete': 'Některé povinné informace ze záznamu ještě nebyly vyplněny.',
'records.completion-status-tooltip.rejected': 'Formulář byl odmítnut',
'records.completion-status-tooltip.published': 'Formulář byl zveřejněn',
'records.last-modified': 'Naposledy upraveno',
'records.open-tooltip': 'Zobrazit či upravit tento záznam',
'records.delete-tooltip': 'Smazat tento záznam',
Expand Down
Loading
Loading