mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Better inline editor.
This commit is contained in:
parent
2f5c37048b
commit
4dcb38290e
@ -89,7 +89,7 @@ class HomeController extends Controller
|
||||
$label = $request->get('label');
|
||||
$isCustomRange = false;
|
||||
|
||||
app('log')->debug('Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
|
||||
app('log')->debug('dateRange: Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
|
||||
// check if the label is "everything" or "Custom range" which will betray
|
||||
// a possible problem with the budgets.
|
||||
if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) {
|
||||
@ -99,7 +99,7 @@ class HomeController extends Controller
|
||||
|
||||
$diff = $start->diffInDays($end, true) + 1;
|
||||
|
||||
if ($diff > 50) {
|
||||
if ($diff > 366) {
|
||||
$request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => (int)$diff]));
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Support\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Trait GetConfigurationData
|
||||
@ -82,6 +83,8 @@ trait GetConfigurationData
|
||||
{
|
||||
$viewRange = app('navigation')->getViewRange(false);
|
||||
|
||||
Log::debug(sprintf('dateRange: the view range is "%s"', $viewRange));
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = session('start');
|
||||
|
||||
@ -97,6 +100,7 @@ trait GetConfigurationData
|
||||
// first range is the current range:
|
||||
$title => [$start, $end],
|
||||
];
|
||||
Log::debug(sprintf('dateRange: the date range in the session is"%s" - "%s"', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
|
||||
// when current range is a custom range, add the current period as the next range.
|
||||
if ($isCustom) {
|
||||
|
@ -28,8 +28,8 @@ import '@ag-grid-community/styles/ag-grid.css';
|
||||
import '@ag-grid-community/styles/ag-theme-alpine.css';
|
||||
import '../../css/grid-ff3-theme.css';
|
||||
import Get from "../../api/v2/model/account/get.js";
|
||||
import GenericEditor from "../../support/editable/GenericEditor.js";
|
||||
import Put from "../../api/v2/model/account/put.js";
|
||||
import AccountRenderer from "../../support/renderers/AccountRenderer.js";
|
||||
|
||||
// set type from URL
|
||||
const urlParts = window.location.href.split('/');
|
||||
@ -78,35 +78,46 @@ let index = function () {
|
||||
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
|
||||
this.loadAccounts();
|
||||
},
|
||||
renderObjectValue(field, account) {
|
||||
let renderer = new AccountRenderer();
|
||||
if ('name' === field) {
|
||||
return renderer.renderName(account);
|
||||
}
|
||||
},
|
||||
submitInlineEdit(e) {
|
||||
e.preventDefault();
|
||||
const newTarget = e.currentTarget;
|
||||
const index = newTarget.dataset.index;
|
||||
const newValue = document.querySelectorAll('[data-index="'+index+'input"]')[0].value ?? '';
|
||||
if('' === newValue) {
|
||||
const fieldName = newTarget.dataset.field;
|
||||
const accountId = newTarget.dataset.id;
|
||||
// need to find the input thing
|
||||
console.log('Clicked edit button for account on index #' + index + ' and field ' + fieldName);
|
||||
const querySelector = 'input[data-field="' + fieldName + '"][data-index="' + index + '"]';
|
||||
console.log(querySelector);
|
||||
const newValue = document.querySelectorAll(querySelector)[0].value ?? '';
|
||||
if ('' === newValue) {
|
||||
return;
|
||||
}
|
||||
// submit the field in an update thing?
|
||||
const fieldName = this.editors[index].options.field;
|
||||
console.log('new field name is ' + fieldName + '=' + newValue + ' for account #' + newTarget.dataset.id);
|
||||
const params = {};
|
||||
params[fieldName] = newValue;
|
||||
console.log(params);
|
||||
console.log('New value is ' + newValue + ' for account #' + this.editors[index].options.id);
|
||||
(new Put()).put(this.editors[index].options.id, params);
|
||||
(new Put()).put(accountId, params);
|
||||
|
||||
// update value, should auto render correctly!
|
||||
this.accounts[index][fieldName] = newValue;
|
||||
this.accounts[index].nameEditorVisible = false;
|
||||
},
|
||||
cancelInlineEdit(e) {
|
||||
const newTarget = e.currentTarget;
|
||||
const index = newTarget.dataset.index;
|
||||
this.editors[index].cancel();
|
||||
this.accounts[index].nameEditorVisible = false;
|
||||
},
|
||||
triggerEdit(e) {
|
||||
const target = e.currentTarget;
|
||||
const index = target.dataset.index;
|
||||
// get parent:
|
||||
this.editors[index] = new GenericEditor();
|
||||
this.editors[index].setElement(target);
|
||||
this.editors[index].init();
|
||||
this.editors[index].replace();
|
||||
const id = target.dataset.id;
|
||||
console.log('Index of this row is ' + index + ' and ID is ' + id);
|
||||
this.accounts[index].nameEditorVisible = true;
|
||||
},
|
||||
loadAccounts() {
|
||||
this.notifications.wait.show = true;
|
||||
@ -124,6 +135,7 @@ let index = function () {
|
||||
id: parseInt(current.id),
|
||||
active: current.attributes.active,
|
||||
name: current.attributes.name,
|
||||
nameEditorVisible: false,
|
||||
type: current.attributes.type,
|
||||
role: current.attributes.account_role,
|
||||
iban: null === current.attributes.iban ? '' : current.attributes.iban.match(/.{1,4}/g).join(' '),
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
import Get from "../../api/v2/model/transaction/get.js";
|
||||
|
||||
export default class TransactionDataSource {
|
||||
export default class TransactionDataSource {
|
||||
constructor() {
|
||||
this.type = 'all';
|
||||
this.rowCount = null;
|
||||
|
@ -35,11 +35,11 @@ export default class GenericEditor {
|
||||
this.options.model = this.element.dataset.model;
|
||||
this.options.field = this.element.dataset.field;
|
||||
//this.options.field = this.element.dataset.type;
|
||||
console.log('GenericEditor['+this.options.index+'].init()');
|
||||
console.log('GenericEditor[' + this.options.index + '].init()');
|
||||
}
|
||||
|
||||
replace() {
|
||||
console.log('GenericEditor['+this.options.index+'].replace()');
|
||||
console.log('GenericEditor[' + this.options.index + '].replace()');
|
||||
// save old HTML in data field (does that work, is it safe?)
|
||||
this.options.original = this.element.parentElement.innerHTML;
|
||||
if (this.options.type === 'text') {
|
||||
@ -48,25 +48,107 @@ export default class GenericEditor {
|
||||
}
|
||||
|
||||
replaceText() {
|
||||
console.log('GenericEditor['+this.options.index+'].replaceText()');
|
||||
let html = this.formStart() + this.rowStart();
|
||||
console.log('GenericEditor[' + this.options.index + '].replaceText()');
|
||||
// create form
|
||||
let form = document.createElement('form');
|
||||
form.classList.add('form-inline');
|
||||
|
||||
// input field:
|
||||
html += this.columnStart('7') + this.label() + this.textField() + this.closeDiv();
|
||||
// create row
|
||||
let row = document.createElement('div');
|
||||
row.classList.add('row');
|
||||
|
||||
// add submit button
|
||||
html += this.columnStart('5') + this.buttonGroup() + this.closeDiv();
|
||||
// create column
|
||||
let column = document.createElement('div');
|
||||
column.classList.add('col-7');
|
||||
|
||||
// close column and form:
|
||||
html += this.closeDiv() + this.closeForm();
|
||||
this.element.parentElement.innerHTML = html;
|
||||
// create label, add to column
|
||||
let label = document.createElement('label');
|
||||
label.classList.add('sr-only');
|
||||
label.setAttribute('for', 'input');
|
||||
label.innerText = 'Field value';
|
||||
column.appendChild(label);
|
||||
|
||||
// creat text field, add to column
|
||||
let input = document.createElement('input');
|
||||
input.classList.add('form-control');
|
||||
input.classList.add('form-control-sm');
|
||||
input.dataset.index = this.options.index + 'index';
|
||||
input.setAttribute('autocomplete', 'off');
|
||||
input.setAttribute('type', 'text');
|
||||
input.setAttribute('id', 'input');
|
||||
input.setAttribute('name', 'name');
|
||||
input.setAttribute('value', this.options.value);
|
||||
input.setAttribute('placeholder', this.options.value);
|
||||
input.setAttribute('autofocus', 'true');
|
||||
column.appendChild(input);
|
||||
|
||||
// add column to row
|
||||
row.appendChild(column);
|
||||
|
||||
// create column for buttons
|
||||
let column2 = document.createElement('div');
|
||||
column2.classList.add('col-5');
|
||||
column2.classList.add('text-right');
|
||||
|
||||
// create button group
|
||||
let buttonGroup = document.createElement('div');
|
||||
buttonGroup.classList.add('btn-group');
|
||||
buttonGroup.classList.add('btn-group-sm');
|
||||
buttonGroup.setAttribute('role', 'group');
|
||||
buttonGroup.setAttribute('aria-label', 'Options');
|
||||
|
||||
// add buttons
|
||||
let cancelButton = document.createElement('button');
|
||||
cancelButton.dataset.index = this.options.index;
|
||||
cancelButton.setAttribute('type', 'button');
|
||||
cancelButton.setAttribute('x-click', 'cancelInlineEdit');
|
||||
cancelButton.classList.add('btn');
|
||||
cancelButton.classList.add('btn-danger');
|
||||
|
||||
// add icon to cancel button
|
||||
let icon = document.createElement('em');
|
||||
icon.classList.add('fa-solid');
|
||||
icon.classList.add('fa-xmark');
|
||||
icon.classList.add('text-white');
|
||||
cancelButton.appendChild(icon);
|
||||
|
||||
// '<button data-index="' + this.options.index + '" type="submit" @click="submitInlineEdit" class="btn btn-success"><em class="fa-solid fa-check"></em></button>' +
|
||||
let submitButton = document.createElement('button');
|
||||
submitButton.dataset.index = this.options.index;
|
||||
submitButton.setAttribute('type', 'submit');
|
||||
submitButton.setAttribute('x-click', 'submitInlineEdit');
|
||||
submitButton.classList.add('btn');
|
||||
submitButton.classList.add('btn-success');
|
||||
|
||||
// add icon to submit button
|
||||
let icon2 = document.createElement('em');
|
||||
icon2.classList.add('fa-solid');
|
||||
icon2.classList.add('fa-check');
|
||||
icon2.classList.add('text-white');
|
||||
submitButton.appendChild(icon2);
|
||||
|
||||
|
||||
// add to button group
|
||||
buttonGroup.appendChild(cancelButton);
|
||||
buttonGroup.appendChild(submitButton);
|
||||
|
||||
// add button group to column
|
||||
column2.appendChild(buttonGroup);
|
||||
|
||||
// add column to row
|
||||
row.appendChild(column2);
|
||||
|
||||
this.element.parentElement.innerHTML = row.outerHTML;
|
||||
}
|
||||
|
||||
textField() {
|
||||
return '<input data-index="' + this.options.index + 'input" autocomplete="off" type="text" class="form-control form-control-sm" id="input" name="name" value="' + this.options.value + '" placeholder="' + this.options.value + '" autofocus>';
|
||||
}
|
||||
|
||||
closeDiv() {
|
||||
return '</div>';
|
||||
}
|
||||
|
||||
closeForm() {
|
||||
return '</form>';
|
||||
}
|
||||
@ -85,22 +167,25 @@ export default class GenericEditor {
|
||||
}
|
||||
return '<div class="col-' + param + '">';
|
||||
}
|
||||
|
||||
label() {
|
||||
return '<label class="sr-only" for="input">Field value</label>';
|
||||
}
|
||||
|
||||
buttonGroup() {
|
||||
return '<div class="btn-group btn-group-sm" role="group" aria-label="Options">'+
|
||||
'<button data-index="'+this.options.index+'" type="button" @click="cancelInlineEdit" class="btn btn-danger"><em class="fa-solid fa-xmark text-white"></em></button>'+
|
||||
'<button data-index="'+this.options.index+'" type="submit" @click="submitInlineEdit" class="btn btn-success"><em class="fa-solid fa-check"></em></button>' +
|
||||
return '<div class="btn-group btn-group-sm" role="group" aria-label="Options">' +
|
||||
'<button data-index="' + this.options.index + '" type="button" @click="cancelInlineEdit" class="btn btn-danger"><em class="fa-solid fa-xmark text-white"></em></button>' +
|
||||
'<button data-index="' + this.options.index + '" type="submit" @click="submitInlineEdit" class="btn btn-success"><em class="fa-solid fa-check"></em></button>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
cancel() {
|
||||
console.log('GenericEditor['+this.options.index+'].cancel()');
|
||||
console.log('GenericEditor[' + this.options.index + '].cancel()');
|
||||
console.log(this.element);
|
||||
console.log(this.parent);
|
||||
this.parent.innerHTML = this.options.original;
|
||||
}
|
||||
|
||||
submitInlineEdit(e) {
|
||||
console.log('Submit?');
|
||||
}
|
||||
|
32
resources/assets/v2/support/renderers/AccountRenderer.js
Normal file
32
resources/assets/v2/support/renderers/AccountRenderer.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* AccountRenderer.js
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
import GenericObjectRenderer from "./GenericObjectRenderer.js";
|
||||
|
||||
export default class AccountRenderer {
|
||||
renderField(field, account) {
|
||||
if('name' === field) {
|
||||
return this.renderName(account);
|
||||
}
|
||||
}
|
||||
renderName(account) {
|
||||
return (new GenericObjectRenderer).renderUrl('./accounts/show/' + account.id.toString(), account.name, account.name);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* GenericObjectRenderer.js
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
export default class GenericObjectRenderer {
|
||||
renderUrl(url, title, text) {
|
||||
return `<a href="${url}" title="${title}">${text}</a>`;
|
||||
}
|
||||
}
|
@ -96,10 +96,24 @@
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'./accounts/show/' + account.id">
|
||||
<span x-text="account.name"></span>
|
||||
</a>
|
||||
<em :data-index="account.id + 'name'" @click="triggerEdit" data-type="text" data-model="Account" :data-id="account.id" data-field="name" :data-value="account.name" class="hidden-edit-button inline-edit-button fa-solid fa-pencil" data-id="1"></em>
|
||||
<!-- render content using a function -->
|
||||
<span x-html="renderObjectValue('name', account)" x-show="!account.nameEditorVisible"></span>
|
||||
|
||||
<!-- edit buttons -->
|
||||
<em x-show="!account.nameEditorVisible" :data-id="account.id" :data-index="index" @click="triggerEdit" data-type="text" class="hidden-edit-button inline-edit-button fa-solid fa-pencil" :data-id="account.id"></em>
|
||||
|
||||
<!-- edit things -->
|
||||
<div class="row" x-show="account.nameEditorVisible">
|
||||
<div class="col-8">
|
||||
<input :data-index="index" data-field="name" autocomplete="off" type="text" class="form-control form-control-sm" id="input" name="name" :value="account.name" :placeholder="account.value" autofocus>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="btn-group btn-group-sm" role="group" aria-label="Options">
|
||||
<button :data-index="index" :data-id="account.id" data-field="name" type="button" @click="cancelInlineEdit" class="btn btn-danger"><em class="fa-solid fa-xmark text-white"></em></button>
|
||||
<button :data-index="index" :data-id="account.id" data-field="name" type="submit" @click="submitInlineEdit" class="btn btn-success"><em class="fa-solid fa-check"></em></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span x-text="account.type"></span>
|
||||
|
Loading…
Reference in New Issue
Block a user