Help with Pines Date Picker

Solved
carlosolivas

Feb 12th, 2024 04:22 PM

Hello,

I'm having trouble figuring out how to use Pines Date Picker component, I want to use it for an appoinments app and need to have some dates blocked, I thought the 'datePickerBlankDaysInMonth' property could do the trick, since it is an array I was expecting it would accept all the days that would be blocked, but I looks that it doesn't work that way.

If someone can help me clarify this, it would be much appreciated.

Thanks

Carlos

bobbyiliev

Feb 19th, 2024 01:34 AM

Best Answer

Hey!

To block specific dates in your date picker, you'll need to introduce a new property or mechanism. Here's one way to do this:

  1. Define Blocked Dates: Create an array to hold the dates that should be blocked. These could be specific dates where appointments are not available. You can pass this from a controller or a Livewire component.

  2. Check for Blocked Dates: Modify the datePickerDayClicked function or introduce a new function to check whether a selected day is blocked. If the day is blocked, you could prevent the selection or display a message to the user.

Here's an example of how you could implement this:

First, add a new property to your component for blocked dates. This property could be an array of strings or Date objects, depending on how you prefer to manage dates. For simplicity, let's use an array of strings in the YYYY-MM-DD format:

datePickerBlockedDates: ['2024-02-20', '2024-02-21'], // Example blocked dates

Then, modify the datePickerDayClicked function to check if the selected day is blocked:

datePickerDayClicked(day) {
    let selectedDate = new Date(this.datePickerYear, this.datePickerMonth, day);
    let formattedDate = this.datePickerFormatDate(selectedDate, 'YYYY-MM-DD'); // Assuming you add this variant to your datePickerFormatDate function

    // Check if selectedDate is in blocked dates
    if (this.datePickerBlockedDates.includes(formattedDate)) {
        alert('This date is not available for appointments.'); // Or handle the blocked date as needed
        return;
    }

    this.datePickerDay = day;
    this.datePickerValue = this.datePickerFormatDate(selectedDate);
    this.datePickerIsSelectedDate(day);
    this.datePickerOpen = false;
},

You might also need to adjust the datePickerFormatDate function to support the YYYY-MM-DD format directly or ensure it can be used to generate strings in the format you're using for your datePickerBlockedDates array.

Finally, to visually indicate blocked dates, you could modify the section of your template that renders each day. Add a condition to apply a different style (e.g., graying out the date) to blocked dates:

:class="{
    'bg-neutral-200': datePickerIsToday(day) == true, 
    'text-gray-600 hover:bg-neutral-200': datePickerIsToday(day) == false && datePickerIsSelectedDate(day) == false && !datePickerBlockedDates.includes(datePickerFormatDate(new Date(datePickerYear, datePickerMonth, day), 'YYYY-MM-DD')),
    'bg-neutral-800 text-white hover:bg-opacity-75': datePickerIsSelectedDate(day) == true,
    'text-gray-400 cursor-not-allowed': datePickerBlockedDates.includes(datePickerFormatDate(new Date(datePickerYear, datePickerMonth, day), 'YYYY-MM-DD')) // This line added to handle blocked dates
}" 

This approach requires you to maintain and dynamically update the datePickerBlockedDates array based on your application's logic for unavailable dates.

Here is the complete code:

