Form wordpress yang dibuat dari awal dengan mengaplikasikan tailwindcss untuk styling nya dan alpine js , file ini adalah bagian dari sebuah theme wordpress yang sedang saya coba bangun tanpa menggunakan Jquery dan bootstrap css yang umumnya digunakan
Halaman Villa Page
<!-- template-parts/villa-page -->
<div class="md:flex md:container md:mx-auto md:-mt-24">
<div class="w-full md:w-7/12">
<main class="p-5 text-sm text-gray-500">
<div class="villadetail">
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1 class="md:text-2xl text-md md:text-white font-medium uppercase md:text-3xl tracking-wider">
<?php the_title() ?>
</h1>
<div class="text-gray-500 text-xs my-10" aria-label="Breadcrumb">
<ol class="list-none p-0 inline-flex">
<li class="flex items-center">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">Home</a><svg class="fill-current w-3 h-3 mx-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg>
</li>
<li class="flex items-center">
<a href="<?php echo esc_url( home_url( '/villas/' ) ); ?>">Villas</a>
</li>
</ol>
</div>
<?php the_content() ?>
<?php endwhile; endif; ?>
</div>
</main>
</div>
<!--sidebar -->
<div class="w-full md:w-5/12">
<aside class="p-5">
<div class="bg-white w-full m-auto boder-1 border-dashed border-gray-100 shadow-md rounded-lg overflow-hidden">
<div class="p-4 border-2">
<h2 class="mb-1 text-gray-700 font-semibold text-sm pb-3 uppercase tracking-wide">
Reservation
</h2>
<p class="text-sm pb-3">Questions?</p>
<div class="flex">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24" style=" fill:#000000;"><path d="M 12.011719 2 C 6.5057187 2 2.0234844 6.478375 2.0214844 11.984375 C 2.0204844 13.744375 2.4814687 15.462563 3.3554688 16.976562 L 2 22 L 7.2324219 20.763672 C 8.6914219 21.559672 10.333859 21.977516 12.005859 21.978516 L 12.009766 21.978516 C 17.514766 21.978516 21.995047 17.499141 21.998047 11.994141 C 22.000047 9.3251406 20.962172 6.8157344 19.076172 4.9277344 C 17.190172 3.0407344 14.683719 2.001 12.011719 2 z M 12.009766 4 C 14.145766 4.001 16.153109 4.8337969 17.662109 6.3417969 C 19.171109 7.8517969 20.000047 9.8581875 19.998047 11.992188 C 19.996047 16.396187 16.413812 19.978516 12.007812 19.978516 C 10.674812 19.977516 9.3544062 19.642812 8.1914062 19.007812 L 7.5175781 18.640625 L 6.7734375 18.816406 L 4.8046875 19.28125 L 5.2851562 17.496094 L 5.5019531 16.695312 L 5.0878906 15.976562 C 4.3898906 14.768562 4.0204844 13.387375 4.0214844 11.984375 C 4.0234844 7.582375 7.6067656 4 12.009766 4 z M 8.4765625 7.375 C 8.3095625 7.375 8.0395469 7.4375 7.8105469 7.6875 C 7.5815469 7.9365 6.9355469 8.5395781 6.9355469 9.7675781 C 6.9355469 10.995578 7.8300781 12.182609 7.9550781 12.349609 C 8.0790781 12.515609 9.68175 15.115234 12.21875 16.115234 C 14.32675 16.946234 14.754891 16.782234 15.212891 16.740234 C 15.670891 16.699234 16.690438 16.137687 16.898438 15.554688 C 17.106437 14.971687 17.106922 14.470187 17.044922 14.367188 C 16.982922 14.263188 16.816406 14.201172 16.566406 14.076172 C 16.317406 13.951172 15.090328 13.348625 14.861328 13.265625 C 14.632328 13.182625 14.464828 13.140625 14.298828 13.390625 C 14.132828 13.640625 13.655766 14.201187 13.509766 14.367188 C 13.363766 14.534188 13.21875 14.556641 12.96875 14.431641 C 12.71875 14.305641 11.914938 14.041406 10.960938 13.191406 C 10.218937 12.530406 9.7182656 11.714844 9.5722656 11.464844 C 9.4272656 11.215844 9.5585938 11.079078 9.6835938 10.955078 C 9.7955938 10.843078 9.9316406 10.663578 10.056641 10.517578 C 10.180641 10.371578 10.223641 10.267562 10.306641 10.101562 C 10.389641 9.9355625 10.347156 9.7890625 10.285156 9.6640625 C 10.223156 9.5390625 9.737625 8.3065 9.515625 7.8125 C 9.328625 7.3975 9.131125 7.3878594 8.953125 7.3808594 C 8.808125 7.3748594 8.6425625 7.375 8.4765625 7.375 z"></path></svg>
<p class="text-sm ml-2 text-gray-600">+62 361 0000 000</p>
</div>
<div class="flex">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 50 50" style=" fill:#000000;"><path d="M 14 3.9902344 C 8.4886661 3.9902344 4 8.4789008 4 13.990234 L 4 35.990234 C 4 41.501568 8.4886661 45.990234 14 45.990234 L 36 45.990234 C 41.511334 45.990234 46 41.501568 46 35.990234 L 46 13.990234 C 46 8.4789008 41.511334 3.9902344 36 3.9902344 L 14 3.9902344 z M 14 5.9902344 L 36 5.9902344 C 40.430666 5.9902344 44 9.5595687 44 13.990234 L 44 35.990234 C 44 40.4209 40.430666 43.990234 36 43.990234 L 14 43.990234 C 9.5693339 43.990234 6 40.4209 6 35.990234 L 6 13.990234 C 6 9.5595687 9.5693339 5.9902344 14 5.9902344 z M 18.048828 11.035156 C 16.003504 10.946776 14.45113 11.723922 13.474609 12.658203 C 12.986349 13.125343 12.633832 13.625179 12.392578 14.091797 C 12.151324 14.558415 11.998047 14.943108 11.998047 15.443359 C 11.998047 15.398799 11.987059 15.632684 11.980469 15.904297 C 11.973869 16.17591 11.97507 16.542045 12 16.984375 C 12.04996 17.869036 12.199897 19.065677 12.597656 20.484375 C 13.393174 23.321771 15.184446 27.043821 19.070312 30.929688 C 22.95618 34.815554 26.678014 36.606575 29.515625 37.402344 C 30.93443 37.800228 32.130881 37.949937 33.015625 38 C 33.457997 38.02503 33.822105 38.026091 34.09375 38.019531 C 34.365395 38.012931 34.601049 38.001953 34.556641 38.001953 C 35.056892 38.001953 35.441585 37.848676 35.908203 37.607422 C 36.374821 37.366168 36.874657 37.013651 37.341797 36.525391 C 38.276078 35.54887 39.053222 33.996496 38.964844 31.951172 C 38.922907 30.975693 38.381316 30.111858 37.582031 29.599609 C 36.96435 29.203814 36.005458 28.589415 34.753906 27.789062 C 33.301811 26.861299 31.44451 26.795029 29.929688 27.625 L 30.015625 27.582031 L 28.837891 28.087891 L 28.751953 28.148438 C 28.465693 28.349428 28.111154 28.386664 27.789062 28.251953 C 26.886813 27.874649 25.480985 27.133329 24.173828 25.826172 C 22.866671 24.519015 22.125351 23.113186 21.748047 22.210938 C 21.613336 21.888845 21.650568 21.534307 21.851562 21.248047 L 21.912109 21.162109 L 22.417969 19.984375 L 22.375 20.070312 C 23.204764 18.555868 23.140248 16.698619 22.210938 15.246094 C 21.410584 13.994542 20.796186 13.03565 20.400391 12.417969 C 19.888142 11.618684 19.02431 11.077096 18.048828 11.035156 z M 17.962891 13.033203 C 18.243409 13.045263 18.533045 13.209378 18.716797 13.496094 C 19.113001 14.114413 19.727696 15.07377 20.527344 16.324219 C 21.058033 17.153694 21.09533 18.243821 20.621094 19.109375 L 20.597656 19.152344 L 20.115234 20.279297 L 20.214844 20.097656 C 19.623835 20.939396 19.505055 22.032514 19.902344 22.982422 C 20.35304 24.060173 21.214923 25.695392 22.759766 27.240234 C 24.304608 28.785077 25.939827 29.64696 27.017578 30.097656 C 27.967486 30.494945 29.060604 30.376165 29.902344 29.785156 L 29.720703 29.884766 L 30.847656 29.402344 L 30.890625 29.378906 C 31.755801 28.904877 32.845877 28.944375 33.675781 29.474609 L 33.675781 29.472656 C 34.92623 30.272304 35.885587 30.886999 36.503906 31.283203 C 36.790622 31.466955 36.954736 31.756591 36.966797 32.037109 C 37.032417 33.555785 36.504954 34.506599 35.896484 35.142578 C 35.59225 35.460568 35.262335 35.691348 34.990234 35.832031 C 34.718133 35.972715 34.457889 36.001953 34.556641 36.001953 C 34.373232 36.001953 34.276633 36.013981 34.046875 36.019531 C 33.817117 36.025131 33.509144 36.025436 33.128906 36.003906 C 32.368431 35.960876 31.318757 35.831053 30.054688 35.476562 C 27.526547 34.767581 24.137509 33.168759 20.484375 29.515625 C 16.831241 25.862491 15.232169 22.473167 14.523438 19.945312 C 14.169071 18.681386 14.039037 17.631464 13.996094 16.871094 C 13.974624 16.490908 13.974899 16.18286 13.980469 15.953125 C 13.986069 15.72339 13.998047 15.626918 13.998047 15.443359 C 13.998047 15.542109 14.027287 15.281867 14.167969 15.009766 C 14.308652 14.737665 14.539432 14.40775 14.857422 14.103516 C 15.493401 13.495046 16.444215 12.967581 17.962891 13.033203 z"></path></svg>
<p class="text-sm ml-2 text-gray-600">+62 888 0000 000</p>
</div>
<p class="text-sm pt-5 text-gray-600">
information : [email protected]
</p>
<div class="mt-8 mb-3">
<div class="relative" x-data="{ toggle:null }">
<button type="button"
@click="toggle !== 1 ? toggle = 1 : toggle = null"
x-ref="reservationbutton"
class="px-4 py-2 bg-blue-600 shadow-lg border rounded-lg text-white uppercase text-sm tracking-wider focus:outline-none focus:shadow-outline hover:bg-blue-800 active:bg-blue-400">
Reservation
</button>
<div class="relative overflow-hidden transition-all max-h-0 duration-700"
x-ref="reservation"
:style="toggle == 1 ? 'max-height: '+ $refs.reservation.scrollHeight + 'px' :''"
>
<div class="p-2">
<p class="text-xs">
This is only an enquiry regarding the villa; it does not constitute a reservation.
<br />Fields (*) required.
</p>
<!--reservation-form-->
<?php get_template_part( 'template-parts/reservation-form');?>
</div>
</div>
</div>
</div>
</div>
</div>
<!--Gallery -->
<?php get_template_part( 'template-parts/gallery');?>
</aside>
</div>
</div>
Reservation-form PHP
<form
method="post"
id="form-reservation"
data-nonce="<?php echo wp_create_nonce('submit_reservation_form_nonce');?>"
@submit.prevent="submitForm"
x-data="ReservationForm"
>
<!--modal success -->
<?php get_template_part('template-parts/success-modal');?>
<input type="hidden" name="subject" value="Reservation for <?php the_title() ?>" />
<div class="mt-8 max-w-md">
<div class="grid grid-cols-1 gap-6 text-sm">
<label class="block">
<span class="text-gray-700">Full name *</span>
<input type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
name="fullname" autocomplete="off" placeholder="" x-model="formData.fullname" x-ref="fullname"/>
<p
class="text-xs text-red-600"
x-text="errors.fullname?'Please fill fullname':''"
>
</p><span class="text-lg font-bold"></span>
</label>
<label class="block">
<span class="text-gray-700">Email address *</span>
<input type="email"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 placeholder-gray-400"
name="email" autocomplete="off"
placeholder="[email protected]"
x-model="formData.email"
x-ref="email"
/>
<p
class="text-xs text-red-600"
x-text="errors.email?'Please fill email':''"
>
</p>
</label>
<label class="block">
<span class="text-gray-700">Phone *</span>
<input type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
name="phone" placeholder="" x-model="formData.phone" x-ref="phone"/>
<p
class="text-xs text-red-600"
x-text="errors.phone?'Please fill phone':''"
>
</p>
</label>
<div class="relative" @keydown.escape="datepicker.closeDatepicker()" @click.away="datepicker.closeDatepicker()">
<label class="block">
<span class="text-gray-700">Check IN</span>
<input type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 text-sm"
name="checkin" autocomplete="off"
@click="datepicker.endToShow = 'from'; datepicker.initDate(); datepicker.showDatepicker = true"
x-model="datepicker.dateFromValue" x-ref="checkin"
:class="{'font-semibold': datepicker.endToShow == 'from' }"
/>
</label>
<p
class="text-xs text-red-600"
x-text="errors.checkin?'Please select date':''"
>
</p>
<label class="block">
<span class="text-gray-700">Check Out</span>
<input type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
name="checkout" autocomplete="off"
@click="datepicker.endToShow = 'to'; datepicker.initDate(); datepicker.showDatepicker = true"
x-model="datepicker.dateToValue"
:class="{'font-semibold': datepicker.endToShow == 'to' }"
/>
</label>
<?php get_template_part( 'template-parts/date-modal');?>
</div>
<label class="block">
<span class="text-gray-700">Number of guests</span>
<input type="number" min="0" step="1"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
name="number_of_guest" placeholder="" x-model="formData.number_of_guest"/>
</label>
<label class="block">
<span class="text-gray-700">Request</span>
<textarea
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm text-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 placeholder-gray-400"
rows="6"
name="request"
placeholder="Please advise flight details for airport pick-up, or estimated arrival time at Villa, if travelling with infant(s), and any special requests, eg kid’s equipment / dietary needs / villa set-up."
x-model="formData.request"
>
resevationform.js
'use strict';
window.ReservationForm = function () {
const formReservation = document.getElementById('form-reservation');
let nonce = formReservation.dataset.nonce;
let fields = ['fullname','email','phone','checkin','checkout','number_of_guest','request'];
let datepicker = new datePicker();
return {
buttonLabel: 'Submit',
loading: false,
status: false,
modalHeaderText:'',
modalBodyText:'',
datepicker,
//create formdata objects from array fields
formData: Object.assign({}, ...Object.entries({...fields}).map(([a,b]) => ({ [b]: '' }))),
//create errors objects from array fields
errors : Object.assign({}, ...Object.entries({...fields}).map(([a,b]) => ({ [b]: false }))),
isEmail(email){
const rgx = /^(([^<>()[].,;:s@"]+(.[^<>()[].,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/
return rgx.test(email);
},
validation(q,w){
var validate=false;
if (!q){
this.errors[w] = true;
this.$refs[w].scrollIntoView();
validate=true;
}else{
this.errors[w]= false;
}
return validate;
},
submitForm() {
//compensate height of reservation when error messages display
this.$refs.reservation.style.maxHeight = '100%';
//validations
if(
this.validation(this.formData.fullname.length>0, 'fullname')||
this.validation(this.isEmail(this.formData.email), 'email')||
this.validation(this.formData.phone.length > 0, 'phone')||
this.validation(this.datepicker.dateFromValue.length > 0, 'checkin')
){ return; }
this.formData.checkin = this.datepicker.dateFromValue;
this.formData.checkout = this.datepicker.dateToValue;
this.buttonLabel = 'Submitting...';
this.loading = true;
fetch( SiteParameters.ajax_url, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Cache-Control': 'no-cache',
'Accept': 'application/json'
},
credentials: 'same-origin',
body: 'action=submit_reservation_form&nonce='+ nonce +'&formdata='+ JSON.stringify( this.formData ) ,
})
.then( async (response) => {
let data = await response.json();
if (data.status == 'true') {
this.$nextTick(() => {
//scroll up reservation form container
this.$refs.reservationbutton.dispatchEvent(new Event("click"));
this.status = true;
this.modalHeaderText = "Thank You!";
this.modalBodyText = "Your form have been successfully submited!";
});
} else {
throw new Error("Your registration failed");
}
//clear forms data after form submitted
Object.keys(this.formData).forEach(key => this.formData[key]='');
//clear submitted date picker
this.datepicker.unsetDateValues();
})
.catch(() => {
})
.finally(() => {
this.loading = false;
this.buttonLabel = 'Submit'
this.status = false;
document.getElementById("form-reservation").reset();
});
},
}
}
success-modal.php pop up window container sebagai alert window setelah fetch ajax success return true
<!-- modal starts -->
<div
x-cloak
x-show.transition="status"
class="fixed inset-0 z-30 flex items-center justify-center overflow-auto bg-black bg-opacity-50"
>
<div
class="max-w-3xl px-6 py-4 mx-auto text-left bg-white rounded shadow-lg"
@click.away="status = false"
x-transition:enter="motion-safe:ease-out duration-300"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
>
<div class="bg-white rounded px-8 py-8 max-w-lg mx-auto">
<div class="flex items-center justify-between">
<h5 class="mr-3 text-black max-w-none" x-text="modalHeaderText"></h5>
<span class="mr-3 text-black max-w-none" x-text="modalBodyText"></span>
<button type="button" class="z-50 cursor-pointer" @click="status = false">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
</div>
<!-- modal ends -->
datepicker.js , date range component yang di implementasikan kedalam form
const MONTH_NAMES = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
window.datePicker = function () {
return {
MONTH_NAMES,
DAYS ,
showDatepicker: false,
dateFromYmd: '',
dateToYmd: '',
outputDateFromValue: '',
outputDateToValue: '',
dateFromValue: '',
dateToValue: '',
currentDate: null,
dateFrom: null,
dateTo: null,
endToShow: '',
selecting: false,
month: '',
year: '',
no_of_days: [],
blankdays: [],
convertFromYmd(dateYmd) {
const year = Number(dateYmd.substr(0, 4));
const month = Number(dateYmd.substr(5, 2)) - 1;
const date = Number(dateYmd.substr(8, 2));
return new Date(year, month, date);
},
convertToYmd(dateObject) {
const year = dateObject.getFullYear();
const month = dateObject.getMonth() + 1;
const date = dateObject.getDate();
return year + "-" + ('0' + month).slice(-2) + "-" + ('0' + date).slice(-2);
},
initDate() {
this.selecting = ( this.endToShow === 'to' && this.dateTo ) || ( this.endToShow === 'from' && this.dateFrom);
if ( ! this.dateFrom ) {
if ( this.dateFromYmd ) {
this.dateFrom = this.convertFromYmd( this.dateFromYmd );
}
}
if ( ! this.dateTo ) {
if ( this.dateToYmd ) {
this.dateTo = this.convertFromYmd( this.dateToYmd );
}
}
if ( ! this.dateFrom ) {
this.dateFrom = this.dateTo;
}
if ( ! this.dateTo ) {
this.dateTo = this.dateFrom;
}
if ( this.endToShow === 'from' && this.dateFrom ) {
this.currentDate = this.dateFrom;
} else if ( this.endToShow === 'to' && this.dateTo ) {
this.currentDate = this.dateTo;
} else {
this.currentDate = new Date();
}
currentMonth = this.currentDate.getMonth();
currentYear = this.currentDate.getFullYear();
if ( this.month !== currentMonth || this.year !== currentYear ) {
this.month = currentMonth;
this.year = currentYear;
this.getNoOfDays();
}
this.setDateValues();
},
isToday(date) {
const today = new Date();
const d = new Date(this.year, this.month, date);
return today.toDateString() === d.toDateString();
},
isDateFrom(date) {
const d = new Date(this.year, this.month, date);
if ( !this.dateFrom ) {
return false;
}
return d.getTime() === this.dateFrom.getTime();
},
isDateTo(date) {
const d = new Date(this.year, this.month, date);
if ( !this.dateTo ) {
return false;
}
return d.getTime() === this.dateTo.getTime();
},
isInRange(date) {
const d = new Date(this.year, this.month, date);
return d > this.dateFrom && d < this.dateTo;
},
outputDateValues() {
if (this.dateFrom) {
this.outputDateFromValue = this.dateFrom.toDateString();
this.dateFromYmd = this.convertToYmd(this.dateFrom);
}
if (this.dateTo) {
this.outputDateToValue = this.dateTo.toDateString();
this.dateToYmd = this.convertToYmd(this.dateTo);
}
},
setDateValues() {
if (this.dateFrom) {
this.dateFromValue = this.dateFrom.toDateString();
}
if (this.dateTo) {
this.dateToValue = this.dateTo.toDateString();
}
},
getDateValue(date, temp) {
// if we are in mouse over mode but have not started selecting a range, there is nothing more to do.
if (temp && !this.selecting) {
return;
}
let selectedDate = new Date(this.year, this.month, date);
if ( this.endToShow === 'from' ) {
this.dateFrom = selectedDate;
if ( ! this.dateTo ) {
this.dateTo = selectedDate;
} else if ( selectedDate > this.dateTo ) {
this.endToShow = 'to';
this.dateFrom = this.dateTo;
this.dateTo = selectedDate;
}
} else if ( this.endToShow === 'to' ) {
this.dateTo = selectedDate;
if ( ! this.dateFrom ) {
this.dateFrom = selectedDate;
} else if ( selectedDate < this.dateFrom ) {
this.endToShow = 'from';
this.dateTo = this.dateFrom;
this.dateFrom = selectedDate;
}
}
this.setDateValues();
if (!temp) {
if (this.selecting) {
this.outputDateValues();
this.closeDatepicker();
}
this.selecting = !this.selecting;
}
},
getNoOfDays() {
let daysInMonth = new Date(this.year, this.month + 1, 0).getDate();
// find where to start calendar day of week
let dayOfWeek = new Date(this.year, this.month).getDay();
let blankdaysArray = [];
for ( var i=1; i <= dayOfWeek; i++) {
blankdaysArray.push(i);
}
let daysArray = [];
for ( var i=1; i <= daysInMonth; i++) {
daysArray.push(i);
}
this.blankdays = blankdaysArray;
this.no_of_days = daysArray;
},
closeDatepicker() {
this.endToShow = '';
this.showDatepicker = false;
},
unsetDateValues() {
this.dateFromYmd= '';
this.dateToYmd= '';
this.outputDateFromValue= '';
this.outputDateToValue= '';
this.dateFromValue= '';
this.dateToValue= '';
this.currentDate= null;
this.dateFrom= null;
this.dateTo= null;
this.endToShow= '';
this.selecting= false;
},
}
}
datemodal php, pop up window yang menampung tabel tanggal dari datepicker.js
<div
class="absolute bg-white mt-0 rounded-lg shadow p-4 w-full z-80"
x-show.transition="datepicker.showDatepicker"
>
<div class="flex flex-col items-center">
<div class="w-full flex justify-between items-center mb-2">
<div>
<span x-text="datepicker.MONTH_NAMES[datepicker.month]" class="text-sm font-bold text-gray-800"></span>
<span x-text="datepicker.year" class="ml-1 text-sm text-gray-600 font-normal"></span>
</div>
<div>
<button
type="button"
class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 p-1 rounded-full"
@click="if (datepicker.month == 0) {datepicker.year--; datepicker.month=11;} else {datepicker.month--;} datepicker.getNoOfDays()">
<svg class="h-4 w-4 text-gray-500 inline-flex" fill="none" viewBox="0 0 20 20" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
</button>
<button
type="button"
class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 p-1 rounded-full"
@click="if (datepicker.month == 11) {datepicker.year++; datepicker.month=0;} else {datepicker.month++;} datepicker.getNoOfDays()">
<svg class="h-4 w-4 text-gray-500 inline-flex" fill="none" viewBox="0 0 20 20" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</button>
</div>
</div>
<div class="w-full flex flex-wrap mb-3 -mx-1">
<template x-for="(day, index) in datepicker.DAYS" :key="index">
<div style="width: 14.26%" class="px-1">
<div
x-text="day"
class="text-gray-800 font-medium text-center text-xs"
></div>
</div>
</template>
</div>
<div class="flex flex-wrap -mx-1">
<template x-for="blankday in datepicker.blankdays">
<div
style="width: 14.28%"
class="text-center border p-1 border-transparent text-sm"
></div>
</template>
<template x-for="(date, dateIndex) in datepicker.no_of_days" :key="dateIndex">
<div style="width: 14.28%" >
<div
@click="datepicker.getDateValue(date, false)"
/*@mouseenter="datepicker.getDateValue(date, true)"*/
x-text="date"
class="p-0 cursor-pointer text-center text-xs leading-loose transition ease-in-out duration-100"
:class="{'font-bold': datepicker.isToday(date) == true, 'bg-blue-800 text-white rounded-l-full': datepicker.isDateFrom(date) == true, 'bg-blue-800 text-white rounded-r-full': datepicker.isDateTo(date) == true, 'bg-blue-200': datepicker.isInRange(date) == true }"
></div>
</div>
</template>
</div>
<div>
<span class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 p-1" @click="datepicker.unsetDateValues()">Clear</span>
</div>
</div>
</div>
kode php untuk memproses data yang dikirimkan dari form
/******* SENDING MAIL *******/
function wpdocs_set_html_mail_content_type() {
return 'text/html';
}
add_filter('wp_mail_content_type', 'wpdocs_set_html_mail_content_type' );
add_action('wp_ajax_submit_reservation_form', 'submit_reservation_form');
add_action('wp_ajax_nopriv_submit_reservation_form', 'submit_reservation_form');
function submit_reservation_form() {
$nonce = $_REQUEST['nonce'];
if (! wp_verify_nonce($nonce , 'submit_reservation_form_nonce') ) {
die('No naughty business please.');
}
else{
$formdata = stripslashes($_POST['formdata']);
$formdata = json_decode($formdata , true);
$fullname = $formdata['fullname'];
$email = sanitize_email($formdata['email']);
$subject = $formdata['subject'];
$phone = $formdata['phone'];
$checkin = $formdata['checkin'];
$checkout = $formdata['checkout'];
$number_of_guest = $formdata['number_of_guest'];
$request = $formdata['request'];
//$recipient_email = base64_decode($formdata['sendto']);
/* Email Message */
$body = "<h2>Reservation Message</h2>";
$body .= "<strong>Villa Name:</strong> ".$subject."<br/>";
$body .= "<strong>Nama:</strong> ".$fullname."<br/>";
$body .= "<strong>E-mail:</strong> ".$email."<br/>";
$body .= "<strong>Phone:</strong> ".$phone."<br/>";
$body .= "<strong>Check in date:</strong> ".$checkin."<br/>";
$body .= "<strong>Check out date:</strong> ".$checkout."<br/>";
$body .= "<strong>Number of guest:</strong> ".$number_of_guest."<br/>";
$body .= "<strong>Request:</strong> ".$request."<br/>";
if( $_SERVER['REMOTE_ADDR'] === '127.0.0.1' ){
$result['status'] = 'true';
} else{
if( function_exists('wp_mail') ) {
$headers = 'From: ' . $fullname . ' <' . $email . '>' . "rn";
if(wp_mail($recipient_email, $subject, $body, $headers)) {
$result['status'] = 'true';
} else {
$result['status'] = 'false';
}
} else {
$result['status'] = 'false';
}
}
echo json_encode($result);
die();
}
}
kode php untuk memasukan ajax url parameter yg dibutuhkan sebagai alamat url pada fetch di reservationform js
/* Make site url available to JS scripts */
$site_parameters = array(
'site_url' => get_site_url(),
'theme_directory' => get_template_directory_uri(),
'ajax_url' => admin_url('admin-ajax.php')
);
wp_localize_script( 'tw-js', 'SiteParameters', $site_parameters );
kode php untuk memasukan source javascript dalam element dengan menambahkan attribute defer
if( is_page_template( 'villa-page.php' )){
//add script into theme and inject defer attributes in the <script>s
$scripts_ids = array('datepicker','reservation-form','alpinejs');
$scripts_srcs = array('datepicker.js','reservationform.js','alpinejs.js');
foreach( array_combine( $scripts_ids , $scripts_srcs ) as $id => $src) {
echo wp_get_script_tag(
array(
'id'=> $id,
'src'=> get_template_directory_uri( ).'/dist/js/'. $src,
'defer'=> true,
)
);
}
}
untuk mengkompilasi script reservationform dan datepicker menggunakan laravel mix ditambahkan kedalam webpack.mix.js
let mix = require('laravel-mix');
mix.setPublicPath('./dist');
mix.postCss('src/css/theme.css','css')
.options(
{
postCss:[
require('tailwindcss')('tailwind.config.js')
],
processCssUrls: false
}
).postCss('src/css/tiny-slider.css','css');
mix.js('src/js/main.js','js')
.js('src/js/tiny-slider.js','js')
.js('src/js/alpinejs.js','js')
.js('src/js/reservationform.js','js')
.js('src/js/datepicker.js','js');