Update, rebuild, and add a new API endpoint.

This commit is contained in:
James Cole
2024-01-17 20:23:02 +01:00
parent 66b0d9d309
commit 44df07a5f5
52 changed files with 1638 additions and 241 deletions

View File

@@ -0,0 +1,42 @@
/*
* BaseMode.js
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
*
* License: MIT
*
* Copied and slightly edited by James Cole <james@firefly-iii.org>
*/
export default class BaseMode{
constructor(context) {
if(this.constructor === BaseMode){
throw new Error(`It's abstract class`);
}
this.context = context;
}
event_show(){
this.context.typeElement.hideError();
this.context.typeElement.element.value = this.context.value;
this.context.element.dispatchEvent(new CustomEvent("show"));
}
event_shown(){
this.context.element.dispatchEvent(new CustomEvent("shown"));
}
event_hide(){
this.context.element.dispatchEvent(new CustomEvent("hide"));
}
event_hidden(){
this.context.element.dispatchEvent(new CustomEvent("hidden"));
}
init(){
throw new Error('Method `init` not define!');
}
enable(){
throw new Error('Method `enable` not define!');
}
disable(){
throw new Error('Method `disable` not define!');
}
hide(){
throw new Error('Method `hide` not define!');
}
}

View File

@@ -0,0 +1,40 @@
/*
* InlineMode.js
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
*
* License: MIT
*
* Copied and slightly edited by James Cole <james@firefly-iii.org>
*/
import BaseMode from "./BaseMode.js";
export default class InlineMode extends BaseMode{
init(){
const open = () => {
if(!this.context.disabled){
const item = this.context.typeElement.create();
this.event_show();
this.context.element.removeEventListener('click', open);
this.context.element.innerHTML = '';
this.context.element.append(item);
this.event_shown();
}
}
this.context.element.addEventListener('click', open);
}
enable(){
}
disable(){
}
hide(){
this.event_hide();
this.context.element.innerHTML = this.context.value;
setTimeout(() => {
this.init();
this.event_hidden();
}, 100);
}
}

View File

@@ -0,0 +1,53 @@
/*
* PopupMode.js
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
*
* License: MIT
*
* Copied and slightly edited by James Cole <james@firefly-iii.org>
*/
import BaseMode from "./BaseMode.js";
export default class PopupMode extends BaseMode{
init(){
this.popover = new bootstrap.Popover(this.context.element, {
container: "body",
content: this.context.typeElement.create(),
html: true,
customClass: "dark-editable",
title: this.context.title,
});
this.context.element.addEventListener('show.bs.popover', () => {
this.event_show();
});
this.context.element.addEventListener('shown.bs.popover', () => {
this.event_shown();
});
this.context.element.addEventListener('hide.bs.popover', () => {
this.event_hide();
});
this.context.element.addEventListener('hidden.bs.popover', () => {
this.event_hidden();
});
document.addEventListener('click', (e) => {
const target = e.target;
if(target === this.popover.tip || target === this.context.element) return;
let current = target;
while(current = current.parentNode){
if(current === this.popover.tip) return;
}
this.hide();
})
}
enable(){
this.popover.enable();
}
disable(){
this.popover.disable();
}
hide(){
this.popover.hide();
}
}

View File

