style forms and add form validation and form status block
This commit is contained in:
committed by
Benedikt Rötsch
parent
f2b2a5845e
commit
6430a2c849
@@ -77,12 +77,13 @@ datalist {
|
||||
|
||||
label {
|
||||
display: var(--fh-layout-display);
|
||||
margin: var(--fh-layout-margin);
|
||||
margin: var(--fh-layout-margin) 0 0;
|
||||
text-align: var(--fh-layout-text-align);
|
||||
font-weight: bold;
|
||||
font-size: 0.875em;
|
||||
|
||||
& + input {
|
||||
margin-top: calc(var(--grid-gutter) * 0.5 * -1)
|
||||
margin-top: calc(var(--grid-gutter) * 0.25)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,9 +234,13 @@ button[disabled] {
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus,
|
||||
option:focus,
|
||||
option:focus {
|
||||
border-color: var(--color-palette-blue);
|
||||
color: var(--fh-font-color);
|
||||
}
|
||||
|
||||
button:focus,
|
||||
.cta:focus {
|
||||
.cta:focus {
|
||||
background-color: var(--fh-button-hover-bg-color);
|
||||
color: var(--fh-button-hover-font-color);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,65 @@
|
||||
.form-item {
|
||||
@block form-item {
|
||||
& + .form-item {
|
||||
margin-top: var(--grid-gutter);
|
||||
}
|
||||
& .help-text {
|
||||
font-size: 0.9em;
|
||||
|
||||
& input {
|
||||
margin-bottom: calc(var(--grid-gutter) * 0.25);
|
||||
}
|
||||
|
||||
@element help-text {
|
||||
font-size: 0.875em;
|
||||
color: var(--color-text-grey);
|
||||
margin-top: calc(var(--grid-gutter) * -1);
|
||||
}
|
||||
|
||||
@element error-message {
|
||||
font-size: 0.875em;
|
||||
color: var(--color-text-error);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
background: url('/images/icon-error.svg') no-repeat left center;
|
||||
background-size: contain;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
padding-right: 0.3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@block status-block {
|
||||
display: flex;
|
||||
margin: var(--grid-gutter) 0;
|
||||
padding: calc(var(--grid-gutter) * 1);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
font-size: 0.875em;
|
||||
|
||||
@modifier success {
|
||||
background: var(--color-status-block-bg-success);
|
||||
}
|
||||
|
||||
@modifier error {
|
||||
background: var(--color-status-block-bg-error);
|
||||
}
|
||||
|
||||
@element icon {
|
||||
flex: 0 0 auto;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: calc(var(--grid-gutter) * 0.5);
|
||||
}
|
||||
|
||||
@element content {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
@element title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@element message {
|
||||
color: var(--color-status-block-message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
--color-palette-blue: #5c9fef;
|
||||
--color-palette-blue-medium: #3c80cf;
|
||||
--color-palette-blue-dark: #3072be;
|
||||
--color-palette-red: #cd3f39;
|
||||
|
||||
--color-text-default: #2a3039;
|
||||
--color-text-grey: #8091a5;
|
||||
--color-text-dark-bg: #fff;
|
||||
--color-text-dark-bg-grey: #a9b9c0;
|
||||
--color-text-error: var(--color-palette-red);
|
||||
|
||||
--color-link-content: var(--color-palette-blue);
|
||||
|
||||
@@ -33,6 +35,10 @@
|
||||
--color-course-active: #536171;
|
||||
--color-course-card-description: #536171;
|
||||
|
||||
--color-status-block-bg-success: #f4fffb;
|
||||
--color-status-block-bg-error: #fbe3e2;
|
||||
--color-status-block-message: #536171;
|
||||
|
||||
--layout-sidebar-sidebar-width: 228px;
|
||||
--layout-sidebar-content-width: 732px;
|
||||
|
||||
|
||||
6
public/images/icon-error.svg
Normal file
6
public/images/icon-error.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M-2-2h24v24H-2z"/>
|
||||
<path fill="#CD3F39" fill-rule="nonzero" d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 291 B |
4
public/images/icon-success.svg
Normal file
4
public/images/icon-success.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
||||
<path fill="none" d="M-1-1h582v402H-1z"/>
|
||||
<path fill="#0EB87F" fill-rule="evenodd" d="M10 15.5c-2.33 0-4.31-1.46-5.11-3.5h10.22c-.8 2.04-2.78 3.5-5.11 3.5M13.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 1 1 0 3m-7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 1 1 0 3m3.5 9c4.42 0 8-3.58 8-8s-3.58-8-8-8-8 3.58-8 8 3.58 8 8 8m.01-18C15.53 0 20 4.48 20 10s-4.47 10-9.99 10C4.48 20 0 15.52 0 10S4.48 0 10.01 0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 457 B |
@@ -561,13 +561,14 @@ datalist {
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin: 22px 0;
|
||||
margin: 22px 0 0 0;
|
||||
text-align: left;
|
||||
font-weight: bold
|
||||
font-weight: bold;
|
||||
font-size: 0.875em
|
||||
}
|
||||
|
||||
label + input {
|
||||
margin-top: -11px;
|
||||
margin-top: 5.5px;
|
||||
}
|
||||
|
||||
/* Input & Textarea ------------------ */
|
||||
@@ -719,9 +720,13 @@ button[disabled] {
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus,
|
||||
option:focus,
|
||||
option:focus {
|
||||
border-color: #5c9fef;
|
||||
color: rgb(40, 40, 40);
|
||||
}
|
||||
|
||||
button:focus,
|
||||
.cta:focus {
|
||||
.cta:focus {
|
||||
background-color: rgb(125, 178, 242);
|
||||
color: #fff;
|
||||
}
|
||||
@@ -749,10 +754,70 @@ input[type="reset"]:focus,
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.form-item .help-text {
|
||||
font-size: 0.9em;
|
||||
color: #8091a5;
|
||||
margin-top: -22px;
|
||||
.form-item input {
|
||||
margin-bottom: 5.5px;
|
||||
}
|
||||
|
||||
.form-item__help-text {
|
||||
font-size: 0.875em;
|
||||
color: #8091a5;
|
||||
}
|
||||
|
||||
.form-item__error-message {
|
||||
font-size: 0.875em;
|
||||
color: #cd3f39;
|
||||
}
|
||||
|
||||
.form-item__error-message:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
background: url('/images/icon-error.svg') no-repeat left center;
|
||||
background-size: contain;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
padding-right: 0.3em;
|
||||
}
|
||||
|
||||
.status-block {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
margin: 22px 0;
|
||||
padding: 22px;
|
||||
border-radius: 4px;
|
||||
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.status-block--success {
|
||||
background: #f4fffb;
|
||||
}
|
||||
|
||||
.status-block--error {
|
||||
background: #fbe3e2;
|
||||
}
|
||||
|
||||
.status-block__icon {
|
||||
-webkit-box-flex: 0;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 11px;
|
||||
}
|
||||
|
||||
.status-block__content {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.status-block__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-block__message {
|
||||
color: #536171;
|
||||
}
|
||||
|
||||
.layout-centered {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const express = require('express')
|
||||
const { createClient } = require('contentful')
|
||||
const { catchErrors } = require('../handlers/errorHandlers')
|
||||
const router = express.Router()
|
||||
|
||||
@@ -6,14 +7,117 @@ const router = express.Router()
|
||||
router.get('/', catchErrors(async function (req, res, next) {
|
||||
const cookie = req.cookies.theExampleAppSettings
|
||||
const settings = cookie || { cpa: '', cda: '', space: '' }
|
||||
res.render('settings', { title: 'Settings', settings })
|
||||
res.render('settings', {
|
||||
title: 'Settings',
|
||||
settings,
|
||||
errors: {},
|
||||
hasErrors: false,
|
||||
success: false
|
||||
})
|
||||
}))
|
||||
|
||||
/* POST settings page. */
|
||||
router.post('/', catchErrors(async function (req, res, next) {
|
||||
const settings = {space: req.body.space, cda: req.body.cda, cpa: req.body.cpa}
|
||||
res.cookie('theExampleAppSettings', settings, { maxAge: 900000, httpOnly: true })
|
||||
res.render('settings', settings)
|
||||
const errorList = []
|
||||
const { space, cda, cpa } = req.body
|
||||
const settings = {space, cda, cpa}
|
||||
|
||||
// Validate required fields.
|
||||
if (!space) {
|
||||
errorList.push({
|
||||
field: 'space',
|
||||
message: 'This field is required'
|
||||
})
|
||||
}
|
||||
|
||||
if (!cda) {
|
||||
errorList.push({
|
||||
field: 'cda',
|
||||
message: 'This field is required'
|
||||
})
|
||||
}
|
||||
|
||||
if (!cpa) {
|
||||
errorList.push({
|
||||
field: 'cpa',
|
||||
message: 'This field is required'
|
||||
})
|
||||
}
|
||||
|
||||
// Validate space and CDA access token.
|
||||
if (space && cda) {
|
||||
try {
|
||||
await createClient({
|
||||
space,
|
||||
accessToken: cda
|
||||
}).getSpace()
|
||||
} catch (err) {
|
||||
if (err.response.status === 401) {
|
||||
errorList.push({
|
||||
field: 'cda',
|
||||
message: 'Your Delivery API key is invalid.'
|
||||
})
|
||||
} else if (err.response.status === 404) {
|
||||
errorList.push({
|
||||
field: 'space',
|
||||
message: 'This space does not exist.'
|
||||
})
|
||||
} else {
|
||||
errorList.push({
|
||||
field: 'cda',
|
||||
message: `Something went wrong: ${err.response.data.message}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate space and CPA access token.
|
||||
if (space && cpa) {
|
||||
try {
|
||||
await createClient({
|
||||
space,
|
||||
accessToken: cpa,
|
||||
host: 'preview.contentful.com'
|
||||
}).getSpace()
|
||||
} catch (err) {
|
||||
if (err.response.status === 401) {
|
||||
errorList.push({
|
||||
field: 'cpa',
|
||||
message: 'Your Preview API key is invalid.'
|
||||
})
|
||||
} else if (err.response.status === 404) {
|
||||
// Already validated via CDA
|
||||
} else {
|
||||
errorList.push({
|
||||
field: 'cpa',
|
||||
message: `Something went wrong: ${err.response.data.message}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!errorList.length) {
|
||||
res.cookie('theExampleAppSettings', settings, { maxAge: 900000, httpOnly: true })
|
||||
}
|
||||
|
||||
// Generate error dictionary
|
||||
const errors = errorList.reduce((errors, error) => {
|
||||
return {
|
||||
...errors,
|
||||
[error.field]: [
|
||||
...(errors[error.field] || []),
|
||||
error.message
|
||||
]
|
||||
}
|
||||
}, {})
|
||||
|
||||
res.render('settings', {
|
||||
title: 'Settings',
|
||||
settings,
|
||||
errors,
|
||||
hasErrors: errorList.length > 0,
|
||||
success: errorList.length === 0
|
||||
})
|
||||
}))
|
||||
|
||||
module.exports = router
|
||||
|
||||
@@ -5,21 +5,43 @@ block content
|
||||
h1= title
|
||||
p To query and get content using the APIs, client applications need to authenticate with both the Space ID and an access token.
|
||||
|
||||
if success
|
||||
.status-block.status-block--success
|
||||
img.status-block__icon(src='/images/icon-success.svg')
|
||||
.status-block__content
|
||||
.status-block__title Changes saved successfully!
|
||||
|
||||
if hasErrors
|
||||
.status-block.status-block--error
|
||||
img.status-block__icon(src='/images/icon-error.svg')
|
||||
.status-block__content
|
||||
.status-block__title Error occurred
|
||||
.status-block__message Some errors occurred. Please check the error messages next to the fields.
|
||||
|
||||
form(action=`/settings` method="POST" class="form")
|
||||
.form-item
|
||||
label(for="space") Space ID
|
||||
input(type="text" name="space" value=settings.space)
|
||||
.help-text Some help text we still need to define
|
||||
if 'space' in errors
|
||||
each message in errors.space
|
||||
.form-item__error-message= message
|
||||
.form-item__help-text Some help text we still need to define
|
||||
|
||||
.form-item
|
||||
label(for="cda") Delivery API key
|
||||
input(type="text" name="cda" value=settings.cda)
|
||||
.help-text Some help text we still need to define
|
||||
if 'cda' in errors
|
||||
each message in errors.cda
|
||||
.form-item__error-message= message
|
||||
.form-item__help-text Some help text we still need to define
|
||||
|
||||
.form-item
|
||||
label(for="cpa") Preview API key
|
||||
input(type="text" name="cpa" value=settings.cpa)
|
||||
.help-text Some help text we still need to define
|
||||
if 'cpa' in errors
|
||||
each message in errors.cpa
|
||||
.form-item__error-message= message
|
||||
.form-item__help-text Some help text we still need to define
|
||||
|
||||
.form-item
|
||||
input.cta(type="submit" value="Load settings")
|
||||
|
||||
Reference in New Issue
Block a user