Skip to content

Commit

Permalink
feat: refactor form system and setup validation
Browse files Browse the repository at this point in the history
  • Loading branch information
kerwanp committed Oct 17, 2024
1 parent c18ed8f commit 23896e6
Show file tree
Hide file tree
Showing 60 changed files with 649 additions and 449 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"tailwindcss-primeui": "^0.3.4",
"ts-morph": "^23.0.0",
"unplugin-vue-components": "^0.27.4",
"vee-validate": "^4.13.2",
"vue": "^3.5.10"
}
}
35 changes: 22 additions & 13 deletions resources/components/fields/belongs_to/form.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,52 @@
<script setup lang="ts">
import type { InferSerializable } from '../../../../src/types'
import type BelongsTo from '../../../../src/fields/belongs_to'
import { ref } from 'vue'
import { computed, ref } from 'vue'
import { useResourceApi } from '../../../composables/resource'
import Select from 'primevue/select'
import { useField } from '../../../composables/field'
import InputText from 'primevue/inputtext'
import { useFormValues } from 'vee-validate'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
error?: string[]
field: InferSerializable<BelongsTo>
record: any
}>()
const filter = ref('')
const model = defineModel()
const { data, isLoading } = useResourceApi.list(props.field.resource.slug, { search: filter })
const record = useFormValues()
const { field, name, value, errorMessage, handleBlur } = useField<BelongsTo>()
const { data, isLoading } = useResourceApi.list(field.resource.slug, { search: filter })
const options = computed(() => {
// TODO: This might not be really performant
const options = data.value ? [...data.value.data] : []
if (!options.some((r) => r[field.resource.idKey] === value.value)) {
options.unshift(record.value[field.relationship.relationName])
}
return options
})
</script>

<template>
<div class="flex flex-col gap-2">
<Select
v-model="model"
:options="data?.data ?? []"
:id="name"
:name="name"
v-model="value"
:options="options"
:loading="isLoading"
:option-label="field.resource.titleKey"
:option-value="field.resource.idKey"
:placeholder="`Select ${field.resource.name}`"
v-bind="field.attributes"
@blur="handleBlur"
>
<template #header>
<div class="p-2">
<InputText class="w-full" v-model="filter" />
</div>
</template>
</Select>
<small class="text-red-400" v-if="error">{{ error.join('\n') }}</small>
<errorMessage />
</div>
</template>
5 changes: 3 additions & 2 deletions resources/components/fields/belongs_to/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import { useResourceQuery } from '../../../composables/resource'
import { injectResources } from '../../../composables/resources'
import Popover from 'primevue/popover'
import Button from 'primevue/button'
import { ResourceRecord } from '../../../types'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
field: InferSerializable<BelongsTo>
record: ResourceRecord
value: any
record: any
}>()
const opened = ref(false)
Expand All @@ -36,6 +37,6 @@ const label = relation[props.field.resource.titleKey] ?? props.value
<template>
<Button @click="toggle" size="small" text :label="label" severity="info" />
<Popover ref="popover" class="px-2">
<ResourcePeek v-if="data" :resource="resources[field.resource.name]" :data="data" />
<ResourcePeek v-if="data" :resource="resources[field.resource.name]" :record="data" />
</Popover>
</template>
23 changes: 10 additions & 13 deletions resources/components/fields/boolean/form.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
<script setup lang="ts">
import ToggleSwitch from 'primevue/toggleswitch'
import type Boolean from '../../../../src/fields/boolean'
import type { InferSerializable } from '../../../../src/types'
import ToggleSwitch from 'primevue/toggleswitch'
import FormMessage from '../../form/form-message.vue'
import { useField } from '../../../composables/field'
defineOptions({
inheritAttrs: false,
})
defineProps<{
error?: string[]
field: InferSerializable<Boolean>
record: any
}>()
const model = defineModel<string | boolean | undefined>()
const { field, name, value, errorMessage, handleBlur } = useField<Boolean>()
</script>

<template>
<div class="flex flex-col gap-2">
<ToggleSwitch
v-model="model"
:id="name"
:input-id="field.name"
:true-value="field.trueValue"
:false-value="field.falseValue"
:input-id="field.name"
:invalid="!!error?.length"
:invalid="!!errorMessage"
v-model="value"
@blur="handleBlur"
/>
<small class="text-red-400" v-if="error">{{ error.join('\n') }}</small>
<FormMessage />
</div>
</template>
38 changes: 38 additions & 0 deletions resources/components/fields/has_many/detail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup lang="ts">
import type { ResourceRecord, InferSerializable } from '../../../types'
import type HasMany from '../../../../src/fields/has_many'
import ResourceTable from '../../resource-table.vue'
import Heading from '../../ui/heading.vue'
import ProvideResource from '../../resource/provide-resource.vue'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
field: InferSerializable<HasMany>
record: ResourceRecord
}>()
const resource = props.field.resource
</script>

