feat: improve validation errors and loosen up in validation based on user requests

This commit is contained in:
Nikolaj Fabricius-Bjerre 2024-04-23 22:18:04 +02:00
parent 095b7ade2d
commit b6f6b3fc3a
5 changed files with 38 additions and 17 deletions

View File

@ -128,6 +128,14 @@ function populate(){
}
await Promise.all(elems.map(async (el) => {
console.log({ dataset: el.dataset })
if (Object.keys(el.dataset).includes('optional') && ['', undefined, null].includes(el.value)) {
return;
}
const humanName = el.placeholder || el.id || el.dataset?.var || 'ukendt';
if(el.dataset.is === 'number'){
try {
const number = parseInt(el.value);
@ -135,23 +143,23 @@ function populate(){
}
catch(e) { throw {
el: el,
name: 'Client-side input validering',
name: `Valideringsfejl for feltet '${humanName}'`,
message: 'Værdien kunne ikke konverteres til et tal'
}; }
}
else if(el.dataset.is === 'boolean') {
if(typeof(el.checked) !== 'boolean') throw {
el: el,
name: 'Client-side input validering',
name: `Valideringsfejl for feltet '${humanName}'`,
message: 'Checkboxens værdi er ikke gyldig :S'
};
addValue(el.dataset.var, el.checked);
}
else if(el.dataset.is === 'string'){
if(typeof(el.value) !== 'string' || el.value === '') throw{
if(typeof(el.value) !== 'string' || el.value === '') throw {
el: el,
name: 'Client-side input validering',
message: 'Teksten kunne ikke valideres'
name: `Valideringsfejl for feltet '${humanName}'`,
message: 'Teksten er tom eller ugyldig'
};
addValue(el.dataset.var, el.value);
}
@ -165,6 +173,7 @@ function populate(){
});
}
// TODO: fix this !
function modal_toggle(name){
const modal = document.querySelector(`#modal-${name}`);
if(modal.className.includes('show')){

2
public/js.min.js vendored
View File

@ -1 +1 @@
var isBool=a=>typeof a=='boolean',isString=a=>typeof a=='string',{keys}=Object,$=str=>{const elems=document.querySelectorAll(str);if(!elems)throw Error(`DOM elem '${str}' not found`);return (elems.length==1)?elems[0]:elems};window.pdfgen={data:{},conf:{disabled:[]}};function merge_obj(obj1,obj2){if(!obj1)return obj2;for(const k of keys(obj2))typeof obj2[k]!=='object'?obj1[k]=obj2[k]:obj1[k]=merge_obj(obj1[k],obj2[k]);return obj1}function error(err){err.name&&err.message?alert(`${err.name}\n${err.message}`):isString((err))&&alert(err);err.el&&typeof err.el=='object'&&document.body.contains(err.el)&&setTimeout(()=>{err.el.focus();err.el.select()},100)}async function cvrapi(el){var cvr=el.value;let resjson=null;if(!/\d{8}/g.test(cvr))return error({el:el,name:'Forkert CVR format',message:' '});var resobj=await fetch(`/api/cvr/${cvr}`);try {resjson=await resobj.json()} catch (e) {return error({el:el,name:'Kunne ikke parse respons body til JSON',message:'Serveren svarede ikke korrekt, skriv gerne en fejlrapport.'})}if(resobj.status==404)return error({el:el,name:'Kunne ikke finde et firma',message:`med CVR-nummer ${cvr}`});if(!resobj.ok)return error({el:el,name:'Server error',message:el.error});return resjson}function format_date(d=new Date()){var is_date=Object.prototype.toString.call(d)=='[object Date]';if(!is_date&&isString(d))try {d=new Date(d)} catch (e) {throw e} else if(!is_date)throw Error('Invalid date!');return`${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()}`}function populate(){return new Promise(async (resolve,reject)=>{let elems=[...document.getElementsByClassName('var')];elems=elems.filter(el=>!window.pdfgen.conf.disabled.includes(el.id));var addValue=(key,val)=>{if(key.includes('.')){const all_keys=[].concat(key.split('.')),keys=key.split('.').reverse();let obj={};obj[keys.shift()]=val;for(const[,k] of keys.entries()){const deep={};deep[k]=obj;obj=deep}window.pdfgen.data=merge_obj(window.pdfgen.data,obj)}else window.pdfgen.data[key]=val};await Promise.all(elems.map(async el=>{if(el.dataset.is=='number')try {var number=parseInt(el.value);addValue(el.dataset.var,number)} catch (e) {throw {el:el,name:'Client-side input validering',message:'Værdien kunne ikke konverteres til et tal'}} else if(el.dataset.is=='boolean'){if(!isBool(el.checked))throw {el:el,name:'Client-side input validering',message:'Checkboxens værdi er ikke gyldig :S'};addValue(el.dataset.var,el.checked)} else if(el.dataset.is=='string'){if(!isString(el.value)||el.value=='')throw {el:el,name:'Client-side input validering',message:'Teksten kunne ikke valideres'};addValue(el.dataset.var,el.value)}})).then(()=>resolve()).catch(e=>{e.el&&(e.el.className+=' vali_err',setTimeout(()=>e.el.className=e.el.className.replaceAll('\ vali_err',''),3800));return reject(e)})})}function modal_toggle(name){var modal=document.querySelector(`#modal-${name}`);modal.className.includes('show')?modal.className=modal.className.replaceAll('\ show',''):modal.className+=' show'}window.addEventListener('DOMContentLoaded',e=>{var inputs=[...document.querySelectorAll('input.var[type="file"]')];inputs.filter(el=>el.dataset.is=='image').forEach(el=>el.addEventListener('change',async evt=>{!window.pdfgen.data.files&&(window.pdfgen.data.files={});let resjson;var data=new FormData();if(!el.files[0])throw {el:el,name:'Client-side input validering',message:'Du mangler at vælge en fil til upload'};data.append('file',el.files[0]);var resobj=await fetch('/pdf/upload/image',{method:'POST',body:data});try {resjson=await resobj.json()} catch (e) {throw {el:el,name:'Kunne ikke parse respons body til JSON',message:'Serveren svarede ikke korrekt, skriv gerne en fejlrapport.'}}if(!resobj.ok){if(!resjson.error)return reject(resjson);throw {el:el,name:'Kunne ikke uploade fil',message:resjson.error}}window.file_reset_timer&&setTimeout(()=>{delete window.pdfgen.data.files[el.dataset.var];if(el.dataset.preview)$(`#${el.dataset.preview}`).innerHTML=''},window.file_reset_timer);if(el.dataset.preview){var preview_container=$(`#${el.dataset.preview}`),preview_img=document.createElement('img');preview_container.innerHTML='';preview_img.className='image_preview';preview_img.src=`file/${resjson.fileid}`;preview_container.appendChild(preview_img)}window.pdfgen.data.files[el.dataset.var]=resjson.fileid}))});
var isBool=a=>typeof a=='boolean',isString=a=>typeof a=='string',{keys}=Object,$=str=>{const elems=document.querySelectorAll(str);if(!elems)throw Error(`DOM elem '${str}' not found`);return (elems.length==1)?elems[0]:elems};window.pdfgen={data:{},conf:{disabled:[]}};function merge_obj(obj1,obj2){if(!obj1)return obj2;for(const k of keys(obj2))typeof obj2[k]!=='object'?obj1[k]=obj2[k]:obj1[k]=merge_obj(obj1[k],obj2[k]);return obj1}function error(err){err.name&&err.message?alert(`${err.name}\n${err.message}`):isString((err))&&alert(err);err.el&&typeof err.el=='object'&&document.body.contains(err.el)&&setTimeout(()=>{err.el.focus();err.el.select()},100)}async function cvrapi(el){var cvr=el.value;let resjson=null;if(!/\d{8}/g.test(cvr))return error({el:el,name:'Forkert CVR format',message:' '});var resobj=await fetch(`/api/cvr/${cvr}`);try {resjson=await resobj.json()} catch (e) {return error({el:el,name:'Kunne ikke parse respons body til JSON',message:'Serveren svarede ikke korrekt, skriv gerne en fejlrapport.'})}if(resobj.status==404)return error({el:el,name:'Kunne ikke finde et firma',message:`med CVR-nummer ${cvr}`});if(!resobj.ok)return error({el:el,name:'Server error',message:el.error});return resjson}function format_date(d=new Date()){var is_date=Object.prototype.toString.call(d)=='[object Date]';if(!is_date&&isString(d))try {d=new Date(d)} catch (e) {throw e} else if(!is_date)throw Error('Invalid date!');return`${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()}`}function populate(){return new Promise(async (resolve,reject)=>{let elems=[...document.getElementsByClassName('var')];elems=elems.filter(el=>!window.pdfgen.conf.disabled.includes(el.id));var addValue=(key,val)=>{if(key.includes('.')){const all_keys=[].concat(key.split('.')),keys=key.split('.').reverse();let obj={};obj[keys.shift()]=val;for(const[,k] of keys.entries()){const deep={};deep[k]=obj;obj=deep}window.pdfgen.data=merge_obj(window.pdfgen.data,obj)}else window.pdfgen.data[key]=val};await Promise.all(elems.map(async el=>{if(keys(el.dataset).includes('optional')&&['',void 0,null].includes(el.value))return;var humanName=el.placeholder||el.id||el.dataset?.var||'ukendt';if(el.dataset.is=='number')try {var number=parseInt(el.value);addValue(el.dataset.var,number)} catch (e) {throw {el:el,name:`Valideringsfejl for feltet '${humanName}'`,message:'Værdien kunne ikke konverteres til et tal'}} else if(el.dataset.is=='boolean'){if(!isBool(el.checked))throw {el:el,name:`Valideringsfejl for feltet '${humanName}'`,message:'Checkboxens værdi er ikke gyldig :S'};addValue(el.dataset.var,el.checked)} else if(el.dataset.is=='string'){if(!isString(el.value)||el.value=='')throw {el:el,name:`Valideringsfejl for feltet '${humanName}'`,message:'Teksten er tom eller ugyldig'};addValue(el.dataset.var,el.value)}})).then(()=>resolve()).catch(e=>{e.el&&(e.el.className+=' vali_err',setTimeout(()=>e.el.className=e.el.className.replaceAll('\ vali_err',''),3800));return reject(e)})})}function modal_toggle(name){var modal=document.querySelector(`#modal-${name}`);modal.className.includes('show')?modal.className=modal.className.replaceAll('\ show',''):modal.className+=' show'}window.addEventListener('DOMContentLoaded',e=>{var inputs=[...document.querySelectorAll('input.var[type="file"]')];inputs.filter(el=>el.dataset.is=='image').forEach(el=>el.addEventListener('change',async evt=>{!window.pdfgen.data.files&&(window.pdfgen.data.files={});let resjson;var data=new FormData();if(!el.files[0])throw {el:el,name:'Client-side input validering',message:'Du mangler at vælge en fil til upload'};data.append('file',el.files[0]);var resobj=await fetch('/pdf/upload/image',{method:'POST',body:data});try {resjson=await resobj.json()} catch (e) {throw {el:el,name:'Kunne ikke parse respons body til JSON',message:'Serveren svarede ikke korrekt, skriv gerne en fejlrapport.'}}if(!resobj.ok){if(!resjson.error)return reject(resjson);throw {el:el,name:'Kunne ikke uploade fil',message:resjson.error}}window.file_reset_timer&&setTimeout(()=>{delete window.pdfgen.data.files[el.dataset.var];if(el.dataset.preview)$(`#${el.dataset.preview}`).innerHTML=''},window.file_reset_timer);if(el.dataset.preview){var preview_container=$(`#${el.dataset.preview}`),preview_img=document.createElement('img');preview_container.innerHTML='';preview_img.className='image_preview';preview_img.src=`file/${resjson.fileid}`;preview_container.appendChild(preview_img)}window.pdfgen.data.files[el.dataset.var]=resjson.fileid}))});

View File

@ -24,14 +24,16 @@
<meta name="twitter:description" content="Simpel, open source faktura pdf generering. Generer en faktura uden at blive overvældet af reklamer der ikke respekterer for dit privatliv.">
<meta name="twitter:title" content="FriFaktura.dk · simpel faktura generering, uden reklamer eller dataindsamling.">
<%
const cssUrl = process.env.NODE_ENV === 'development' ? '/css.css' : '/css.min.css'
const cssVersion = 1;
const cssUrl = process.env.NODE_ENV === 'development' ? '/css.css' : '/css.min.css?v=${cssVersion}'
%>
<link rel="stylesheet" type="text/css" href="<%= cssUrl %>" />
<script>
window.file_reset_timer = <%= locals.file_reset_timer ? file_reset_timer : 0 %>;
window.file_reset_timer = <%= locals?.file_reset_timer ? file_reset_timer : 0 %>;
</script>
<%
const jsUrl = process.env.NODE_ENV === 'development' ? '/js.js' : '/js.min.js';
const jsVersion = 2;
const jsUrl = process.env.NODE_ENV === 'development' ? '/js.js' : `/js.min.js?v=${jsVersion}`;
%>
<script src="<%= jsUrl %>"></script>
<script src="/rain.min.js"></script>

View File

@ -59,7 +59,7 @@
<label>Navn</label>
</div>
<div>
<input id="private_seller_cpr" data-var="seller.cpr" class="var small" type="text" data-is="string" placeholder="Indtast CPR" />
<input id="private_seller_cpr" data-var="seller.cpr" class="var small" type="text" data-is="string" data-optional placeholder="Indtast CPR" />
</div>
<div class="doublecell">
<input style="width: 100%" id="private_seller_name" data-var="seller.name" class="var small" type="text" data-is="string" placeholder="Indtast navn" />
@ -193,10 +193,10 @@
<div></div>
<div></div>
<div>
<input class="var small" type="text" data-var="accountno" data-is="string" value="12312312" />
<input class="var small" type="text" data-var="accountno" data-is="string" data-optional value="12312312" />
</div>
<div>
<input class="var small" type="text" data-var="regno" data-is="string" value="1234" />
<input class="var small" type="text" data-var="regno" data-is="string" data-optional value="1234" />
</div>
</div>
@ -474,7 +474,7 @@
$('#submit').addEventListener('click', async (event) => {
// validate stuff manually put in window.pdfgen.data from js in this template file
if(!Array.isArray(window.pdfgen.data.work) || !window.pdfgen.data.work.length) {
if (!Array.isArray(window.pdfgen.data.work) || !window.pdfgen.data.work.length) {
return error({
el: document.getElementById('work_desc'),
name: 'Du mangler at tilføje produkt eller service',
@ -482,7 +482,7 @@
});
}
if(typeof(window.pdfgen.data.seller) !== 'object') {
if (typeof(window.pdfgen.data.seller) !== 'object') {
return error({
el: document.getElementById('seller_cvr_input'),
name: 'Du mangler at indtaste dit firmas oplysninger',
@ -490,7 +490,7 @@
});
}
if(typeof(window.pdfgen.data.buyer) !== 'object') {
if (typeof(window.pdfgen.data.buyer) !== 'object') {
let el = null;
if(Array.isArray(window.pdfgen.conf.disabled)){
if(window.pdfgen.conf.disabled.length) {

View File

@ -13,6 +13,17 @@ function hasLogo(){
return (locals.files && locals.files.logo);
}
function accountInfo() {
const result = '';
if (typeof locals?.accountno === 'number') {
result += `& & \\textbf{Konto nr.:} & ${locals.accountno} \\\\\n`;
}
if (typeof locals?.regno === 'number') {
result += `& & \\textbf{Reg nr.:} & ${locals.regno} \\\\n`;
}
return result;
}
%>\documentclass[a4paper]{article}
\usepackage{fancyhdr, graphicx, tabularx, changepage, multicol, array}
@ -58,8 +69,7 @@ function hasLogo(){
<%- buyer["name"] %> & & \textbf{Fakturanummer:} & <%= invoiceno %> \\
<%- buyer["address"] %>, <%- buyer["zipcode"] %> <%- buyer["city"] %> & & \textbf{Fakturadato:} & <%= sent_date %> \\
<%= locals.buyer['vat'] ? 'CVR ' + buyer['vat'] + ' ' : '' %>& & \textbf{Forfaldsdato:} & <%= pay_date %> \\
& & \textbf{Konto nr.:} & <%= accountno %> \\
& & \textbf{Reg nr.:} & <%= regno %> \\ \\ \\ \\
<%- accountInfo() %> \\ \\ \\
\textbf{\textsf{Beskrivelse}} & \textbf{\textsf{Antal}} & \textbf{\textsf{Pris}} & \textbf{\textsf{Sum}} \\
\hline \\