1+ <script setup lang="ts">
2+ import { Switch } from ' @/Components/ui/switch' ;
3+ import { Popover , PopoverContent , PopoverTrigger } from ' @/Components/ui/popover' ;
4+ import { Button } from ' @/Components/ui/button' ;
5+ import {
6+ Select ,
7+ SelectContent ,
8+ SelectItem ,
9+ SelectTrigger ,
10+ SelectValue
11+ } from ' @/Components/ui/select' ;
12+ import InputLabel from ' @/packages/ui/src/Input/InputLabel.vue' ;
13+ import {
14+ NumberField ,
15+ NumberFieldInput ,
16+ NumberFieldContent ,
17+ NumberFieldIncrement ,
18+ NumberFieldDecrement
19+ } from ' @/Components/ui/number-field' ;
20+ import { ArrowsUpDownIcon } from ' @heroicons/vue/20/solid' ;
21+ import { computed , ref , watch } from ' vue' ;
22+ // TimeEntryRoundingType definition
23+ const TimeEntryRoundingType = {
24+ Up: ' up' as const ,
25+ Down: ' down' as const ,
26+ Nearest: ' nearest' as const ,
27+ } as const ;
28+
29+ type TimeEntryRoundingType = typeof TimeEntryRoundingType [keyof typeof TimeEntryRoundingType ];
30+
31+ interface Props {
32+ enabled: boolean ;
33+ type: TimeEntryRoundingType ;
34+ minutes: number ;
35+ }
36+
37+ const props = defineProps <Props >();
38+
39+ const emit = defineEmits <{
40+ ' update:enabled' : [value : boolean ];
41+ ' update:type' : [value : TimeEntryRoundingType ];
42+ ' update:minutes' : [value : number ];
43+ ' change' : [];
44+ }>();
45+
46+ function updateEnabled(value : boolean ) {
47+ emit (' update:enabled' , value );
48+ emit (' change' );
49+ }
50+
51+ function updateType(value : TimeEntryRoundingType ) {
52+ emit (' update:type' , value );
53+ emit (' change' );
54+ }
55+
56+ function updateMinutes(value : number ) {
57+ emit (' update:minutes' , value );
58+ emit (' change' );
59+ }
60+
61+ // Predefined intervals
62+ const predefinedIntervals = [
63+ { value: ' 5' , label: ' 5 minutes' },
64+ { value: ' 6' , label: ' 6 minutes' },
65+ { value: ' 10' , label: ' 10 minutes' },
66+ { value: ' 15' , label: ' 15 minutes' },
67+ { value: ' 30' , label: ' 30 minutes' },
68+ { value: ' 60' , label: ' 1 hour' },
69+ { value: ' custom' , label: ' Custom' },
70+ ];
71+
72+ const showCustomInput = ref (false );
73+ const customMinutes = ref (props .minutes );
74+ const selectedInterval = ref (' ' );
75+
76+ // Compute the current interval value based on props
77+ const currentInterval = computed (() => {
78+ const predefined = predefinedIntervals .find (interval =>
79+ interval .value !== ' custom' && parseInt (interval .value ) === props .minutes
80+ );
81+ return predefined ? predefined .value : ' custom' ;
82+ });
83+
84+ // Initialize selectedInterval
85+ const initializeSelectedInterval = () => {
86+ selectedInterval .value = currentInterval .value ;
87+ showCustomInput .value = selectedInterval .value === ' custom' ;
88+ if (showCustomInput .value ) {
89+ customMinutes .value = props .minutes ;
90+ }
91+ };
92+
93+ function handleIntervalChange(value : string ) {
94+ selectedInterval .value = value ;
95+ if (value === ' custom' ) {
96+ showCustomInput .value = true ;
97+ // Update minutes to current custom value to ensure "custom" shows as selected
98+ updateMinutes (customMinutes .value );
99+ } else {
100+ showCustomInput .value = false ;
101+ const minutes = parseInt (value );
102+ updateMinutes (minutes );
103+ }
104+ }
105+
106+ function handleCustomMinutesChange(value : string | number ) {
107+ const numValue = typeof value === ' string' ? parseInt (value ) : value ;
108+ if (! isNaN (numValue ) && numValue > 0 ) {
109+ customMinutes .value = numValue ;
110+ updateMinutes (numValue );
111+ }
112+ }
113+
114+ // Watch for changes in props.minutes
115+ watch (() => props .minutes , (newMinutes ) => {
116+ customMinutes .value = newMinutes ;
117+ initializeSelectedInterval ();
118+ }, { immediate: true });
119+
120+ watch (currentInterval , () => {
121+ initializeSelectedInterval ();
122+ });
123+ </script >
124+
125+ <template >
126+ <Popover >
127+ <PopoverTrigger as-child >
128+ <Button
129+ variant =" outline"
130+ size =" sm"
131+ class =" text-sm" >
132+ <ArrowsUpDownIcon class =" w-4 h-4" :class =" enabled ? 'text-primary' : 'text-muted-foreground opacity-50'" />
133+ Rounding {{ enabled ? 'on' : 'off' }}
134+ </Button >
135+ </PopoverTrigger >
136+ <PopoverContent class =" w-72 p-4" >
137+ <div class =" space-y-4" >
138+ <div class =" flex items-center justify-between" >
139+ <InputLabel for =" enable-rounding" value =" Enable Rounding" />
140+ <Switch
141+ id =" enable-rounding"
142+ :model-value =" enabled"
143+ class =" data-[state=checked]:bg-accent-500"
144+ @update:model-value =" updateEnabled" />
145+ </div >
146+
147+ <div >
148+ <InputLabel for =" rounding-type" value =" Rounding Type" class =" mb-2" />
149+ <Select
150+ :model-value =" type"
151+ :disabled =" !enabled"
152+ @update:model-value =" (value) => updateType(value as TimeEntryRoundingType)" >
153+ <SelectTrigger id =" rounding-type" size =" small" class =" w-full" :disabled =" !enabled" >
154+ <SelectValue placeholder =" Select rounding type" />
155+ </SelectTrigger >
156+ <SelectContent >
157+ <SelectItem value =" up" >Round Up</SelectItem >
158+ <SelectItem value =" down" >Round Down</SelectItem >
159+ <SelectItem value =" nearest" >Round Nearest</SelectItem >
160+ </SelectContent >
161+ </Select >
162+ </div >
163+ <div >
164+ <InputLabel for =" minutes-interval" value =" Minutes Interval" class =" mb-2" />
165+ <Select
166+ :model-value =" selectedInterval"
167+ :disabled =" !enabled"
168+ @update:model-value =" (value) => handleIntervalChange(value as string)" >
169+ <SelectTrigger id =" minutes-interval" size =" small" class =" w-full" :disabled =" !enabled" >
170+ <SelectValue placeholder =" Select interval" />
171+ </SelectTrigger >
172+ <SelectContent >
173+ <SelectItem
174+ v-for =" interval in predefinedIntervals"
175+ :key =" interval.value"
176+ :value =" interval.value" >
177+ {{ interval.label }}
178+ </SelectItem >
179+ </SelectContent >
180+ </Select >
181+
182+ <div v-if =" showCustomInput" class =" mt-2" >
183+ <NumberField
184+ id =" custom-minutes"
185+ :model-value =" customMinutes"
186+ size =" small"
187+ :min =" 1"
188+ :max =" 1440"
189+ :disabled =" !enabled"
190+ class =" text-sm"
191+ @update:model-value =" handleCustomMinutesChange" >
192+ <NumberFieldContent >
193+ <NumberFieldDecrement :disabled =" !enabled" />
194+ <NumberFieldInput placeholder =" Enter custom minutes" :disabled =" !enabled" />
195+ <NumberFieldIncrement :disabled =" !enabled" />
196+ </NumberFieldContent >
197+ </NumberField >
198+ </div >
199+ </div >
200+ </div >
201+ </PopoverContent >
202+ </Popover >
203+ </template >
0 commit comments