added transaction date to transaction form

This commit is contained in:
Fran Jurmanović
2021-06-19 14:14:12 +02:00
parent dc1c7590fe
commit 6fa3c9266c
12 changed files with 119 additions and 32 deletions

View File

@@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@github/catalyst": "^1.1.3", "@github/catalyst": "^1.1.3",
"dayjs": "^1.10.5",
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
"validator": "^13.6.0" "validator": "^13.6.0"
}, },

View File

@@ -79,6 +79,10 @@ class BaseElement extends HTMLElement {
update = (): void => { update = (): void => {
render(this.render(), this); render(this.render(), this);
if (this.shadowRoot) {
const renderSlot = () => html`<slot><slot></slot></slot>`;
render(renderSlot(), this.shadowRoot);
}
this.bindEvents('data-action'); this.bindEvents('data-action');
this.bindEvents('app-action'); this.bindEvents('app-action');
this.updateCallback(); this.updateCallback();

View File

@@ -39,6 +39,7 @@ class Validator {
.reverse() .reverse()
.map((rule) => { .map((rule) => {
let args = []; let args = [];
let inps = [];
if (rule.includes('[') && rule.includes(']')) { if (rule.includes('[') && rule.includes(']')) {
const begSep = rule.lastIndexOf('['); const begSep = rule.lastIndexOf('[');
const endSep = rule.lastIndexOf(']'); const endSep = rule.lastIndexOf(']');
@@ -51,7 +52,7 @@ class Validator {
const begBr = arg.lastIndexOf('('); const begBr = arg.lastIndexOf('(');
const endBr = arg.lastIndexOf(')'); const endBr = arg.lastIndexOf(')');
const field = arg.slice(begBr + 1, endBr); const field = arg.slice(begBr + 1, endBr);
inps = [...inps, this.form.getInput(field)];
return this.form?.values[field]; return this.form?.values[field];
} }
}); });
@@ -69,10 +70,19 @@ class Validator {
} }
} }
if (!valid) { if (!valid) {
const error = ruleArray let error = ruleArray
? validRule?.[1]?.replaceAll('{- name}', firstUpper(this.name?.toString())) ? validRule?.[1]?.replaceAll('{- name}', firstUpper(this.name?.toString()))
: validatorErrors[rule]?.replaceAll('{- name}', firstUpper(this.name?.toString())); : validatorErrors[rule]?.replaceAll('{- name}', firstUpper(this.name?.toString()));
if (error) { if (error) {
if (inps?.length > 1) {
inps.forEach((arg) => {
if (arg) {
error.replaceAll(`{- ${arg?.type}}`, firstUpper(arg?.name?.toString()));
}
});
} else if (inps?.length == 1) {
error = error.replaceAll('{- field}', firstUpper(inps[0]?.name?.toString()));
}
this.error = error; this.error = error;
} }
} }

View File

@@ -88,7 +88,7 @@ class AppDropdownElement extends BaseComponentElement {
return this.value; return this.value;
} }
validate = (): boolean => { validate = (change = false): boolean => {
return this.validator.validate(); return this.validator.validate();
}; };

View File

@@ -12,6 +12,7 @@ class AppFormElement extends BaseComponentElement {
@querys inputField: NodeListOf<InputFieldElement>; @querys inputField: NodeListOf<InputFieldElement>;
@querys appDropdown: NodeListOf<AppDropdownElement>; @querys appDropdown: NodeListOf<AppDropdownElement>;
@attr custom: string; @attr custom: string;
@attr renderInput: string;
@attr hasCancel: string; @attr hasCancel: string;
slotted: any; slotted: any;
isValid: boolean = false; isValid: boolean = false;
@@ -24,6 +25,10 @@ class AppFormElement extends BaseComponentElement {
return findMethod(this.custom, this.appMain); return findMethod(this.custom, this.appMain);
} }
get customRender() {
return findMethod(this.renderInput, this.appMain);
}
public inputChange = (e) => { public inputChange = (e) => {
this.validate(); this.validate();
this.update(); this.update();
@@ -103,13 +108,15 @@ class AppFormElement extends BaseComponentElement {
} }
elementConnected = (): void => { elementConnected = (): void => {
if (this.renderInput) {
this.update();
} else {
const _template = document.createElement('template'); const _template = document.createElement('template');
const _slot = this.innerHTML; const _slot = this.innerHTML;
_template.innerHTML = _slot; _template.innerHTML = _slot;
this.innerHTML = null;
this.update(); this.update();
this.formElement?.replaceChild(_template.content, this.innerSlot);
this.formElement.replaceChild(_template.content, this.innerSlot); }
}; };
render = (): TemplateResult => { render = (): TemplateResult => {
@@ -139,7 +146,7 @@ class AppFormElement extends BaseComponentElement {
return html` return html`
<div class="app-form"> <div class="app-form">
<form app-action="submit:app-form#onSubmit" data-target="app-form.formElement"> <form app-action="submit:app-form#onSubmit" data-target="app-form.formElement">
<slot data-target="app-form.innerSlot"></slot> ${this.renderInput ? this.customRender() : html`<slot data-target="app-form.innerSlot"></slot>`}
${renderError(this.error)} ${renderError(this.error)}
<div class="form-buttons"> <div class="form-buttons">
<div class="button-content">${renderSubmit(this.isValid)}${renderCancel(isTrue(this.hasCancel))}</div> <div class="button-content">${renderSubmit(this.isValid)}${renderCancel(isTrue(this.hasCancel))}</div>

View File

@@ -13,7 +13,7 @@ class AppMainElement extends HTMLElement {
public authStore: AuthStore; public authStore: AuthStore;
private httpClient: HttpClient; private httpClient: HttpClient;
public appService: AppService; public appService: AppService;
public shadow: any; //public shadow: any;
@target appModal: AppModalElement; @target appModal: AppModalElement;
@target mainRoot: AppRootElement; @target mainRoot: AppRootElement;
@target appLoader: AppLoaderElement; @target appLoader: AppLoaderElement;

View File

@@ -13,12 +13,14 @@ class InputFieldElement extends BaseComponentElement {
@attr type: string; @attr type: string;
@attr label: string; @attr label: string;
@attr rules: string; @attr rules: string;
@attr customAction: string;
@target main: HTMLElement; @target main: HTMLElement;
@target inp: HTMLElement; @target inp: HTMLElement;
@closest appForm: AppFormElement; @closest appForm: AppFormElement;
valid: boolean; valid: boolean;
displayError: boolean; displayError: boolean;
randId: string; randId: string;
changed: boolean = false;
validator: Validator; validator: Validator;
@@ -54,7 +56,15 @@ class InputFieldElement extends BaseComponentElement {
} }
validate = (): boolean => { validate = (): boolean => {
return this.validator.validate(); const valid = this.validator.validate();
if (valid && this.displayError) {
this.displayError = false;
this.update();
} else if (this.changed && !valid) {
this.displayError = true;
this.update();
}
return valid;
}; };
validateDisplay = () => { validateDisplay = () => {
@@ -69,6 +79,9 @@ class InputFieldElement extends BaseComponentElement {
}; };
inputChange = (e) => { inputChange = (e) => {
if (!this.changed && e?.target?.value) {
this.changed = true;
}
this.validate(); this.validate();
this.appForm?.inputChange(e); this.appForm?.inputChange(e);
}; };
@@ -96,6 +109,7 @@ class InputFieldElement extends BaseComponentElement {
app-action=" app-action="
input:input-field#inputChange input:input-field#inputChange
blur:input-field#validateDisplay blur:input-field#validateDisplay
${this.customAction ? this.customAction : ''}
" "
/>`; />`;
}; };

View File

@@ -1,6 +1,7 @@
import isEmail from 'validator/lib/isEmail'; import isEmail from 'validator/lib/isEmail';
import isDate from 'validator/lib/isDate'; import isDate from 'validator/lib/isDate';
import isNumeric from 'validator/lib/isNumeric'; import isNumeric from 'validator/lib/isNumeric';
import isAfter from 'validator/lib/isAfter';
import matches from 'validator/lib/matches'; import matches from 'validator/lib/matches';
const validator = { const validator = {
@@ -10,6 +11,7 @@ const validator = {
matches: matches, matches: matches,
is_same: [isSame, '{- name} needs to be same to {- field}.'], is_same: [isSame, '{- name} needs to be same to {- field}.'],
required: [required, '{- name} is required.'], required: [required, '{- name} is required.'],
is_after: [isAfter, '{- name} needs to be after {- field}.'],
}; };
function required(str: string): boolean { function required(str: string): boolean {

View File

@@ -43,6 +43,14 @@ class SubscriptionCreateElement extends BasePageElement {
} }
}; };
get hasEndCheck(): InputFieldElement | AppDropdownElement {
for (const i in this.inputs) {
if (this.inputs[i]?.name == 'hasEnd') {
return this.inputs[i];
}
}
}
get nameInput(): InputFieldElement | AppDropdownElement { get nameInput(): InputFieldElement | AppDropdownElement {
for (const i in this.inputs) { for (const i in this.inputs) {
if (this.inputs[i]?.name == 'name') { if (this.inputs[i]?.name == 'name') {
@@ -148,10 +156,16 @@ class SubscriptionCreateElement extends BasePageElement {
return _return; return _return;
} }
render = (): TemplateResult => { onCheck = () => {
const renderInput = (type, name, label, rules, hide?) => { this.appForm.update();
this.appForm.validate();
this.appForm.update();
};
renderForms = () => {
const renderInput = (type, name, label, rules, hide?, customAction?) => {
if (hide) { if (hide) {
return html``; return null;
} }
return html`<input-field return html`<input-field
data-type="${type}" data-type="${type}"
@@ -159,6 +173,7 @@ class SubscriptionCreateElement extends BasePageElement {
data-label="${label}" data-label="${label}"
data-targets="subscription-create.inputs" data-targets="subscription-create.inputs"
data-rules="${rules}" data-rules="${rules}"
data-custom-action="${customAction || ''}"
></input-field>`; ></input-field>`;
}; };
@@ -174,15 +189,19 @@ class SubscriptionCreateElement extends BasePageElement {
data-fetch="${fetch}" data-fetch="${fetch}"
></app-dropdown>`; ></app-dropdown>`;
}; };
return html` return html`
<app-form <div slot="inputs">
data-custom="subscription-create#onSubmit"
data-has-cancel="true"
data-target="subscription-create.appForm"
>
${renderInput('number', 'amount', 'Amount', 'required')} ${renderInput('number', 'amount', 'Amount', 'required')}
${renderInput('text', 'description', 'Description', 'required')} ${renderInput('text', 'description', 'Description', 'required')}
${renderInput('date', 'startDate', 'Start date', 'required')}
${renderInput('checkbox', 'hasEnd', 'Existing End Date', '', false, 'change:subscription-create#onCheck')}
${renderInput(
'date',
'endDate',
'End date',
'required|is_after[field(startDate)]',
!(this.hasEndCheck?.inp as HTMLInputElement)?.checked
)}
${renderDropdown( ${renderDropdown(
'subscription-create#getWallets', 'subscription-create#getWallets',
'wallet', 'wallet',
@@ -193,7 +212,18 @@ class SubscriptionCreateElement extends BasePageElement {
${renderDropdown('subscription-create#getTypes', 'transactionType', 'Transaction Type', 'required')} ${renderDropdown('subscription-create#getTypes', 'transactionType', 'Transaction Type', 'required')}
${renderInput('number', 'customRange', 'Every', 'required')} ${renderInput('number', 'customRange', 'Every', 'required')}
${renderDropdown('subscription-create#getSubs', 'subscriptionType', 'Subscription Type', 'required')} ${renderDropdown('subscription-create#getSubs', 'subscriptionType', 'Subscription Type', 'required')}
${this.errorMessage ? html`<div>${this.errorMessage}</div>` : html``} ${this.errorMessage ? html`<div>${this.errorMessage}</div>` : html``}</template
>`;
};
render = (): TemplateResult => {
return html`
<app-form
data-custom="subscription-create#onSubmit"
data-has-cancel="true"
data-target="subscription-create.appForm"
data-render-input="subscription-create#renderForms"
>
</app-form> </app-form>
`; `;
}; };

View File

@@ -5,6 +5,7 @@ import { AppFormElement, InputFieldElement } from 'components/';
import { RouterService } from 'core/services'; import { RouterService } from 'core/services';
import { BasePageElement } from 'common/'; import { BasePageElement } from 'common/';
import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement'; import { AppDropdownElement } from 'components/app-dropdown/AppDropdownElement';
import dayjs from 'dayjs';
@controller @controller
class TransactionCreateElement extends BasePageElement { class TransactionCreateElement extends BasePageElement {
@@ -84,7 +85,15 @@ class TransactionCreateElement extends BasePageElement {
return; return;
} }
const { description: description, wallet: walletId, amount, transactionType: transactionTypeId } = values; const {
description: description,
wallet: walletId,
amount,
transactionType: transactionTypeId,
transactionDate,
} = values;
const formattedDate = dayjs(transactionDate).format();
const walletData = this.walletData; const walletData = this.walletData;
@@ -92,9 +101,11 @@ class TransactionCreateElement extends BasePageElement {
description, description,
amount, amount,
walletId: walletData && walletData.walletId ? walletData.walletId : walletId, walletId: walletData && walletData.walletId ? walletData.walletId : walletId,
transactionDate: formattedDate,
transactionTypeId: transactionTypeId:
walletData && walletData.transactionTypeId ? walletData.transactionTypeId : transactionTypeId, walletData && walletData.transactionTypeId ? walletData.transactionTypeId : transactionTypeId,
}; };
const response = await this.transactionService.post(formData); const response = await this.transactionService.post(formData);
if (response?.id) { if (response?.id) {
@@ -125,7 +136,7 @@ class TransactionCreateElement extends BasePageElement {
} }
render = (): TemplateResult => { render = (): TemplateResult => {
const renderInput = (type, name, label, rules, hide?) => { const renderInput = (type, name, label, rules, hide?, customAction?) => {
if (hide) { if (hide) {
return html``; return html``;
} }
@@ -135,6 +146,7 @@ class TransactionCreateElement extends BasePageElement {
data-label="${label}" data-label="${label}"
data-targets="transaction-create.inputs" data-targets="transaction-create.inputs"
data-rules="${rules}" data-rules="${rules}"
custom-action="${customAction}"
></input-field>`; ></input-field>`;
}; };
@@ -159,6 +171,7 @@ class TransactionCreateElement extends BasePageElement {
> >
${renderInput('number', 'amount', 'Amount', 'required')} ${renderInput('number', 'amount', 'Amount', 'required')}
${renderInput('text', 'description', 'Description', 'required')} ${renderInput('text', 'description', 'Description', 'required')}
${renderInput('date', 'transactionDate', 'Transaction date', 'required')}
${renderDropdown( ${renderDropdown(
'transaction-create#getWallets', 'transaction-create#getWallets',
'wallet', 'wallet',

View File

@@ -16,6 +16,7 @@ app-modal {
width: 960px; width: 960px;
min-height: 160px; min-height: 160px;
max-height: 560px; max-height: 560px;
overflow-y: auto;
padding-bottom: 10px; padding-bottom: 10px;
margin: 0; margin: 0;
top: 50%; top: 50%;
@@ -41,7 +42,7 @@ app-modal {
.button-content { .button-content {
position: absolute; position: absolute;
left: 4px; left: 4px;
bottom: 4px; margin-top: 20pxf;
} }
} }
} }

View File

@@ -2179,6 +2179,11 @@ dashdash@^1.12.0:
dependencies: dependencies:
assert-plus "^1.0.0" assert-plus "^1.0.0"
dayjs@^1.10.5:
version "1.10.5"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986"
integrity sha512-BUFis41ikLz+65iH6LHQCDm4YPMj5r1YFLdupPIyM4SGcXMmtiLQ7U37i+hGS8urIuqe7I/ou3IS1jVc4nbN4g==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
version "2.6.9" version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"