Skip to content

Commit

Permalink
form, validation, relationships and authorization (#20)
Browse files Browse the repository at this point in the history
* feat: refactor form system and setup validation

* test: remove legacy tests

* chore: changeset

* feat: manage relationships

* refactor: rename cockpit variables to admin

* feat: authentication system

* feat: always show logout button

* fix: fix useForm type issue
  • Loading branch information
kerwanp authored Oct 17, 2024
1 parent b16f91e commit bbb98ab
Show file tree
Hide file tree
Showing 82 changed files with 950 additions and 860 deletions.
5 changes: 5 additions & 0 deletions .changeset/brown-chicken-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'adonis-cockpit': patch
---

Rename admin variables to cockpit
5 changes: 5 additions & 0 deletions .changeset/cold-lions-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'adonis-cockpit': minor
---

Frontend relationship management
5 changes: 5 additions & 0 deletions .changeset/curvy-jeans-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'adonis-cockpit': minor
---

Authentication system
5 changes: 5 additions & 0 deletions .changeset/quiet-clocks-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'adonis-cockpit': minor
---

Implementation of the validation system
5 changes: 5 additions & 0 deletions .changeset/red-papayas-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'adonis-cockpit': patch
---

Refactoring of the frontend code
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"license": "MIT",
"devDependencies": {
"@adonisjs/assembler": "^7.7.0",
"@adonisjs/auth": "^9.2.3",
"@adonisjs/bouncer": "^3.1.3",
"@adonisjs/core": "^6.12.0",
"@adonisjs/eslint-config": "^2.0.0-beta.7",
Expand Down Expand Up @@ -128,6 +129,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"
}
}
46 changes: 32 additions & 14 deletions resources/components/fields/belongs_to/form.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,61 @@
<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, toValue } 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'
import { useSearchParams } from '../../../composables/route'
import { ViaRelationship } from '../../../types'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{
error?: string[]
field: InferSerializable<BelongsTo>
record: any
}>()
const filter = ref('')
const model = defineModel()
const params = useSearchParams<{ via?: ViaRelationship }>()
const record = useFormValues()
const { field, name, value, errorMessage, setValue, 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
})
const isVia = params.via?.foreignKey === toValue(name)
const { data, isLoading } = useResourceApi.list(props.field.resource.slug, { search: filter })
if (isVia) {
setValue(params.via?.value)
}
</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}`"
:placeholder="`Select ${field.resource.label}`"
:disabled="isVia"
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>
15 changes: 9 additions & 6 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 @@ -30,12 +31,14 @@ function toggle(event: Event) {
}
const relation = props.record[props.field.relationship.relationName]
const label = relation[props.field.resource.titleKey] ?? props.value
const label = relation ? (relation[props.field.resource.titleKey] ?? props.value) : ''
</script>

<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" />
</Popover>
<div v-if="value">
<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]" :record="data" />
</Popover>
</div>
</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>
40 changes: 40 additions & 0 deletions resources/components/fields/has_many/detail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<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] },
]"
:via="{
resource: resource.name,
foreignKey: field.relationship.foreignKey,
value: record[resource.idKey],
}"
>
<template #title>
<Heading variant="h2">{{ field.resource.labelPlural }}</Heading>
</template>
</ResourceTable>
</div>
</ProvideResource>
</template>
49 changes: 25 additions & 24 deletions resources/components/fields/has_many/form.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
<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'
import ProvideResource from '../../resource/provide-resource.vue'
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>
<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 :resource="field.resource">
<div class="flex flex-col gap-2">
<ResourceTable
:additional-filters="[
{ field: field.relationship.foreignKey, value: record[resource.idKey] },
]"
:via="{
resource: resource.name,
foreignKey: field.relationship.foreignKey,
value: record[resource.idKey],
}"
>
<template #title>
<Heading variant="h2">{{ field.resource.labelPlural }}</Heading>
</template>
</ResourceTable>
</div>
</ProvideResource>
</template>
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>
Loading

0 comments on commit bbb98ab

Please sign in to comment.