<template>
<ProvideResource :resource="resource">
<div class="flex flex-col gap-2">
<ResourceTable
:resource="field.resource"
:additional-filters="[
{ field: field.relationship.foreignKey, value: record[resource.idKey] },
]"
:create-params="{
initialData: { [field.relationship.foreignKey]: record[resource.idKey] },
}"
>
<template #title>
<Heading variant="h2">{{ field.resource.labelPlural }}</Heading>
</template>
</ResourceTable>
</div>
</ProvideResource>
</template>
17 changes: 6 additions & 11 deletions resources/components/fields/has_many/form.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
<script setup lang="ts">
import type { InferSerializable } from '../../../../src/types'
import type { BaseResource } from '../../../../src/resources/base_resource'
import type HasMany from '../../../../src/fields/has_many'
import ResourceTable from '../../resource-table.vue'
import { provideResource } from '../../../composables/resource'
import Heading from '../../ui/heading.vue'
import { useResource } from '../../../composables/resource'
import { useField } from '../../../composables/field'
import { useFormValues } from 'vee-validate'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
error?: string[]
resource: InferSerializable<BaseResource>
field: InferSerializable<HasMany>
record: any
}>()
provideResource(props.field.resource)
const resource = useResource()
const record = useFormValues()
const { field } = useField<HasMany>()
</script>

<template>
Expand Down
2 changes: 2 additions & 0 deletions resources/components/fields/has_many/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Detail from './detail.vue'
import Form from './form.vue'
import Index from './index.vue'

export default {
Index,
Form,
Detail,
}
2 changes: 1 addition & 1 deletion resources/components/fields/has_many/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ function toggleMore(event: Event) {
</div>
</Popover>
<Popover ref="peekPopover" class="px-2">
<ResourcePeek v-if="data" :resource="resources[field.resource.name]" :data="data" />
<ResourcePeek v-if="data" :resource="resources[field.resource.name]" :record="data" />
</Popover>
</template>
23 changes: 12 additions & 11 deletions resources/components/fields/multi-select/form.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
<script setup lang="ts">
import type MultiSelectField from '../../../../src/fields/multi_select'
import type { InferSerializable } from '../../../../src/types'
import MultiSelect from 'primevue/multiselect'
import FormMessage from '../../form/form-message.vue'
import { useField } from '../../../composables/field'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
error?: string[]
field: InferSerializable<MultiSelectField>
record: any
}>()
const { field, name, value, errorMessage, handleBlur } = useField<MultiSelectField>()
const options = Object.entries(props.field.options).map(([value, label]) => ({
const options = Object.entries(field.options).map(([value, label]) => ({
label,
value,
}))
console.log(value)
</script>

<template>
<div class="flex flex-col gap-2">
<MultiSelect
v-model="record[field.name]"
:name="field.name"
:input-id="field.name"
v-model="value"
:name="name"
:input-id="name"
display="chip"
option-label="label"
option-value="value"
:options="options"
:invalid="Boolean(errorMessage)"
@blur="handleBlur"
/>
<small class="text-red-400" v-if="error">{{ error.join('\n') }}</small>
<FormMessage />
</div>
</template>
22 changes: 10 additions & 12 deletions resources/components/fields/password/form.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
<script setup lang="ts">
import type PasswordField from '../../../../src/fields/password'
import Password from 'primevue/password'
import type Text from '../../../../src/fields/text'
import type { InferSerializable } from '../../../../src/types'
import FormMessage from '../../form/form-message.vue'
import { useField } from '../../../composables/field'
defineOptions({
inheritAttrs: false,
})
defineProps<{
error?: string[]
field: InferSerializable<Text>
record: any
}>()
const { field, value, name, handleBlur, errorMessage } = useField<PasswordField>()
</script>

<template>
<div class="flex flex-col gap-2">
<Password
:id="field.name"
:name="field.name"
:invalid="!!error?.length"
v-model="record[field.name]"
:id="name"
:name="name"
:invalid="Boolean(errorMessage)"
v-model="value"
v-bind="field.attributes"
@blur="handleBlur"
/>
<small class="text-red-400" v-if="error">{{ error.join('\n') }}</small>
<FormMessage />
</div>
</template>
24 changes: 12 additions & 12 deletions resources/components/fields/select/form.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
<script setup lang="ts">
import Select from 'primevue/select'
import type SelectField from '../../../../src/fields/select'
import type { InferSerializable } from '../../../../src/types'
import Select from 'primevue/select'
import FormMessage from '../../form/form-message.vue'
import { useField } from '../../../composables/field'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
error?: string[]
field: InferSerializable<SelectField>
record: any
}>()
const { field, value, name, errorMessage, handleBlur } = useField<SelectField>()
const options = Object.entries(props.field.options).map(([value, label]) => ({
const options = Object.entries(field.options).map(([value, label]) => ({
label,
value,
}))
Expand All @@ -22,13 +19,16 @@ const options = Object.entries(props.field.options).map(([value, label]) => ({
<template>
<div class="flex flex-col gap-2">
<Select
v-model="record[field.name]"
:name="field.name"
:id="name"
:name="name"
:input-id="field.name"
:options="options"
:invalid="errorMessage"
option-label="label"
option-value="value"
:options="options"
v-model="value"
@blur="handleBlur"
/>
<small class="text-red-400" v-if="error">{{ error.join('\n') }}</small>
<FormMessage />
</div>
</template>
Loading

0 comments on commit 23896e6

Please sign in to comment.