Commit 8ece6070 authored by ZeHiro's avatar ZeHiro
Browse files

Delete operation: client side

parent c9f7c0fe
......@@ -43,6 +43,17 @@ export function getOperations(accountId) {
});
}
export function deleteOperation(opId) {
return new Promise((accept, reject) => {
$.ajax({
url: `operations/${opId}`,
type: 'DELETE',
success: accept,
error: xhrReject(reject)
});
});
}
export function deleteBank(bankId) {
return new Promise((accept, reject) => {
$.ajax({
......
import { has, translate as $t } from '../../helpers';
import { Actions } from '../../store';
import ConfirmDeleteModal from '../ui/confirm-delete-modal';
export default class DeleteOperation extends React.Component {
constructor(props) {
has(props, 'operation');
has(props, 'formatCurrency');
super(props);
this.handleDeleteOperation = this.handleDeleteOperation.bind(this);
}
handleDeleteOperation() {
Actions.deleteOperation(this.props.operation);
}
render() {
let op = this.props.operation;
let label = `"${op.customLabel ? op.customLabel : op.title}"`;
let amount = this.props.formatCurrency(op.amount);
let date = op.date.toLocaleDateString();
let modalBody = (
<div>
<div>{ $t('client.operations.warning_delete') }</div>
<div>{ $t('client.operations.are_you_sure', { label, amount, date }) }</div>
</div>
);
return (
<div>
<button className="btn btn-danger"
data-toggle="modal"
data-target={ `#delete${op.id}` }>
<span className="fa fa-trash"></span>&nbsp;
{ $t('client.operations.delete_operation_button') }
</button>
<ConfirmDeleteModal
modalId={ `delete${op.id}` }
modalBody={ modalBody }
onDelete={ this.handleDeleteOperation }
/>
</div>
);
}
}
......@@ -2,6 +2,7 @@ import { has, translate as $t } from '../../helpers';
import { Actions } from '../../store';
import { DetailedViewLabel } from './label';
import DeleteOperation from './delete-operation';
import OperationTypeSelect from '../ui/operation-type-select';
import CategorySelect from '../ui/category-select';
......@@ -16,6 +17,7 @@ export default class OperationDetails extends React.Component {
has(props, 'onToggleDetails');
has(props, 'operation');
has(props, 'rowClassName');
has(props, 'formatCurrency');
super(props);
this.handleSelectType = this.handleSelectType.bind(this);
this.handleSelectCategory = this.handleSelectCategory.bind(this);
......@@ -75,7 +77,7 @@ export default class OperationDetails extends React.Component {
</li>
<li>
{ $t('client.operations.amount') }
{ op.amount }
{ this.props.formatCurrency(op.amount) }
</li>
<li className="form-inline">
{ $t('client.operations.type') }
......@@ -92,7 +94,14 @@ export default class OperationDetails extends React.Component {
/>
</li>
{ maybeAttachment }
<li>
<DeleteOperation
operation={ this.props.operation }
formatCurrency={ this.props.formatCurrency }
/>
</li>
</ul>
</td>
</tr>
);
......
......@@ -51,6 +51,7 @@ export default class Operation extends React.Component {
onToggleDetails ={ this.handleToggleDetails }
operation={ op }
rowClassName={ rowClassName }
formatCurrency= { this.props.formatCurrency }
/>
);
}
......
......@@ -637,6 +637,35 @@ store.updateCustomLabelForOperation = function(operation, customLabel) {
.catch(genericErrorHandler);
};
store.deleteOperation = function(operation) {
let operationId = operation.id;
backend.deleteOperation(operationId)
.then(() => {
store.deleteOperationOfCurrentAccount(operationId);
flux.dispatch({
type: Events.forward,
event: State.operations
});
})
.catch(genericErrorHandler);
};
store.deleteOperationOfCurrentAccount = function(operationId) {
let toDeleteIdx = null;
let operations = store.getCurrentOperations();
for (let i = 0; i < operations.length; i++) {
let op = operations[i];
if (op.id === operationId) {
toDeleteIdx = i;
break;
}
}
assert(toDeleteIdx !== null);
operations.splice(toDeleteIdx, 1);
};
store.mergeOperations = function(toKeepId, toRemoveId) {
backend.mergeOperations(toKeepId, toRemoveId).then(newToKeep => {
......@@ -1019,6 +1048,14 @@ export let Actions = {
});
},
deleteOperation(operation) {
assert(operation instanceof Operation, 'deleteOperation arg must be an Operation');
flux.dispatch({
type: Events.user.deletedOperation,
operation
});
},
// Settings
deleteAccount(account) {
assert(account instanceof Account, 'DeleteAccount expects an Account');
......@@ -1228,6 +1265,11 @@ flux.register(action => {
store.mergeOperations(action.toKeepId, action.toRemoveId);
break;
case Events.user.deletedOperation:
has(action, 'operation');
store.deleteOperation(action.operation);
break;
case Events.user.fetchedOperations:
store.fetchOperations();
break;
......
......@@ -116,7 +116,11 @@ module.exports = {
title: 'Transactions',
type: 'Type:',
custom_label: 'Custom label',
add_custom_label: 'Add a custom label'
add_custom_label: 'Add a custom label',
delete_operation_button: "Delete this operation",
warning_delete: "Before deleting the operation by this mean, ensure it does not appear in the duplicates list, you can delete it there with the 'merge button'.",
are_you_sure: `Are you sure you still want to delete the operation %{label} (%{amount}) of %{date} ?`
},
search: {
......
......@@ -116,7 +116,11 @@ module.exports = {
title: 'Opérations',
type: 'Type :',
custom_label: 'Libellé personnalisé :',
add_custom_label: 'Ajouter un libellé personnalisé'
add_custom_label: 'Ajouter un libellé personnalisé',
delete_operation_button: "Supprimer l'opération",
warning_delete: "Avant de supprimer l'opération par ce moyen, assurez-vous que celle-ci n'apparait pas dans la liste des doublons, vous pourrez la supprimer avec le bouton 'fusionner'.",
are_you_sure: `Êtes-vous sur(e) de toujours vouloir supprimer l'opération %{label} (%{amount}) du %{date} ?`
},
search: {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment