11<script setup>
22/** Vendor */
3- import { DateTime } from " luxon"
3+ import { DateTime , Info } from " luxon"
44
55/** UI */
66import Button from " @/components/ui/Button.vue"
@@ -16,13 +16,70 @@ const props = defineProps({
1616
1717const month = ref (DateTime .now ().month )
1818const year = ref (DateTime .now ().year )
19- const startDate = ref (null )
20- const endDate = ref (null )
21- const days = ref ([])
19+ const startDate = ref ({})
20+ const endDate = ref ({})
21+ const weekdays = ref (Info .weekdays (' narrow' , { locale: ' en-US' }))
22+ const days = computed (() => {
23+ let rawDays = []
24+ const firstDay = DateTime .local (year .value , month .value ).setLocale (' en-US' )
25+ const lastDay = firstDay .endOf (' month' )
2226
23- // console.log(month.value);
24- // console.log(year.value);
25- // console.log(DateTime.local(year.value, month.value).toFormat('LLLL'));
27+ for (let day = firstDay; day <= lastDay; day = day .plus ({ days: 1 })) {
28+ rawDays .push (day)
29+ }
30+
31+ if (firstDay .weekday !== 1 ) {
32+ let prevDay = firstDay
33+ while (prevDay .weekday !== 1 ) {
34+ prevDay = prevDay .minus ({ days: 1 }).startOf (' day' )
35+ rawDays .unshift (prevDay)
36+ }
37+ }
38+
39+ if (lastDay .weekday !== 7 ) {
40+ let nextDay = lastDay
41+ while (nextDay .weekday !== 7 ) {
42+ nextDay = nextDay .plus ({ days: 1 }).startOf (' day' )
43+ rawDays .push (nextDay)
44+ }
45+ }
46+
47+ let resDays = []
48+ while (rawDays .length ) {
49+ resDays .push (rawDays .splice (0 , 7 ))
50+ }
51+
52+ return resDays
53+ })
54+
55+ const handleSelectDate = (d ) => {
56+ if (! startDate .value .ts ) {
57+ startDate .value = d
58+ } else if (startDate .value .ts !== d .ts ) {
59+ if (! endDate .value .ts ) {
60+ if (startDate .value .ts > d .ts ) {
61+ endDate .value = startDate .value
62+ startDate .value = d
63+ } else {
64+ endDate .value = d
65+ }
66+ } else {
67+ startDate .value = d
68+ endDate .value = {}
69+ }
70+ } else {
71+ if (! endDate .value .ts ) {
72+ startDate .value = {}
73+ } else {
74+ startDate .value = endDate .value
75+ endDate .value = {}
76+ }
77+ }
78+ }
79+
80+ const isInSelectedPeriod = (d ) => {
81+ return startDate .value .ts < d .ts && d .ts < endDate .value .ts
82+ }
2683
2784const isOpen = ref (false )
2885const handleOpen = () => {
@@ -53,11 +110,11 @@ const handleMonthChange = (v) => {
53110 switch (month .value + v) {
54111 case 0 :
55112 month .value = 12
56- year .value -= 1
113+ year .value --
57114 break
58115 case 13 :
59116 month .value = 1
60- year .value += 1
117+ year .value ++
61118 break
62119 default :
63120 month .value += v
@@ -75,136 +132,132 @@ const handleMonthChange = (v) => {
75132
76133 <template #content >
77134 <Flex direction =" column" gap =" 12" >
78- <!-- <div :class="$style.calendar"> -->
79- <div >
80- <!-- <Flex align="center" gap="6" :class="$style.calendar_header"> -->
81- <Flex align =" center" justify =" center" gap =" 6" >
82- <Icon
83- @click =" handleMonthChange(-1)"
84- name =" chevron"
85- size =" 14"
86- color =" tertiary"
87- :style =" { transform: 'rotate(90deg)' }"
88- />
89-
90- <Text size =" 12" color =" secondary" > {{ `${DateTime.local(year, month).toFormat('LLLL')} ${year}` }} </Text >
91-
92- <Icon
93- @click =" handleMonthChange(1)"
94- name =" chevron"
95- size =" 14"
96- color =" tertiary"
97- :style =" { transform: 'rotate(-90deg)' }"
98- />
99- </Flex >
100- <!-- <div class="calendar-body">
101- <div class="day" v-for="day in days" :key="day.date">
102- <span
103- :class="{
104- 'is-today': isToday(day.date),
105- 'is-selected': isSelected(day.date),
106- 'is-in-range': isInRange(day.date)
107- }"
108- @click="selectDate(day.date)"
109- >
110- {{ day.date.getDate() }}
111- </span>
112- </div>
113- </div>
114- <div class="selected-dates">
115- <p>Start Date: {{ formatDate(startDate) }}</p>
116- <p>End Date: {{ formatDate(endDate) }}</p>
117- </div> -->
118- </div >
119-
120-
121- <!-- <Text size="12" weight="500" color="secondary">Filter by Date</Text> -->
122-
123- <!-- <Input v-model="searchTerm" size="small" placeholder="Search" autofocus /> -->
124-
125- <!-- <Flex direction="column" gap="8" :class="$style.message_types_list">
126- <template
127- v-if="
128- Object.keys(filters.message_type).filter((t) =>
129- t.toLowerCase().includes(searchTerm.trim().toLowerCase()),
130- ).length
131- "
132- >
133- <Checkbox
134- v-for="msg_type in Object.keys(filters.message_type).filter((t) =>
135- t.toLowerCase().includes(searchTerm.trim().toLowerCase()),
136- )"
137- v-model="filters.message_type[msg_type]"
138- >
139- <Text size="12" weight="500" color="primary">{{ msg_type.replace("Msg", "") }}</Text>
140- </Checkbox>
141- </template>
142- <Flex v-else direction="column" gap="8">
143- <Text size="12" weight="500" color="tertiary">Nothing was found</Text>
144- </Flex>
145- </Flex> -->
135+ <Flex align =" center" justify =" center" gap =" 6" >
136+ <Icon
137+ @click =" handleMonthChange(-1)"
138+ name =" chevron"
139+ size =" 14"
140+ color =" tertiary"
141+ :style =" { transform: 'rotate(90deg)' }"
142+ />
143+
144+ <Text size =" 12" color =" secondary" > {{ `${DateTime.local(year, month).toFormat('LLLL')} ${year}` }} </Text >
145+
146+ <Icon
147+ @click =" handleMonthChange(1)"
148+ name =" chevron"
149+ size =" 14"
150+ color =" tertiary"
151+ :style =" { transform: 'rotate(-90deg)' }"
152+ />
153+ </Flex >
154+
155+ <Flex direction =" column" gap =" 16" wide :class =" $style.table" >
156+ <table >
157+ <thead >
158+ <tr >
159+ <th v-for =" wd in weekdays" >
160+ <Text size =" 10" color =" secondary" > {{ wd }} </Text >
161+ </th >
162+ </tr >
163+ </thead >
164+
165+ <tbody >
166+ <tr v-for =" w in days" >
167+ <td v-for =" d in w" >
168+ <Flex align =" center" justify =" center"
169+ @click =" handleSelectDate(d)"
170+ :class =" [
171+ $style.day,
172+ (d.ts === startDate.ts || d.ts === endDate.ts) && $style.edgeDate,
173+ isInSelectedPeriod(d) && $style.inSelectedPeriod
174+ ]"
175+ >
176+ <Text size =" 12" color =" primary"
177+ :class =" [
178+ d.month !== month && $style.notInCurrentMonth,
179+ (d.ts === startDate.ts || d.ts === endDate.ts || isInSelectedPeriod(d)) && $style.text_primary
180+ ]"
181+ > {{ d.day }} </Text >
182+ </Flex >
183+ </td >
184+ </tr >
185+ </tbody >
186+ </table >
187+ </Flex >
146188
147189 <Button @click =" handleApply" type =" secondary" size =" mini" wide >Apply</Button >
148190 </Flex >
149191 </template >
150192 </Popover >
151- <!-- <Popover :open="isMessageTypePopoverOpen" @on-close="onMessageTypePopoverClose" width="250">
152- <Button @click="handleOpenMessageTypePopover" type="secondary" size="mini">
153- <Icon name="plus-circle" size="12" color="tertiary" />
154- <Text color="secondary">Message Type</Text>
155-
156- <template v-if="Object.keys(filters.message_type).find((f) => filters.message_type[f])">
157- <div :class="$style.vertical_divider" />
158-
159- <Text size="12" weight="600" color="primary">
160- {{
161- Object.keys(filters.message_type).filter((f) => filters.message_type[f]).length < 3
162- ? Object.keys(filters.message_type)
163- .filter((f) => filters.message_type[f])
164- .map((f) => f.replace("Msg", ""))
165- .join(", ")
166- : `${Object.keys(filters.message_type)
167- .filter((f) => filters.message_type[f])[0]
168- .replace("Msg", "")} and ${
169- Object.keys(filters.message_type).filter((f) => filters.message_type[f]).length - 1
170- } more`
171- }}
172- </Text>
173-
174- <Icon @click.stop="resetFilters('message_type', true)" name="close-circle" size="12" color="secondary" />
175- </template>
176- </Button>
193+ </template >
177194
178- <template #content>
179- <Flex direction="column" gap="12">
180- <Text size="12" weight="500" color="secondary">Filter by Message Type</Text>
181-
182- <Input v-model="searchTerm" size="small" placeholder="Search" autofocus />
183-
184- <Flex direction="column" gap="8" :class="$style.message_types_list">
185- <template
186- v-if="
187- Object.keys(filters.message_type).filter((t) =>
188- t.toLowerCase().includes(searchTerm.trim().toLowerCase()),
189- ).length
190- "
191- >
192- <Checkbox
193- v-for="msg_type in Object.keys(filters.message_type).filter((t) =>
194- t.toLowerCase().includes(searchTerm.trim().toLowerCase()),
195- )"
196- v-model="filters.message_type[msg_type]"
197- >
198- <Text size="12" weight="500" color="primary">{{ msg_type.replace("Msg", "") }}</Text>
199- </Checkbox>
200- </template>
201- <Flex v-else direction="column" gap="8">
202- <Text size="12" weight="500" color="tertiary">Nothing was found</Text>
203- </Flex>
204- </Flex>
195+ <style module>
196+ .table {
197+ transition : all 0.2s ease ;
205198
206- <Button @click="handleApplyMessageTypeFilters" type="secondary" size="mini" wide>Apply</Button>
207- </Flex>
208- </template>
209- </Popover> -->
210- </template >
199+ & table {
200+ width : 100% ;
201+ height : fit-content ;
202+
203+ border-spacing : 0px ;
204+
205+ & tbody {
206+ & tr {
207+ transition : all 0.05s ease ;
208+ }
209+ }
210+
211+ & tr th {
212+ text-align : center ;
213+ padding-bottom : 8px ;
214+ }
215+
216+ & tr td {
217+ text-align : center ;
218+
219+ cursor : pointer ;
220+ }
221+
222+ th :first-child , td :first-child {
223+ border-radius : 3px ;
224+ }
225+
226+ th :last-child , td :last-child {
227+ border-radius : 3px ;
228+ }
229+ }
230+ }
231+
232+ .day {
233+ min-width : 20px ;
234+ min-height : 20px ;
235+
236+ border-radius : 3px ;
237+ }
238+
239+ .day :hover {
240+ background-color : rgba (51 , 168 , 83 , 70% );
241+ }
242+
243+ .notInCurrentMonth {
244+ color : var (--txt-tertiary );
245+ }
246+
247+ .edgeDate {
248+ background-color : rgba (51 , 168 , 83 , 70% );
249+ /* background-color: var(--neutral-green); */
250+ }
251+
252+ .inSelectedPeriod {
253+ background-color : var (--btn-secondary-bg );
254+ }
255+
256+ .text_primary {
257+ color : var (--txt-primary );
258+ }
259+
260+ .endDate {
261+ background-color : rgba (51 , 168 , 83 , 70% );
262+ }
263+ </style >
0 commit comments