@@ -0,0 +1,255 @@
/*
* BaseMode.js
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
*
* License: MIT
*
* Copied and slightly edited by James Cole <james@firefly-iii.org>
*/
export default class BaseType {
context = null;
element = null;
error = null;
form = null;
load = null;
buttonGroup = null;
buttons = {success: null, cancel: null};
constructor(context) {
if (this.constructor === BaseType) {
throw new Error(`It's abstract class`);
}
this.context = context;
}
create() {
throw new Error('Method `create` not define!');
}
createContainer(element) {
const div = document.createElement(`div`);
// original list of elements:
this.element = element;
this.error = this.createContainerError();
this.form = this.createContainerForm();
this.load = this.createContainerLoad();
this.buttons.success = this.createButtonSuccess();
this.buttons.cancel = this.createButtonCancel();
// create first div, with label and input:
const topDiv = document.createElement(`div`);
topDiv.classList.add("col-12");
// create label:
const label = document.createElement(`label`);
label.classList.add("visually-hidden");
label.for = element.id;
// add label + input to top div:
topDiv.append(label, element);
// create second div, with button group:
const bottomDiv = document.createElement(`div`);
bottomDiv.classList.add("col-12");
// create button group:
this.buttonGroup = this.createButtonGroup();
// append buttons to button group:
this.buttonGroup.append(this.buttons.success, this.buttons.cancel);
bottomDiv.append(this.buttonGroup);
// append bottom and top div to form:
this.form.append(topDiv, bottomDiv);
//this.form.append(element, this.load, this.buttons.success, this.buttons.cancel);
//this.form.append(element, this.load, this.buttonGroup);
div.append(this.error, this.form);
return div;
}
createButtonGroup() {
const div = document.createElement(`div`);
div.classList.add("btn-group", "btn-group-sm");
return div;
}
createContainerError() {
const div = document.createElement(`div`);
div.classList.add("text-danger", "fst-italic", "mb-2", "fw-bold");
div.style.display = "none";
return div;
}
createContainerForm() {
const form = document.createElement(`form`);
form.classList.add("row", "row-cols-lg-auto", "g-3", "align-items-center");
//form.style.gap = "20px";
form.addEventListener('submit', async e => {
e.preventDefault();
const newValue = this.getValue();
if (this.context.send && this.context.pk && this.context.url && (this.context.value !== newValue)) {
this.showLoad();
let msg;
try {
const response = await this.ajax(newValue);
if (response.ok) {
msg = await this.context.success(response, newValue);
} else {
msg = await this.context.error(response, newValue) || `${response.status} ${response.statusText}`;
}
} catch (error) {
console.error(error);
msg = error;
}
if (msg) {
this.setError(msg);
this.showError();
} else {
this.setError(null);
this.hideError();
this.context.value = this.getValue();
this.context.modeElement.hide();
this.initText();
}
this.hideLoad();
} else {
this.context.value = this.getValue();
this.context.modeElement.hide();
this.initText();
}
this.context.element.dispatchEvent(new CustomEvent("save"));
})
return form;
}
createContainerLoad() {
const div = document.createElement(`div`);
div.style.display = "none";
div.style.position = "absolute";
div.style.background = "white";
div.style.width = "100%";
div.style.height = "100%";
div.style.top = 0;
div.style.left = 0;
const loader = document.createElement(`div`);
loader.classList.add("dark-editable-loader");
div.append(loader);
return div;
}
createButton() {
const button = document.createElement("button");
button.type = "button";
button.classList.add("btn", "btn-sm");
button.style.color = "transparent";
button.style.textShadow = "0 0 0 white";
return button;
}
createButtonSuccess() {
const btn_success = this.createButton();
btn_success.type = "submit";
btn_success.classList.add("btn-success");
btn_success.innerHTML = "✔";
return btn_success;
}
createButtonCancel() {
const btn_cancel = this.createButton();
btn_cancel.classList.add("btn-danger");
const div = document.createElement("div");
div.innerHTML = "✖";
btn_cancel.append(div);
btn_cancel.addEventListener("click", () => {
this.context.modeElement.hide();
});
return btn_cancel;
}
hideLoad() {
this.load.style.display = "none";
}
showLoad() {
this.load.style.display = "block";
}
ajax(new_value) {
let url = this.context.url;
const form = new FormData;
form.append("pk", this.context.pk);
form.append("name", this.context.name);
form.append("value", new_value);
const option = {};
option.method = this.context.ajaxOptions.method;
if (option.method === "POST") {
option.body = form;
} else {
url += "?" + new URLSearchParams(form).toString();
}
return fetch(url, option);
}
async successResponse(response, newValue) {
}
async errorResponse(response, newValue) {
}
setError(errorMsg) {
this.error.innerHTML = errorMsg;
}
showError() {
this.error.style.display = "block";
}
hideError() {
if (this.error) {
this.error.style.display = "none";
}
}
createElement(name) {
const element = document.createElement(name);
console.log(element);
element.classList.add("form-control");
if (this.context.required) {
element.required = this.context.required;
}
this.add_focus(element);
return element;
}
add_focus(element) {
this.context.element.addEventListener('shown', function () {
element.focus();
});
}
initText() {
if (this.context.value === "") {
this.context.element.innerHTML = this.context.emptytext;
return true;
} else {
this.context.element.innerHTML = this.context.value;
return false;
}
}
initOptions() {
}
getValue() {
return this.element.value;
}
}

View File

@@ -0,0 +1,16 @@
import DateType from "./DateType.js";
export default class DateTimeType extends DateType{
create(){
const input = this.createElement(`input`);
input.type = "datetime-local";
return this.createContainer(input);
}
initOptions(){
this.context.get_opt("format", "YYYY-MM-DD HH:mm");
this.context.get_opt("viewformat", "YYYY-MM-DD HH:mm");
this.context.value = moment(this.context.value).format("YYYY-MM-DDTHH:mm");
}
}

View File

@@ -0,0 +1,25 @@
import BaseType from "./BaseType.js";
export default class DateType extends BaseType{
create(){
const input = this.createElement(`input`);
input.type = "date";
return this.createContainer(input);
}
initText(){
if(this.value === ""){
this.context.element.innerHTML = this.context.emptytext;
return true;
} else {
this.context.element.innerHTML = moment(this.context.value).format(this.context.viewformat);
return false;
}
}
initOptions(){
this.context.get_opt("format", "YYYY-MM-DD");
this.context.get_opt("viewformat", "YYYY-MM-DD");
}
}

View File

@@ -0,0 +1,19 @@
import BaseType from "./BaseType.js";
export default class InputType extends BaseType{
create(){
// expand input element with necessary classes and things.
// <input type="text" class="form-control form-control-md" id="inlineFormInputGroupUsername" placeholder="Username">
const input = this.createElement(`input`);
const id = this.context.element.id + '_input';
input.type = this.context.type;
input.id = id;
input.autocomplete = 'off';
input.placeholder = this.context.element.innerText;
input.classList.add("form-control", "form-control-md");
return this.createContainer(input);
}
}

View File

@@ -0,0 +1,36 @@
import BaseType from "./BaseType.js";
export default class SelectType extends BaseType{
create(){
const select = this.createElement(`select`);
this.context.source.forEach(item => {
const opt = document.createElement(`option`);
opt.value = item.value;
opt.innerHTML = item.text;
select.append(opt);
});
return this.createContainer(select);
}
initText(){
this.context.element.innerHTML = this.context.emptytext;
if(this.context.value !== "" && this.context.source.length > 0){
for(const key in this.context.source){
const item = this.context.source[ key ];
if(item.value == this.context.value){
this.context.element.innerHTML = item.text;
return false;
}
}
}
return true;
}
initOptions(){
this.context.get_opt("source", []);
if(typeof this.context.source === "string" && this.context.source !== ""){
this.context.source = JSON.parse(this.context.source);
}
}
}

View File

@@ -0,0 +1,9 @@
import BaseType from "./BaseType.js";
export default class TextAreaType extends BaseType{
create(){
const textarea = this.createElement(`textarea`);
return this.createContainer(textarea);
}
}

View File

