From 4bec738cc33abe65a1ad65e78401de123b4f4d3f Mon Sep 17 00:00:00 2001 From: Jay Colson Date: Mon, 15 Mar 2021 12:35:03 +0000 Subject: [PATCH] #103 little bit of input validation using css/javascript --- http_root/css/site.css | 52 +++++++++++++++++++++++---- http_views/events.pug | 80 +++++++++++++++++++++++++++--------------- index.js | 5 ++- 3 files changed, 100 insertions(+), 37 deletions(-) diff --git a/http_root/css/site.css b/http_root/css/site.css index 3bb7ff0..9a9b4c3 100644 --- a/http_root/css/site.css +++ b/http_root/css/site.css @@ -9,7 +9,17 @@ input[type=text] { resize: vertical; padding: 10px; margin: 10px 0; - border: 0; + border: 2px solid; + box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06); + border-radius: 10px; +} + +input[type=number] { + width: 100%; + resize: vertical; + padding: 10px; + margin: 10px 0; + border: 2px solid; box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06); border-radius: 10px; } @@ -19,7 +29,7 @@ input[type=date] { resize: vertical; padding: 10px; margin: 10px 0; - border: 0; + border: 2px solid; box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06); border-radius: 10px; } @@ -29,24 +39,48 @@ input[type=time] { resize: vertical; padding: 10px; margin: 10px 0; - border: 0; + border: 2px solid; box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06); border-radius: 10px; } +input:optional { + border-color: gray; +} + +input:required:valid { + border-color: green; +} + +input:invalid { + border-color: red; +} + textarea { width: 100%; resize: vertical; padding: 10px; margin: 10px 0; border-radius: 10px; - border: 0; + border: 2px solid; box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.06); height: 175px; word-wrap: break-word; } -textareax { +textarea:optional { + border-color: gray; +} + +textarea:required:valid { + border-color: green; +} + +textarea:invalid { + border-color: red; +} + +/* textareax { width: 100%; height: 150px; overflow-wrap: break-word; @@ -57,7 +91,7 @@ textareax { overflow-y: scroll; overflow-x: hidden; overflow: -moz-scrollbars-vertical; -} +} */ button { /* remove default behavior */ @@ -87,8 +121,12 @@ button { display: block; color: lightblue; text-align: center; - padding: 14px 16px; + padding: 14px; + /* 14px 16px; */ text-decoration: none; + border-radius: 10px; + border: 0; + box-shadow: 4px 4px 10px lightblue; } /* Links - change color on hover */ diff --git a/http_views/events.pug b/http_views/events.pug index 893229e..a1eedac 100644 --- a/http_views/events.pug +++ b/http_views/events.pug @@ -2,46 +2,68 @@ extends layout.pug block content - script. - event=!{event?JSON.stringify(event):'{}'}; - async function setEvent() { - console.log('setEvent fired: '+event.guildID); - event.title=document.getElementById('event-info').value; - event.dm=document.getElementById('event-dm').value; - event.duration_hours=document.getElementById('event-duration_hours').value; - event.number_player_slots=document.getElementById('event-number_player_slots').value; - event.campaign=document.getElementById('event-campaign').value; - event.description=document.getElementById('event-description').value; - event.date_time=new Date(document.getElementById('event-date').value+'T'+document.getElementById('event-time').value); - try { - const jsonResponse = await postData('#{Config.httpServerURL}/events/set', event); - document.getElementById('event-info').innerHTML='Updated!'; - } catch (error) { - document.getElementById('event-info').innerHTML='Error: '+error.message; - } - } .thebody - div(id="event-info") - form + div(id='event-info') + form(id='event-form') .row .column .column | Title: - input(type='text' name='title' id='event-title' placeholder='Title' value=`${event&&event.title?event.title:''}`) + input(type='text' name='title' id='event-title' placeholder='Title' required value=`${event&&event.title?event.title:''}`) | DM/GM: input(type='text' name='dm' id='event-dm' placeholder='DM/GM' value=`${event&&event.dm?event.dm:''}`) | Duration (in hours): - input(type='text' name='duration_hours' id='event-duration_hours' placeholder='Duration (in hours)' value=`${event&&event.duration_hours?event.duration_hours:''}`) - | Start (Date): - input(type='date' name='date' id='event-date' value=`${event?event.date_time.toISOString().substring(0,10):''}`) - | Start (Time in GMT): - input(type='time' name='time' id='event-time' value=`${event?event.date_time.toISOString().substring(11,16):''}`) + input(type='number' name='duration_hours' id='event-duration_hours' min='0' step='.01' required placeholder='Duration (in hours)' value=`${event&&event.duration_hours?event.duration_hours:''}`) + | Start Date (in #{userConfig.timezone} TZ): + input(type='date' name='date' id='event-date' required value=`${event?event.date_time.toISOString().substring(0,10):''}`) + | Start Time (in #{userConfig.timezone} TZ): + input(type='time' name='time' id='event-time' required value=`${event?event.date_time.toISOString().substring(11,16):''}`) .column | Number of Players: - input(type='text' name='number_player_slots' id='event-number_player_slots' placeholder='Number of Players' value=`${event&&event.number_player_slots?event.number_player_slots:''}`) + input(type='number' name='number_player_slots' id='event-number_player_slots' placeholder='Number of Players' min='1' required value=`${event&&event.number_player_slots?event.number_player_slots:''}`) | Campaign: input(type='text' name='campaign' id='event-campaign' placeholder='Campaign' value=`${event&&event.campaign?event.campaign:''}`) | Description: - textarea(name='description' id='event-description' placeholder='Description') #{event&&event.description?event.description:''} + textarea(name='description' id='event-description' placeholder='Description' required) #{event&&event.description?event.description:''} .column - button(type='button' onclick='setEvent();') Save \ No newline at end of file + button(type='button' id='event-save' onclick='setEvent();') Save + script. + event=!{event?JSON.stringify(event):'{}'}; + // if timezone isn't set yet, disable the form + if ('#{userConfig?userConfig.timezone:''}' == '') { + document.getElementById('event-info').innerHTML='You must set your timezone prior to editing / creating events.'; + document.getElementById('event-save').disabled=true; + document.getElementById('event-form').style.display='none'; + } + async function setEvent() { + console.log(`setEvent fired: ${event.guildID} or #{guildConfig.guildID}`); + const invalids = document.querySelectorAll(':invalid'); + if (invalids.length > 0) { + for (invalid of invalids) { + console.log('invalid input: '+invalid.name); + } + document.getElementById('event-info').innerHTML='Invalid form fields, please check your entries.'; + } else if ('#{guildConfig?guildConfig.guildID:''}' == '') { + document.getElementById('event-info').innerHTML='Unknown guild to create for, please follow the link from `!event create` on discord.'; + } else { + event.guildID=#{guildConfig.guildID}; + event.userID=#{discordMe.id}; + event.title=document.getElementById('event-title').value; + event.dm=document.getElementById('event-dm').value; + event.duration_hours=document.getElementById('event-duration_hours').value; + event.number_player_slots=document.getElementById('event-number_player_slots').value; + event.campaign=document.getElementById('event-campaign').value; + event.description=document.getElementById('event-description').value; + event.date_time=new Date(document.getElementById('event-date').value+'T'+document.getElementById('event-time').value); + try { + const responseJson = await postData('#{Config.httpServerURL}/events/set', event); + if (responseJson.status == 'false') { + document.getElementById('event-info').innerHTML='Could not update due to error from backend, sorry.'; + } else { + document.getElementById('event-info').innerHTML='Updated!'; + } + } catch (error) { + document.getElementById('event-info').innerHTML='Error: '+error.message; + } + } + } \ No newline at end of file diff --git a/index.js b/index.js index 0125a93..93f8c46 100644 --- a/index.js +++ b/index.js @@ -73,6 +73,7 @@ let server = app .use(session({ secret: 'grant', saveUninitialized: true, resave: false, maxAge: Date.now() + (7 * 86400 * 1000) })) .use(grant) .use(ROUTE_ROOT, express.static(Config.httpStaticDir)) + .use(express.json()) .use(async function (request, response, next) { console.log(`in middleware checking if I need to update guildID, guildID status: ${request.session.guildConfig ? true : false}`); const requestUrl = new URL(request.url, `${request.protocol}://${request.headers.host}`); @@ -234,7 +235,9 @@ let server = app console.log(`event is not owned by current user, dereferencing`); event = undefined; } - response.render('events', { title: 'Events', event: event, Config: Config, guildConfig: request.session.guildConfig, discordMe: request.session.discordMe }) + let userConfig = await UserModel.findOne({userID: request.session.discordMe.id, guildID: request.session.guildConfig.guildID}); + // console.log(userConfig); + response.render('events', { title: 'Events', event: event, Config: Config, guildConfig: request.session.guildConfig, discordMe: request.session.discordMe, userConfig: userConfig }) } catch (error) { console.error(error.message); response.setHeader('Content-Type', 'text/html');