<div x-data="{
      datePickerOpen: false,
      datePickerValue: '',
      datePickerFormat: 'M d, Y',
      datePickerMonth: '',
      datePickerYear: '',
      datePickerDay: '',
      datePickerDaysInMonth: [],
      datePickerBlankDaysInMonth: [],
      datePickerBlockedDates: ['2024-02-14', '2024-02-15'], // Example blocked dates
      datePickerMonthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
      datePickerDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
      datePickerDayClicked(day) {
        let selectedDate = new Date(this.datePickerYear, this.datePickerMonth, day);
        let formattedDate = this.datePickerFormatDate(selectedDate, 'YYYY-MM-DD'); // Add support for this format in datePickerFormatDate function

        // Check if selectedDate is in blocked dates
        if (this.datePickerBlockedDates.includes(formattedDate)) {
            alert('This date is not available for appointments.'); // Or handle the blocked date as needed
            return;
        }

        this.datePickerDay = day;
        this.datePickerValue = this.datePickerFormatDate(selectedDate);
        this.datePickerIsSelectedDate(day);
        this.datePickerOpen = false;
      },
      datePickerPreviousMonth(){
        if (this.datePickerMonth == 0) { 
            this.datePickerYear--; 
            this.datePickerMonth = 12; 
        } 
        this.datePickerMonth--;
        this.datePickerCalculateDays();
      },
      datePickerNextMonth(){
        if (this.datePickerMonth == 11) { 
            this.datePickerMonth = 0; 
            this.datePickerYear++; 
        } else { 
            this.datePickerMonth++; 
        }
        this.datePickerCalculateDays();
      },
      datePickerIsSelectedDate(day) {
        const d = new Date(this.datePickerYear, this.datePickerMonth, day);
        return this.datePickerValue === this.datePickerFormatDate(d) ? true : false;
      },
      datePickerIsToday(day) {
        const today = new Date();
        const d = new Date(this.datePickerYear, this.datePickerMonth, day);
        return today.toDateString() === d.toDateString() ? true : false;
      },
      datePickerCalculateDays() {
        let daysInMonth = new Date(this.datePickerYear, this.datePickerMonth + 1, 0).getDate();
        let dayOfWeek = new Date(this.datePickerYear, this.datePickerMonth).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.datePickerBlankDaysInMonth = blankdaysArray;
        this.datePickerDaysInMonth = daysArray;
      },
      datePickerFormatDate(date, format = this.datePickerFormat) {
        let formattedDay = this.datePickerDays[date.getDay()];
        let formattedDate = ('0' + date.getDate()).slice(-2); // appends 0 (zero) in single digit date
        let formattedMonth = this.datePickerMonthNames[date.getMonth()];
        let formattedMonthShortName = this.datePickerMonthNames[date.getMonth()].substring(0, 3);
        let formattedMonthInNumber = ('0' + (parseInt(date.getMonth()) + 1)).slice(-2);
        let formattedYear = date.getFullYear();

        if (format === 'M d, Y') {
          return `${formattedMonthShortName} ${formattedDate}, ${formattedYear}`;
        }
        if (format === 'MM-DD-YYYY') {
          return `${formattedMonthInNumber}-${formattedDate}-${formattedYear}`;
        }
        if (format === 'DD-MM-YYYY') {
          return `${formattedDate}-${formattedMonthInNumber}-${formattedYear}`;
        }
        if (format === 'YYYY-MM-DD') {
          return `${formattedYear}-${formattedMonthInNumber}-${formattedDate}`;
        }
        if (format === 'D d M, Y') {
          return `${formattedDay} ${formattedDate} ${formattedMonthShortName} ${formattedYear}`;
        }
        
        return `${formattedMonth} ${formattedDate}, ${formattedYear}`;
      },
    }" x-init="
        currentDate = new Date();
        if (datePickerValue) {
            currentDate = new Date(Date.parse(datePickerValue));
        }
        datePickerMonth = currentDate.getMonth();
        datePickerYear = currentDate.getFullYear();
        datePickerDay = currentDate.getDay();
        datePickerValue = datePickerFormatDate(currentDate);
        datePickerCalculateDays();
    " x-cloak>
    <div class="container px-4 py-2 mx-auto md:py-10">
        <div class="w-full mb-5">
            <label for="datepicker" class="block mb-1 text-sm font-medium text-neutral-500">Select Date</label>
            <div class="relative w-[17rem]">
                <input x-ref="datePickerInput" type="text" @click="datePickerOpen=!datePickerOpen" x-model="datePickerValue" x-on:keydown.escape="datePickerOpen=false" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md text-neutral-600 border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50" placeholder="Select date" readonly />
                <div @click="datePickerOpen=!datePickerOpen; if(datePickerOpen){ $refs.datePickerInput.focus() }" class="absolute top-0 right-0 px-3 py-2 cursor-pointer text-neutral-400 hover:text-neutral-500">
                    <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>
                </div>
                <div  
                    x-show="datePickerOpen"
                    x-transition
                    @click.away="datePickerOpen = false" 
                    class="absolute top-0 left-0 max-w-lg p-4 mt-12 antialiased bg-white border rounded-lg shadow w-[17rem] border-neutral-200/70">
                    <div class="flex items-center justify-between mb-2">
                        <div>
                            <span x-text="datePickerMonthNames[datePickerMonth]" class="text-lg font-bold text-gray-800"></span>
                            <span x-text="datePickerYear" class="ml-1 text-lg font-normal text-gray-600"></span>
                        </div>
                        <div>
                            <button @click="datePickerPreviousMonth()" type="button" class="inline-flex p-1 transition duration-100 ease-in-out rounded-full cursor-pointer focus:outline-none focus:shadow-outline hover:bg-gray-100">
                                <svg class="inline-flex w-6 h-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /></svg>
                            </button>
                            <button @click="datePickerNextMonth()" type="button" class="inline-flex p-1 transition duration-100 ease-in-out rounded-full cursor-pointer focus:outline-none focus:shadow-outline hover:bg-gray-100">
                                <svg class="inline-flex w-6 h-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
                            </button>
                        </div>
                    </div>
                    <div class="grid grid-cols-7 mb-3">
                        <template x-for="(day, index) in datePickerDays" :key="index">
                            <div class="px-0.5">
                                <div x-text="day" class="text-xs font-medium text-center text-gray-800"></div>
                            </div>
                        </template>
                    </div>
                    <div class="grid grid-cols-7">
                        <template x-for="blankDay in datePickerBlankDaysInMonth">
                            <div class="p-1 text-sm text-center border border-transparent"></div>
                        </template>
                        <template x-for="(day, dayIndex) in datePickerDaysInMonth" :key="dayIndex">
                            <div class="px-0.5 mb-1 aspect-square">
                                <div 
                                    x-text="day"
                                    @click="datePickerDayClicked(day)" 
                                    :class="{
                                        'bg-neutral-200': datePickerIsToday(day) == true, 
                                        'text-gray-600 hover:bg-neutral-200': datePickerIsToday(day) == false && datePickerIsSelectedDate(day) == false && !datePickerBlockedDates.includes(datePickerFormatDate(new Date(datePickerYear, datePickerMonth, day), 'YYYY-MM-DD')),
                                        'bg-neutral-800 text-white hover:bg-opacity-75': datePickerIsSelectedDate(day) == true,
                                        'text-gray-400 cursor-not-allowed': datePickerBlockedDates.includes(datePickerFormatDate(new Date(datePickerYear, datePickerMonth, day), 'YYYY-MM-DD')) // This line handles blocked dates
                                    }" 
                                    class="flex items-center justify-center text-sm leading-none text-center rounded-full cursor-pointer h-7 w-7"></div>
                            </div>
                        </template>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Hope that this helps!

Best,

Bobby