@@ -0,0 +1,87 @@
.dark-editable-element{
border-bottom: dashed 1px #0088cc;
text-decoration: none;
cursor: pointer;
}
.dark-editable-element-disabled{
border-bottom: none;
cursor: default;
}
.dark-editable-element-empty{
font-style: italic;
color: #DD1144;
}
.dark-editable{
max-width: none;
}
.dark-editable-loader {
font-size: 5px;
left: 50%;
top: 50%;
width: 1em;
height: 1em;
border-radius: 50%;
position: relative;
text-indent: -9999em;
-webkit-animation: load5 1.1s infinite ease;
animation: load5 1.1s infinite ease;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
@-webkit-keyframes load5 {
0%,
100% {
box-shadow: 0em -2.6em 0em 0em #000000, 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.5), -1.8em -1.8em 0 0em rgba(0,0,0, 0.7);
}
12.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.7), 1.8em -1.8em 0 0em #000000, 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.5);
}
25% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.5), 1.8em -1.8em 0 0em rgba(0,0,0, 0.7), 2.5em 0em 0 0em #000000, 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
37.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.5), 2.5em 0em 0 0em rgba(0,0,0, 0.7), 1.75em 1.75em 0 0em #000000, 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
50% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.5), 1.75em 1.75em 0 0em rgba(0,0,0, 0.7), 0em 2.5em 0 0em #000000, -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
62.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.5), 0em 2.5em 0 0em rgba(0,0,0, 0.7), -1.8em 1.8em 0 0em #000000, -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
75% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.5), -1.8em 1.8em 0 0em rgba(0,0,0, 0.7), -2.6em 0em 0 0em #000000, -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
87.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.5), -2.6em 0em 0 0em rgba(0,0,0, 0.7), -1.8em -1.8em 0 0em #000000;
}
}
@keyframes load5 {
0%,
100% {
box-shadow: 0em -2.6em 0em 0em #000000, 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.5), -1.8em -1.8em 0 0em rgba(0,0,0, 0.7);
}
12.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.7), 1.8em -1.8em 0 0em #000000, 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.5);
}
25% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.5), 1.8em -1.8em 0 0em rgba(0,0,0, 0.7), 2.5em 0em 0 0em #000000, 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
37.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.5), 2.5em 0em 0 0em rgba(0,0,0, 0.7), 1.75em 1.75em 0 0em #000000, 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
50% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.5), 1.75em 1.75em 0 0em rgba(0,0,0, 0.7), 0em 2.5em 0 0em #000000, -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
62.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.5), 0em 2.5em 0 0em rgba(0,0,0, 0.7), -1.8em 1.8em 0 0em #000000, -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
75% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.5), -1.8em 1.8em 0 0em rgba(0,0,0, 0.7), -2.6em 0em 0 0em #000000, -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
}
87.5% {
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.5), -2.6em 0em 0 0em rgba(0,0,0, 0.7), -1.8em -1.8em 0 0em #000000;
}
}

View File

