The goal was to create custom booking management system and admin panel for writing blog posts.
For coach Marzena Peplińska we have designed and developed three applications - Vue.js marketing page, Vue.js admin panel and Node.js back-end API.
Here's the live site if you want to take a look!
Scope of the project
We provided Vue.js front-end, Node.js back-end development and UI & UX design services.
Colors
Here's the color palette we've used!
Typography
We've used sans-serif Lato font for majority of the apps' content.
Lora serif font was used for blog posts' content, to provide optimal readability.

Responsiveness
As responsiveness is a major part of a good user experience, we've put a great effort in desiging the marketing website so that it looks perfect on all devices.
Booking system
Major part of all three applications is the custom booking system. User is presented with a simple form, where she can book an online meeting.
We've used great a 3rd party library for datepicker component - vuejs-datepicker. It supports a variety of languages, which was a crucial requirement, as the website's default language is polish.
.small-12.medium-3.columns
label(for="dateFrom") Date of consultation
Datepicker(
v-model="dateFrom",
id="dateFrom",
name="dateFrom",
placeholder="1 September 2017",
language="pl",
format="d MMMM yyyy",
:full-month-name="true",
:monday-first="true",
:input-class="['cf__input', { 'input--error': !isDateFromValid }]",
:disabled="disabledDates",
@selected="hour = ''",
@changedMonth="getNewAvailabilities",
@changedYear="getNewAvailabilities",
)
small.error(v-if="!isDateFromValid") Date of consultation is required.
For the form validation we're using a fantastic vuelidate lib, which is a core of our Vue.js starter kit we use for every project!
validations: {
firstName: { required },
lastName: { required },
email: { required, email },
dateFrom: { isValidDate() { return moment(this.dateFrom).isValid() } },
hour: { required },
contactType: { required },
consultationType: { required },
message: { required },
isFirst: { required },
isTerms: { required, isAccepted() { return this.isTerms } },
consultationForm: [
'firstName',
'lastName',
'email',
'dateFrom',
'hour',
'contactType',
'consultationType',
'message',
'isFirst',
'isTerms',
],
}
Here's a peak at admin's part of booking system...

...and here's the client part:
Blog
Blog is one of the best resources for great SEO, that's why we put a great focus on building robust, custom blogging system.
We usualy use Prerender for making webapp's content fully accessible for crawlers. There's of course also Nuxt.js, which is a powerful tool for not only better SEO, but also for better user experience. For this case however, we decided it would have been a bit of an overkill to use Nuxt.js. ;)

We've also built an admin panel section for writing blog posts. We used Quill's Vue.js implementation for WYSIWYG editor.
Node.js Rest API
We have developed a Node.js back-end JSON Rest API for "gluing" both front-end apps together.
We used Koa.js framework, together with Sequelize ORM for PostgreSQL database. Other great library worth mentioning is schema-inspector, which greatly helps us to sanitize and validate requests' payload.
Here's a peak at the source code of one of booking system's controllers:
export default router.controller('/availabilities', (ctrl) => {
ctrl
.post('/', auth, async (ctx, next) => {
const { body } = ctx.request
validate
.run(body, {
dateFrom: {
type: 'string',
exec(schema, _dateFrom) {
const isValidDate = moment(_dateFrom).isValid()
const isAfterNow = moment(_dateFrom).isAfter(moment())
const isSameDay = moment(_dateFrom).isSame(moment(body.dateTo), 'day')
if (!isValidDate) this.report('must be valid date', 422)
if (!isAfterNow) this.report('must not be past date', 422)
if (!isSameDay) this.report('must be same day', 422)
},
},
dateTo: {
type: 'string',
exec(schema, _dateTo) {
const isValidDate = moment(_dateTo).isValid()
const isAfter = moment(_dateTo).isAfter(moment(body.dateFrom))
if (!isValidDate) this.report('must be valid date', 422)
if (!isAfter) this.report('must be after \'dateFrom\'', 422)
},
},
})
.throwIfInvalid()
const dateFrom = moment(body.dateFrom).startOf('hour')
const dateTo = moment(body.dateTo).startOf('hour')
const isAlready = await Availability.findOne({ where: { dateFrom: { $between: [
moment(dateFrom).startOf('day').format(), moment(dateFrom).endOf('day').format(),
] } } })
if (isAlready) error.throw({ name: 'UniqueConstraintError', data: { dateFrom: ['must be unique day'] } })
const availability = await Availability.create({ dateFrom, dateTo })
ctx
.success(availability)
.withStatus(201)
.then(await next)
})
Client's feedback
Henryk Pepliński
ZapytajCoacha
“Professionals looking for very challenging tasks! They are not afraid of anything related to their work. Punctuality and flexibility ideally suited to the customer. The experience and knowledge of people from InventiStudio means that I will always come back to them if in need of a new web application or other IT service.”
