IKHSAN
EN Read in EnglishID

opendesk

Publik
Branch
main
Commit terakhir
08d9f3328 Oktober 2026

HolidaysCalendarView.vue

Buka raw
<template>
  <div class="p-6.5 px-5 rounded-7 border border-outline-gray-2">
    <div class="mb-6.5 flex justify-between items-center">
      <div class="ms-1">
        <Popover v-if="startYear !== endYear">
          <template #trigger>
            <Button
              class="flex items-center gap-2 text-xl-semibold cursor-pointer select-none"
              variant="ghost"
              :label="currentYear + ''"
              icon-right="lucide-chevron-down"
            />
          </template>
          <template #default="{ close: closePopover }">
            <div class="w-24">
              <div ref="yearsContainer" class="max-h-60 overflow-y-auto py-1">
                <div
                  v-for="year in yearsList"
                  :key="year"
                  ref="yearItems"
                  class="cursor-pointer px-3 py-1.5 text-sm hover:bg-surface-gray-2 flex items-center justify-between"
                  @click="onYearChange(closePopover, year)"
                >
                  {{ year }}
                  <LucideCheck class="size-4" v-if="year === currentYear" />
                </div>
              </div>
            </div>
          </template>
        </Popover>
        <div
          v-else
          class="flex items-center gap-2 px-2 text-xl-semibold select-none"
        >
          {{ startYear }}
        </div>
      </div>
      <div class="flex gap-2 items-center">
        <Button
          variant="ghost"
          icon="lucide-chevron-left"
          :disabled="visibleMonths === 'first-half'"
          @click="visibleMonths = 'first-half'"
        />
        <Button variant="ghost" label="Today" @click="goToToday()" />
        <Button
          variant="ghost"
          icon="lucide-chevron-right"
          :disabled="visibleMonths === 'second-half'"
          @click="visibleMonths = 'second-half'"
        />
      </div>
    </div>
    <div
      class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5"
      v-if="visibleMonths === 'first-half'"
    >
      <HLCalender
        v-for="month in months.slice(0, 6)"
        :key="month"
        :year="currentYear"
        :month="month"
        :holidays="holidayData.holidays"
      />
    </div>
    <div
      class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 justify-between gap-5"
      v-else
    >
      <HLCalender
        v-for="month in months.slice(6, 12)"
        :key="month"
        :year="currentYear"
        :month="month"
        :holidays="holidayData.holidays"
      />
    </div>
    <div class="flex gap-2 items-center w-full justify-center mt-8">
      <div
        :class="[
          'size-1.5 rounded-full cursor-pointer',
          {
            'bg-surface-gray-10': visibleMonths === 'first-half',
            'bg-surface-gray-4': visibleMonths === 'second-half',
          },
        ]"
        @click="visibleMonths = 'first-half'"
      />
      <div
        :class="[
          'size-1.5 rounded-full cursor-pointer',
          {
            'bg-surface-gray-10': visibleMonths === 'second-half',
            'bg-surface-gray-4': visibleMonths === 'first-half',
          },
        ]"
        @click="visibleMonths = 'second-half'"
      />
    </div>
  </div>
</template>
<script setup lang="ts">
import LucideCheck from "~icons/lucide/check";
import { computed, ref, watch } from "vue";
import HLCalender from "./HLCalender.vue";
import { holidayData } from "@/stores/holidayList";
import { Button, dayjs, Popover } from "frappe-ui";

const visibleMonths = ref<"first-half" | "second-half">("first-half");
const months = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
const currentYear = ref(dayjs().year());

const startYear = ref(dayjs(holidayData.value.from_date || dayjs()).year());
const endYear = ref(dayjs(holidayData.value.to_date || dayjs()).year());

const yearsList = computed(() => {
  const yearList = [];
  for (let year = startYear.value; year <= endYear.value; year++) {
    yearList.push(year);
  }
  return yearList;
});

const onYearChange = (closePopover: () => void, year: number) => {
  currentYear.value = year;
  if (year === dayjs(holidayData.value.from_date).year()) {
    if (dayjs(holidayData.value.from_date).month() >= 6) {
      visibleMonths.value = "second-half";
    }
  } else if (year === dayjs(holidayData.value.to_date).year()) {
    if (dayjs(holidayData.value.to_date).month() >= 6) {
      visibleMonths.value = "first-half";
    }
  } else {
    visibleMonths.value = "first-half";
  }
  closePopover();
};

const goToToday = () => {
  const today = dayjs();
  currentYear.value = today.year();
  visibleMonths.value = today.month() >= 6 ? "second-half" : "first-half";
};

watch(
  () => [holidayData.value.from_date, holidayData.value.to_date],
  ([fromDate, toDate]) => {
    fromDate = dayjs(fromDate || dayjs());
    toDate = dayjs(toDate || dayjs());
    startYear.value = fromDate.year();
    endYear.value = toDate.year();
    currentYear.value = fromDate.year();
    visibleMonths.value = fromDate.month() >= 6 ? "second-half" : "first-half";
  },
  // The list is loaded before this view mounts, so a change-only watch never fires.
  { immediate: true }
);
</script>
byte
5.178
baris
160
Jenis
vue
Commit terakhir
28 Oktober 2026