1<template>
2 <Dialog
3 v-model:open="dialog.show"
4 size="sm"
5 :title="dialog.editing ? 'Edit Holiday' : 'Add Holiday'"
6 @after-leave="resetForm"
7 >
8 <template #default>
9 <div class="flex flex-col gap-4 mt-4">
10 <div class="flex flex-col gap-1.5">
11 <FormLabel label="Date" required />
12 <DatePicker
13 :model-value="dayjs(dialog.holiday_date).format('YYYY-MM-DD')"
14 @update:model-value="
15 (value) => {
16 dialog.holiday_date = value;
17 errors.holiday_date = '';
18 }
19 "
20 :format="getDateFormat()"
21 variant="subtle"
22 placeholder="Date"
23 class="w-full"
24 id="holiday_date"
25 required
26 />
27 <ErrorMessage :message="errors.holiday_date" />
28 </div>
29 <div class="flex flex-col gap-1.5">
30 <FormControl
31 :type="'textarea'"
32 size="sm"
33 variant="subtle"
34 placeholder="National holiday, etc."
35 label="Description"
36 v-model="dialog.description"
37 required
38 @change="errors.description = ''"
39 />
40 <ErrorMessage :message="errors.description" />
41 </div>
42 </div>
43 </template>
44 <template #actions>
45 <div class="flex justify-end gap-2">
46 <Button
47 variant="subtle"
48 theme="gray"
49 label="Cancel"
50 @click="dialog.show = false"
51 />
52 <Button
53 variant="solid"
54 icon-left="lucide-plus"
55 label="Add Holiday"
56 @click="onSave"
57 />
58 </div>
59 </template>
60 </Dialog>
61</template>
62
63<script setup lang="ts">
64import { getDateFormat, getFormattedDate } from "@/utils";
65import {
66 Dialog,
67 FormControl,
68 Button,
69 FormLabel,
70 toast,
71 ErrorMessage,
72 DatePicker,
73 dayjs,
74} from "frappe-ui";
75import { ref } from "vue";
76import { holidayData } from "@/stores/holidayList";
77
78interface ModelType {
79 show: boolean;
80 holiday_date: null | string | Date;
81 description: string;
82 editing: null | any;
83}
84
85const dialog = defineModel<ModelType>();
86
87const errors = ref({
88 holiday_date: "",
89 description: "",
90});
91
92const resetForm = () => {
93 dialog.value.holiday_date = new Date();
94 dialog.value.description = "";
95 errors.value = {
96 holiday_date: "",
97 description: "",
98 };
99};
100
101const onSave = () => {
102 if (dialog.value.description?.trim() === "") {
103 errors.value.description = "Please enter a description";
104 }
105 if (!dialog.value.holiday_date) {
106 errors.value.holiday_date = "Please enter a valid date";
107 }
108
109 if (errors.value.holiday_date || errors.value.description) {
110 return;
111 }
112
113 const holidayDate = dayjs(dialog.value.holiday_date).startOf("day");
114 const fromDate = dayjs(holidayData.value.from_date).startOf("day");
115 const toDate = dayjs(holidayData.value.to_date).startOf("day");
116
117 if (holidayDate.isBefore(fromDate) || holidayDate.isAfter(toDate)) {
118 toast.error(
119 `Holiday date must be between ${getFormattedDate(
120 holidayData.value.from_date
121 )} and ${getFormattedDate(holidayData.value.to_date)}`
122 );
123 return;
124 }
125
126 if (dialog.value.editing) {
127 const holidayExists = holidayData.value.holidays.find(
128 (h) =>
129 getFormattedDate(h.holiday_date) ===
130 getFormattedDate(dialog.value.holiday_date)
131 );
132
133 // If the holiday exists and user is trying to add a new holiday on the same date, show error
134 if (
135 holidayExists &&
136 getFormattedDate(holidayExists.holiday_date) !==
137 getFormattedDate(dialog.value.editing.holiday_date)
138 ) {
139 toast.error("Holiday already exists");
140 return;
141 }
142 const holidayIndex = holidayData.value.holidays.indexOf(
143 dialog.value.editing
144 );
145 holidayData.value.holidays.splice(holidayIndex, 1, {
146 ...dialog.value,
147 weekly_off: 0,
148 });
149 } else {
150 const index = holidayData.value.holidays.findIndex(
151 (h) =>
152 getFormattedDate(h.holiday_date) ===
153 getFormattedDate(dialog.value.holiday_date)
154 );
155 if (index !== -1) {
156 toast.error("Holiday already exists");
157 return;
158 }
159 holidayData.value.holidays.push({
160 ...dialog.value,
161 weekly_off: 0,
162 });
163 }
164
165 dialog.value = {
166 show: false,
167 holiday_date: null,
168 description: "",
169 editing: null,
170 };
171};
172</script>
173