@@ -0,0 +1,181 @@
/*
* dark-editable.js
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
*
* License: MIT
*
* Copied and slightly edited by James Cole <james@firefly-iii.org>
*/
import "./dark-editable.css";
import PopupMode from "./Modes/PopupMode.js";
import InlineMode from "./Modes/InlineMode.js";
import BaseType from "./Types/BaseType.js";
import InputType from "./Types/InputType.js";
import TextAreaType from "./Types/TextAreaType.js";
import SelectType from "./Types/SelectType.js";
import DateType from "./Types/DateType.js";
import DateTimeType from "./Types/DateTimeType.js";
export default class DarkEditable{
modeElement = null;
typeElement = null;
mode = null;
type = null;
emptytext = null;
viewformat = null;
pk = null;
name = null;
constructor(element, options = {}){
this.element = element;
this.options = options;
this.init_options();
this.typeElement = this.route_type();
this.typeElement.initOptions();
this.modeElement = this.route_mode();
this.modeElement.init();
this.init_text();
this.init_style();
if(this.disabled){
this.disable();
}
this.element.dispatchEvent(new CustomEvent("init"));
}
/* INIT METHODS */
get_opt(name, default_value){
return this[ name ] = this.element.dataset?.[ name ] ?? this.options?.[ name ] ?? default_value;
}
get_opt_bool(name, default_value){
this.get_opt(name, default_value);
if(typeof this[ name ] !== "boolean"){
if(this[ name ] === "true") {
this[ name ] = true;
} else if(this[ name ] === "false") {
this[ name ] = false;
} else {
this[ name ] = default_value;
}
}
return this[ name ];
}
init_options(){
//priority date elements
this.get_opt("value", this.element.innerHTML);
this.get_opt("name", this.element.id);
this.get_opt("pk", null);
this.get_opt("title", "");
this.get_opt("type", "text");
this.get_opt("emptytext", "Empty");
this.get_opt("mode", "popup");
this.get_opt("url", null);
this.get_opt("ajaxOptions", {});
this.ajaxOptions = Object.assign({
method: "POST",
dataType: "text",
}, this.ajaxOptions);
this.get_opt_bool("send", true);
this.get_opt_bool("disabled", false);
this.get_opt_bool("required", false);
if(this.options?.success && typeof this.options?.success == "function"){
this.success = this.options.success;
}
if(this.options?.error && typeof this.options?.error == "function"){
this.error = this.options.error;
}
}
init_text(){
const empty_class = "dark-editable-element-empty";
this.element.classList.remove(empty_class);
if(this.typeElement.initText()){
this.element.classList.add(empty_class);
}
}
init_style(){
this.element.classList.add("dark-editable-element");
}
/* INIT METHODS END */
route_mode(){
switch (this.mode){
default:
throw new Error(`Mode ${this.mode} not found!`)
case 'popup':
return new PopupMode(this);
case 'inline':
return new InlineMode(this);
}
}
route_type(){
if(this.type.prototype instanceof BaseType){
return new this.type(this);
}
if(typeof this.type === 'string'){
switch(this.type){
case "text":
case "password":
case "email":
case "url":
case "tel":
case "number":
case "range":
case "time":
return new InputType(this);
case "textarea":
return new TextAreaType(this);
case "select":
return new SelectType(this);
case "date":
return new DateType(this);
case "datetime":
return new DateTimeType(this);
}
}
throw new Error(`Undefined type`);
}
/* AJAX */
async success(response, newValue){
return await this.typeElement.successResponse(response, newValue);
}
async error(response, newValue){
return await this.typeElement.errorResponse(response, newValue);
}
/* AJAX END */
/* METHODS */
enable(){
this.disabled = false;
this.element.classList.remove("dark-editable-element-disabled");
this.modeElement.enable();
}
disable(){
this.disabled = true;
this.element.classList.add("dark-editable-element-disabled");
this.modeElement.enable();
}
setValue(value){
this.value = value;
this.init_text();
}
getValue(){
return this.value;
}
/* METHODS END */
}

View File

