Skip to content

Instantly share code, notes, and snippets.

@peteleco
Last active September 12, 2022 16:25
Show Gist options
  • Select an option

  • Save peteleco/c0badd3255b05371d34f4b7c10689653 to your computer and use it in GitHub Desktop.

Select an option

Save peteleco/c0badd3255b05371d34f4b7c10689653 to your computer and use it in GitHub Desktop.
A simple validation wrapper for inertia.js
<template>
<app-layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Cadastrar Novo Cliente
</h2>
<breadcrumb slot="breadcrumb" :items="breadcrumbItems"></breadcrumb>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<jet-form-section @submitted="create">
<template #title>
{{$t('PAGES.CLIENTS.CREATE.form_title')}}
</template>
<template #description>
{{$t('PAGES.CLIENTS.CREATE.form_description')}}
</template>
<template #form>
<!-- Name -->
<div class="col-span-6 sm:col-span-4">
<jet-label for="name" value="Name" />
<jet-input id="name" type="text" class="mt-1 block w-full" aria-required="true" v-model="form.name" autocomplete="name" />
<jet-input-error :message="form.error('name')" class="mt-2" />
</div>
<!-- Mobile -->
<div class="col-span-6 sm:col-span-4">
<jet-label for="mobile" value="Celular do cliente"/>
<jet-input-validation :error-bag="form.error('mobile')" :error-length="18" :value="form.mobile" :v-function="vMobile" ref="mobileWrapper">
<template #input="slotProps">
<jet-input id="mobile" type="tel"
v-facade="[$t('MASKS.MOBILE')]"
class="mt-1 block w-full"
:class="{'focus:border-gray-400': !slotProps.displayError, 'border-danger-300 focus:border-danger-500': slotProps.displayError}"
aria-required="true" v-model="form.mobile" :aria-placeholder="$t('PAGES.CLIENTS.CREATE.aria_mobile_placeholder')" :placeholder="$t('PAGES.CLIENTS.CREATE.mobile_placeholder')" autocomplete="off"/>
</template>
<template #valid>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" class="w-6 h-6 text-success" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
</svg>
</template>
<template #invalid>
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-danger" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</template>
</jet-input-validation>
<jet-input-error :message="form.error('mobile')" class="mt-2" />
</div>
</template>
<template #actions>
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
Saved.
</jet-action-message>
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
Save
</jet-button>
</template>
</jet-form-section>
</div>
</div>
</app-layout>
</template>
<script>
import {JetInput, JetButton, JetFormSection, JetInputError, JetLabel, JetActionMessage, JetSecondaryButton} from '@/Jetstream';
import AppLayout from '@/Layouts/AppLayout';
import {Breadcrumb} from '@/Components';
import {JetInputValidation} from '@/Chama/Jetstream';
import {vMobile} from '@/Chama/Validations';
export default {
name: "Create",
components: {
AppLayout,
Breadcrumb,
JetActionMessage,
JetButton,
JetFormSection,
JetInputError,
JetLabel,
JetSecondaryButton,
JetInput,
JetInputValidation
},
computed: {
breadcrumbItems() {
return [
{route: this.route('app.clients'), label: this.$t('BREADCRUMB.clients')},
{route: this.route('app.clients.create'), label: this.$t('BREADCRUMB.create')},
];
}
},
data() {
return {
form: this.$inertia.form({
'_method': 'POST',
name: this.name,
mobile: this.mobile,
photo: null,
}, {
bag: 'default',
resetOnSuccess: false,
}),
}
},
methods: {
vMobile,
create() {
this.form.post(this.route('app.clients.post'), {
preserveScroll: true
});
},
}
}
</script>
<style scoped>
</style>
<template>
<span class="flex flex-row mb-1 relative" ref="inputValidation">
<slot name="input" v-bind:displayError="displayError"></slot>
<span class="absolute right-0 flex items-center py-3 px-4 h-full">
<template v-if="isValid">
<slot name="valid">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</slot>
</template>
<template v-if="displayError">
<slot name="invalid">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</slot>
</template>
</span>
</span>
</template>
<script>
export default {
name: 'InputValidation',
props: {
/**
* The value from input
*/
value: {
type: String,
default: ""
},
/**
*The function that will be used
* at validation
*/
vFunction: {
type: Function,
default: (val) => true
},
/**
* After type how many characters
* the error can be displayed
*/
errorLength: {
type: Number,
default: 0
},
errorBag: {
default: false
}
},
watch: {
value: {
// the callback will be called immediately after the start of the observation
immediate: true,
handler(val, oldVal) {
this.handleValueUpdates(val);
}
}
},
computed: {
isValid() {
return this.valid;
},
/**
* Tells whether the error can already be displayed
*/
displayError() {
if (this.hasBagError && !this.valid) {
return true;
}
return (!this.valid && this.hasMinLength);
},
/**
* Checks if enough has been entered
* to display error
* @returns {boolean}
*/
hasMinLength() {
return (this.value.length >= this.errorLength);
},
/**
* Has error from server request
*/
hasBagError() {
return (this.errorBag.length > 0);
}
},
data() {
return {
valid: false,
}
},
methods: {
/**
* Every time that value prop changes this function will be called
* @param val
*/
handleValueUpdates(val) {
if (this.vFunction(val) === true) {
return this.setAsValid();
}
return this.setAsInvalid();
},
setAsValid() {
this.valid = true;
// this.$emit('form-input-validation', this.name, this.valid = true);
},
setAsInvalid() {
// this.$emit('form-input-validation', this.name, false);
this.valid = false;
},
}
}
</script>
<style scoped>
</style>
// Validate mobile numbers in Brazil
const vMobile = (mobile) => {
const m = mobile.replace(/\+|\)|\(| |-/g, '');
const regex = /^55[1-9]{2}9[1-9][0-9]{7}$/;
const ddd = m.substring(3, 5);
const ddds = ['11', '12', '13', '14', '15', '16', '17', '18', '19', '21', '22', '24', '27', '28', '31', '32', '33', '34', '35', '37', '38', '41', '42', '43', '44', '45', '46', '47', '48', '49', '51', '53', '54', '55', '61', '62', '63', '64', '65', '66', '67', '68', '69', '71', '73', '74', '75', '77', '79', '81', '82', '83', '84', '85', '86', '87', '88', '89', '91', '92', '93', '94', '95', '96', '97', '98', '99'];
if (ddds.indexOf(ddd) < 0) {
return false;
}
return regex.test(m);
}
export default vMobile ;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment