Skip to content

Commit 92948d5

Browse files
committed
feat: duration field
1 parent 08d1f82 commit 92948d5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+680
-29
lines changed

apps/frontend/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum FieldType {
3636
createdBy
3737
currency
3838
date
39+
duration
3940
email
4041
id
4142
json
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts">
2+
import { durationToMilliseconds, isDurationString, millisecondsToDuration } from "@undb/table"
3+
4+
export let value: number | undefined
5+
export let onValueChange: ((value: number) => void) | undefined = undefined
6+
7+
let internalValue = value ? millisecondsToDuration(value) : ""
8+
9+
export let isValid = true
10+
11+
function onChange(event: Event) {
12+
const valueString = (event.target as HTMLInputElement).value
13+
if (!isDurationString(valueString)) {
14+
isValid = false
15+
return
16+
}
17+
18+
isValid = true
19+
20+
internalValue = valueString
21+
22+
value = durationToMilliseconds(valueString)
23+
onValueChange?.(value)
24+
}
25+
</script>
26+
27+
<input value={internalValue} on:change={onChange} {...$$restProps} />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script lang="ts">
2+
import { cn } from "$lib/utils"
3+
import DurationInput from "$lib/components/blocks/duration/duration-input.svelte"
4+
5+
export let readonly = false
6+
export let value: number
7+
</script>
8+
9+
<DurationInput
10+
bind:value
11+
disabled={readonly}
12+
{...$$restProps}
13+
class={cn(
14+
"border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50",
15+
$$restProps.class,
16+
)}
17+
/>

apps/frontend/src/lib/components/blocks/field-control/field-control.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import type { NoneSystemField, NoneSystemFieldType } from "@undb/table"
2+
import type { NoneSystemField, NoneSystemFieldType, RecordDO } from "@undb/table"
33
import StringControl from "./string-control.svelte"
44
import NumberControl from "./number-control.svelte"
55
import type { ComponentType } from "svelte"
@@ -17,6 +17,7 @@
1717
import LongTextControl from "./long-text-control.svelte"
1818
import CurrencyControl from "./currency-control.svelte"
1919
import ButtonControl from "./button-control.svelte"
20+
import DurationControl from "./duration-control.svelte"
2021
2122
export let readonly = false
2223
export let field: NoneSystemField
@@ -53,6 +54,7 @@
5354
json: JsonControl,
5455
checkbox: CheckboxControl,
5556
user: UserControl,
57+
duration: DurationControl,
5658
}
5759
</script>
5860

apps/frontend/src/lib/components/blocks/field-icon/field-icon.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
FolderIcon,
2727
DollarSignIcon,
2828
MousePointerClickIcon,
29+
TimerIcon,
2930
} from "lucide-svelte"
3031
3132
export let type: FieldType
@@ -54,6 +55,7 @@
5455
checkbox: SquareCheckIcon,
5556
user: field?.type === "user" && field.isMultiple ? UsersIcon : UserIcon,
5657
button: MousePointerClickIcon,
58+
duration: TimerIcon,
5759
}
5860
</script>
5961

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script lang="ts">
2+
import { Checkbox } from "$lib/components/ui/checkbox"
3+
import NumberInput from "$lib/components/ui/input/number-input.svelte"
4+
import { Label } from "$lib/components/ui/label/index.js"
5+
import { Separator } from "$lib/components/ui/separator"
6+
import { DurationFieldConstraint, type IDurationFieldConstraint } from "@undb/table"
7+
import * as Alert from "$lib/components/ui/alert/index.js"
8+
9+
export let constraint: IDurationFieldConstraint | undefined
10+
export let display: boolean | undefined
11+
export let defaultValue: number | undefined
12+
export let disabled = false
13+
14+
$: c = constraint ? new DurationFieldConstraint(constraint) : undefined
15+
$: isDefaultValueValid = c && defaultValue ? c.schema.safeParse(defaultValue).success : true
16+
</script>
17+
18+
<div class="space-y-2">
19+
<div class="space-y-1">
20+
<Label for="defaultValue" class="text-xs font-normal">Default value</Label>
21+
<NumberInput
22+
{disabled}
23+
id="defaultValue"
24+
class="bg-background flex-1 text-xs"
25+
placeholder="Default value..."
26+
bind:value={defaultValue}
27+
/>
28+
</div>
29+
30+
{#if !isDefaultValueValid}
31+
<Alert.Root class="border-yellow-500 bg-yellow-50">
32+
<Alert.Title>Invalid default value</Alert.Title>
33+
<Alert.Description>Your default value is invalid. Default value will not be saved.</Alert.Description>
34+
</Alert.Root>
35+
{/if}
36+
{#if constraint}
37+
<div class="grid grid-cols-2 gap-2">
38+
<div class="space-y-1">
39+
<Label for="min" class="text-xs font-normal">Min</Label>
40+
<NumberInput
41+
{disabled}
42+
id="min"
43+
min={0}
44+
max={constraint.max}
45+
step={1}
46+
bind:value={constraint.min}
47+
placeholder="Min value..."
48+
class="bg-background text-xs"
49+
/>
50+
</div>
51+
<div class="space-y-1">
52+
<Label for="max" class="text-xs font-normal">Max</Label>
53+
<NumberInput
54+
{disabled}
55+
id="max"
56+
min={constraint.min || 0}
57+
step={1}
58+
bind:value={constraint.max}
59+
placeholder="Max value..."
60+
class="bg-background text-xs"
61+
/>
62+
</div>
63+
</div>
64+
65+
<div class="pt-2">
66+
<Separator />
67+
</div>
68+
<div class="flex items-center space-x-2">
69+
<Checkbox id="required" {disabled} bind:checked={constraint.required} />
70+
<Label for="required" class="text-xs font-normal">Mark as required field.</Label>
71+
</div>
72+
73+
<div class="flex items-center space-x-2">
74+
<Checkbox id="display" {disabled} bind:checked={display} />
75+
<Label for="display" class="text-xs font-normal">Mark as display field.</Label>
76+
</div>
77+
{/if}
78+
</div>

apps/frontend/src/lib/components/blocks/field-options/field-options.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import LongTextFieldOption from "./long-text-field-option.svelte"
1818
import CurrencyFieldOption from "./currency-field-option.svelte"
1919
import ButtonFieldOption from "./button-field-option.svelte"
20+
import DurationFieldOption from "./duration-field-option.svelte"
2021
2122
export let constraint: IFieldConstraint | undefined
2223
export let option: any | undefined
@@ -43,6 +44,7 @@
4344
checkbox: CheckboxFieldOption,
4445
json: JsonFieldOption,
4546
date: DateFieldOption,
47+
duration: DurationFieldOption,
4648
}
4749
4850
export let type: NoneSystemFieldType
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
import { millisecondsToDuration } from "@undb/table"
3+
4+
export let value: number | undefined = undefined
5+
</script>
6+
7+
<span class={$$restProps.class}>{value ? millisecondsToDuration(value) : ""}</span>

apps/frontend/src/lib/components/blocks/field-value/field-value.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import UrlField from "./url-field.svelte"
1717
import LongTextField from "./long-text-field.svelte"
1818
import CurrencyField from "./currency-field.svelte"
19+
import DurationField from "./duration-field.svelte"
1920
2021
export let type: FieldType
2122
export let value: any
@@ -47,6 +48,7 @@
4748
json: JsonField,
4849
checkbox: CheckboxField,
4950
user: UserField,
51+
duration: DurationField,
5052
}
5153
</script>
5254

apps/frontend/src/lib/components/blocks/filters-editor/filter-input.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
type ICreatedByFieldConditionOp,
99
type ICurrencyFieldConditionOp,
1010
type IDateFieldConditionOp,
11+
type IDurationFieldConditionOp,
1112
type IEmailFieldConditionOp,
1213
type IIdFieldConditionOp,
1314
type IJsonFieldConditionOp,
@@ -185,6 +186,13 @@
185186
lte: NumberInput,
186187
}
187188
189+
const duration: Record<IDurationFieldConditionOp, ComponentType | null> = {
190+
eq: NumberInput,
191+
neq: NumberInput,
192+
is_empty: null,
193+
is_not_empty: null,
194+
}
195+
188196
const id: Record<IIdFieldConditionOp, ComponentType | null> = {
189197
eq: IdFilterInput,
190198
neq: IdFilterInput,
@@ -268,6 +276,7 @@
268276
json,
269277
url,
270278
longText,
279+
duration,
271280
}
272281
</script>
273282

0 commit comments

Comments
 (0)