@@ -349,7 +349,7 @@ let transactions = function () {
this.detectTransactionType();
// parse transaction:
let transactions = parseFromEntries(this.entries, this.groupProperties.transactionType);
let transactions = parseFromEntries(this.entries, null, this.groupProperties.transactionType);
let submission = {
group_title: this.groupProperties.title,
fire_webhooks: this.formStates.webhooksButton,
@@ -466,7 +466,7 @@ let transactions = function () {
// onRenderItem: renderAccount,
onChange: changeSourceAccount,
onSelectItem: selectSourceAccount,
hiddenValue: this.items[count].source_account.alpine_name,
hiddenValue: this.entries[count].source_account.alpine_name
});
addAutocomplete({
selector: 'input.ac-dest',

View File

@@ -58,6 +58,7 @@ let transactions = function () {
return {
// transactions are stored in "entries":
entries: [],
originals: [],
// state of the form is stored in formState:
formStates: {
@@ -126,7 +127,7 @@ let transactions = function () {
this.formStates.isSubmitting = true;
// parse transaction:
let transactions = parseFromEntries(this.entries, this.groupProperties.transactionType);
let transactions = parseFromEntries(this.entries, this.originals, this.groupProperties.transactionType);
let submission = {
group_title: this.groupProperties.title,
fire_webhooks: this.formStates.webhooksButton,
@@ -263,7 +264,7 @@ let transactions = function () {
getter.show(groupId, {}).then((response) => {
const data = response.data.data;
this.groupProperties.id = parseInt(data.id);
this.groupProperties.transactionType = data.attributes.transactions[0].type;
this.groupProperties.transactionType = data.attributes.transactions[0].type.toLowerCase();
this.groupProperties.title = data.attributes.title ?? data.attributes.transactions[0].description;
this.entries = parseDownloadedSplits(data.attributes.transactions);

View File

@@ -43,12 +43,14 @@ function addPointToMap(e) {
markers[index].on('dragend', dragEnd);
markers[index].addTo(maps[index]);
const setEvent = new CustomEvent('location-set', {detail: {
const setEvent = new CustomEvent('location-set', {
detail: {
latitude: e.latlng.lat,
longitude: e.latlng.lng,
index: index,
zoomLevel: maps[index].getZoom()
}});
}
});
document.dispatchEvent(setEvent);
}
}
@@ -56,10 +58,12 @@ function addPointToMap(e) {
function saveZoomOfMap(e) {
//let index = parseInt(e.sourceTarget._container.attributes['data-index'].value);
let index = 0;
const zoomEvent = new CustomEvent('location-zoom', {detail: {
const zoomEvent = new CustomEvent('location-zoom', {
detail: {
index: index,
zoomLevel: maps[index].getZoom()
}});
}
});
document.dispatchEvent(zoomEvent);
}
@@ -87,13 +91,15 @@ export function addLocation(index) {
//let holder = document.getElementById('location_map_' + index);
let holder = document.getElementById('location_map');
maps[index] = L.map(holder).setView([holder.dataset.latitude, holder.dataset.longitude], holder.dataset.zoomLevel);
if (holder) {
maps[index] = L.map(holder).setView([holder.dataset.latitude, holder.dataset.longitude], holder.dataset.zoomLevel);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(maps[index]);
maps[index].on('click', addPointToMap);
maps[index].on('zoomend', saveZoomOfMap);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(maps[index]);
maps[index].on('click', addPointToMap);
maps[index].on('zoomend', saveZoomOfMap);
}
}
}

View File

@@ -21,15 +21,23 @@
/**
*
*/
export function parseFromEntries(entries, transactionType) {
export function parseFromEntries(entries, originals, transactionType) {
let returnArray = [];
for (let i in entries) {
if (entries.hasOwnProperty(i)) {
const entry = entries[i];
let compare = false;
let original = {};
if (originals !== null && originals.hasOwnProperty(i)) {
compare = true;
let original = originals[i];
}
let current = {};
// fields for transaction
current.description = entry.description;
if ((compare && original.description !== entry.description) || !compare) {
current.description = entry.description;
}
// source and destination
current.source_name = entry.source_account.name;

View File

@@ -25,6 +25,8 @@ import Get from "../../api/v2/model/transaction/get.js";
import {parseDownloadedSplits} from "./shared/parse-downloaded-splits.js";
import {format} from "date-fns";
import formatMoney from "../../util/format-money.js";
import DarkEditable from "../../libraries/dark-editable/dark-editable.js";
let show = function () {
return {
@@ -56,6 +58,10 @@ let show = function () {
pageProperties: {},
formatMoney(amount, currencyCode) {
console.log('formatting', amount, currencyCode);
if('' === currencyCode) {
currencyCode = 'EUR';
}
return formatMoney(amount, currencyCode);
},
format(date) {
@@ -95,6 +101,12 @@ let show = function () {
}
}
}
// at this point do the inline change fields
//inlineEdit('journal_description')
const usernameEl = document.getElementById('journal_description');
const popover = new DarkEditable(usernameEl, {mode: 'inline', url: '/something-else'});
}).catch((error) => {
// todo auto generated.
this.notifications.error.show = true;