@devappsnpm/vue-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bin/devapps-vue.js +3 -0
- package/dist/index.cjs +556 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +510 -0
- package/dist/index.js.map +1 -0
- package/dist/ui.cjs +515 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.js +485 -0
- package/dist/ui.js.map +1 -0
- package/package.json +57 -0
- package/stubs/components/component.vue.stub +17 -0
- package/stubs/module/basic/component.vue.stub +9 -0
- package/stubs/module/basic/index.ts.stub +1 -0
- package/stubs/module/crud/modal/components/DeleteModal.vue.stub +35 -0
- package/stubs/module/crud/modal/components/FormModal.vue.stub +88 -0
- package/stubs/module/crud/modal/components/List.vue.stub +60 -0
- package/stubs/module/crud/modal/index.ts.stub +4 -0
- package/stubs/module/crud/modal/router.ts.stub +11 -0
- package/stubs/module/crud/modal/services/service.ts.stub +9 -0
- package/stubs/module/crud/modal/stores/store.ts.stub +9 -0
- package/stubs/module/crud/modal/types/types.ts.stub +16 -0
- package/stubs/module/crud/modal/views/View.vue.stub +83 -0
- package/stubs/module/crud/page/components/DeleteModal.vue.stub +35 -0
- package/stubs/module/crud/page/components/Form.vue.stub +62 -0
- package/stubs/module/crud/page/components/List.vue.stub +42 -0
- package/stubs/module/crud/page/index.ts.stub +4 -0
- package/stubs/module/crud/page/pages/CreatePage.vue.stub +27 -0
- package/stubs/module/crud/page/pages/EditPage.vue.stub +34 -0
- package/stubs/module/crud/page/pages/ListPage.vue.stub +59 -0
- package/stubs/module/crud/page/pages/ShowPage.vue.stub +28 -0
- package/stubs/module/crud/page/router.ts.stub +26 -0
- package/stubs/module/crud/page/services/service.ts.stub +9 -0
- package/stubs/module/crud/page/stores/store.ts.stub +9 -0
- package/stubs/module/crud/page/types/types.ts.stub +16 -0
- package/stubs/module/dashboard/Dashboard.vue.stub +24 -0
- package/stubs/module/dashboard/index.ts.stub +1 -0
- package/stubs/module/dashboard/router.ts.stub +11 -0
- package/stubs/module/resource/List.vue.stub +19 -0
- package/stubs/module/resource/index.ts.stub +3 -0
- package/stubs/module/resource/service.ts.stub +9 -0
- package/stubs/module/resource/store.ts.stub +9 -0
- package/stubs/module/resource/types.ts.stub +10 -0
- package/stubs/pages/page.vue.stub +14 -0
- package/stubs/services/service.ts.stub +13 -0
- package/stubs/stores/store.ts.stub +19 -0
- package/stubs/types/types.ts.stub +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components/{{ ModuleName }}Component.vue'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ConfirmDeleteModal } from '@devapps/vue-kit/ui'
|
|
3
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
4
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
open: boolean
|
|
8
|
+
item: {{ ModuleName }} | null
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const emit = defineEmits<{
|
|
12
|
+
close: []
|
|
13
|
+
deleted: []
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const store = use{{ ModuleName }}Store()
|
|
17
|
+
|
|
18
|
+
async function handleConfirm() {
|
|
19
|
+
if (!props.item) return
|
|
20
|
+
await store.remove(props.item.uuid)
|
|
21
|
+
emit('deleted')
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<ConfirmDeleteModal
|
|
27
|
+
:open="open"
|
|
28
|
+
title="Delete {{ ModuleName }}"
|
|
29
|
+
description="This action cannot be undone. Please type the identifier to confirm."
|
|
30
|
+
:confirmation-label="item?.uuid ?? ''"
|
|
31
|
+
:loading="store.deleting"
|
|
32
|
+
@close="emit('close')"
|
|
33
|
+
@confirm="handleConfirm"
|
|
34
|
+
/>
|
|
35
|
+
</template>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive, watch } from 'vue'
|
|
3
|
+
import { BaseModal } from '@devapps/vue-kit/ui'
|
|
4
|
+
import { Form } from '@devapps/vue-kit'
|
|
5
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
6
|
+
import type { {{ ModuleName }}, {{ ModuleName }}Form } from '../types/{{ moduleSlug }}.types'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
open: boolean
|
|
10
|
+
item?: {{ ModuleName }} | null
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const emit = defineEmits<{
|
|
14
|
+
close: []
|
|
15
|
+
saved: []
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
const store = use{{ ModuleName }}Store()
|
|
19
|
+
|
|
20
|
+
const form = reactive(
|
|
21
|
+
new Form<{{ ModuleName }}Form>({
|
|
22
|
+
// TODO: initialise form fields
|
|
23
|
+
}),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
watch(
|
|
27
|
+
() => props.item,
|
|
28
|
+
(item) => {
|
|
29
|
+
if (item) {
|
|
30
|
+
// TODO: populate form from item
|
|
31
|
+
// Object.assign(form.data, { field: item.field })
|
|
32
|
+
} else {
|
|
33
|
+
form.reset()
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{ immediate: true },
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
async function handleSubmit() {
|
|
40
|
+
await form.submit(async (data) => {
|
|
41
|
+
if (props.item) {
|
|
42
|
+
await store.update(props.item.uuid, data)
|
|
43
|
+
} else {
|
|
44
|
+
await store.create(data)
|
|
45
|
+
}
|
|
46
|
+
emit('saved')
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<BaseModal
|
|
53
|
+
:open="open"
|
|
54
|
+
:loading="form.processing"
|
|
55
|
+
@close="emit('close')"
|
|
56
|
+
>
|
|
57
|
+
<template #header>
|
|
58
|
+
<h2 class="text-lg font-semibold">
|
|
59
|
+
{{ item ? 'Edit' : 'Create' }} {{ ModuleName }}
|
|
60
|
+
</h2>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<form id="{{ moduleSlug }}-form" class="space-y-4" @submit.prevent="handleSubmit">
|
|
64
|
+
<!-- TODO: add form fields -->
|
|
65
|
+
</form>
|
|
66
|
+
|
|
67
|
+
<template #footer>
|
|
68
|
+
<div class="flex justify-end gap-2">
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
class="px-4 py-2 rounded border text-sm"
|
|
72
|
+
:disabled="form.processing"
|
|
73
|
+
@click="emit('close')"
|
|
74
|
+
>
|
|
75
|
+
Cancel
|
|
76
|
+
</button>
|
|
77
|
+
<button
|
|
78
|
+
type="submit"
|
|
79
|
+
form="{{ moduleSlug }}-form"
|
|
80
|
+
class="px-4 py-2 rounded bg-blue-600 text-white text-sm disabled:opacity-50"
|
|
81
|
+
:disabled="form.processing"
|
|
82
|
+
>
|
|
83
|
+
{{ form.processing ? 'Saving...' : 'Save' }}
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
</BaseModal>
|
|
88
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
3
|
+
|
|
4
|
+
const emit = defineEmits<{
|
|
5
|
+
edit: [item: {{ ModuleName }}]
|
|
6
|
+
delete: [item: {{ ModuleName }}]
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
defineProps<{
|
|
10
|
+
items: {{ ModuleName }}[]
|
|
11
|
+
loading?: boolean
|
|
12
|
+
}>()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="{{ moduleSlug }}-list">
|
|
17
|
+
<div v-if="loading" class="flex justify-center py-8">
|
|
18
|
+
<span class="text-gray-500">Loading...</span>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<table v-else class="w-full text-sm">
|
|
22
|
+
<thead>
|
|
23
|
+
<tr class="border-b text-left text-gray-500">
|
|
24
|
+
<th class="pb-2">UUID</th>
|
|
25
|
+
<!-- TODO: add columns -->
|
|
26
|
+
<th class="pb-2 text-right">Actions</th>
|
|
27
|
+
</tr>
|
|
28
|
+
</thead>
|
|
29
|
+
<tbody>
|
|
30
|
+
<tr
|
|
31
|
+
v-for="item in items"
|
|
32
|
+
:key="item.uuid"
|
|
33
|
+
class="border-b hover:bg-gray-50"
|
|
34
|
+
>
|
|
35
|
+
<td class="py-2 font-mono text-xs">{{ item.uuid }}</td>
|
|
36
|
+
<!-- TODO: add cells -->
|
|
37
|
+
<td class="py-2 text-right space-x-2">
|
|
38
|
+
<button
|
|
39
|
+
class="text-blue-600 hover:underline text-xs"
|
|
40
|
+
@click="emit('edit', item)"
|
|
41
|
+
>
|
|
42
|
+
Edit
|
|
43
|
+
</button>
|
|
44
|
+
<button
|
|
45
|
+
class="text-red-600 hover:underline text-xs"
|
|
46
|
+
@click="emit('delete', item)"
|
|
47
|
+
>
|
|
48
|
+
Delete
|
|
49
|
+
</button>
|
|
50
|
+
</td>
|
|
51
|
+
</tr>
|
|
52
|
+
<tr v-if="items.length === 0">
|
|
53
|
+
<td colspan="3" class="py-8 text-center text-gray-400">
|
|
54
|
+
No records found.
|
|
55
|
+
</td>
|
|
56
|
+
</tr>
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
const routes: RouteRecordRaw[] = [
|
|
4
|
+
{
|
|
5
|
+
path: '/{{ moduleSlugPlural }}',
|
|
6
|
+
name: '{{ moduleSlugPlural }}.index',
|
|
7
|
+
component: () => import('./views/{{ ModuleName }}View.vue'),
|
|
8
|
+
},
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
export default routes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CrudService } from '@devapps/vue-kit'
|
|
2
|
+
import type { HttpClient } from '@devapps/vue-kit'
|
|
3
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
4
|
+
|
|
5
|
+
export class {{ ModuleName }}Service extends CrudService<{{ ModuleName }}> {
|
|
6
|
+
constructor(http: HttpClient) {
|
|
7
|
+
super(http, '{{ resourcePath }}')
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineCrudStore } from '@devapps/vue-kit'
|
|
2
|
+
import { {{ ModuleName }}Service } from '../services/{{ moduleSlug }}.service'
|
|
3
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
4
|
+
import { useHttpClient } from '@/composables/useHttpClient' // adjust to your project
|
|
5
|
+
|
|
6
|
+
export const use{{ ModuleName }}Store = defineCrudStore<{{ ModuleName }}>(
|
|
7
|
+
'{{ moduleName }}',
|
|
8
|
+
() => new {{ ModuleName }}Service(useHttpClient()),
|
|
9
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface {{ ModuleName }} {
|
|
2
|
+
uuid: string
|
|
3
|
+
// TODO: add fields
|
|
4
|
+
created_at: string
|
|
5
|
+
updated_at: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface {{ ModuleName }}Form {
|
|
9
|
+
// TODO: add form fields
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface {{ ModuleName }}Filters {
|
|
13
|
+
page?: number
|
|
14
|
+
per_page?: number
|
|
15
|
+
search?: string
|
|
16
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, onMounted } from 'vue'
|
|
3
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
4
|
+
import {{ ModuleName }}List from '../components/{{ ModuleName }}List.vue'
|
|
5
|
+
import {{ ModuleName }}FormModal from '../components/{{ ModuleName }}FormModal.vue'
|
|
6
|
+
import {{ ModuleName }}DeleteModal from '../components/{{ ModuleName }}DeleteModal.vue'
|
|
7
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
8
|
+
|
|
9
|
+
const store = use{{ ModuleName }}Store()
|
|
10
|
+
|
|
11
|
+
const isFormModalOpen = ref(false)
|
|
12
|
+
const isDeleteModalOpen = ref(false)
|
|
13
|
+
const selected{{ ModuleName }} = ref<{{ ModuleName }} | null>(null)
|
|
14
|
+
const {{ moduleName }}ToDelete = ref<{{ ModuleName }} | null>(null)
|
|
15
|
+
|
|
16
|
+
onMounted(() => loadItems())
|
|
17
|
+
|
|
18
|
+
async function loadItems(page = store.pagination?.currentPage ?? 1) {
|
|
19
|
+
await store.fetchAll({ page })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function handleCreate() {
|
|
23
|
+
selected{{ ModuleName }}.value = null
|
|
24
|
+
isFormModalOpen.value = true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function handleEdit(item: {{ ModuleName }}) {
|
|
28
|
+
await store.fetchOne(item.uuid)
|
|
29
|
+
selected{{ ModuleName }}.value = store.item
|
|
30
|
+
isFormModalOpen.value = true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function handleDelete(item: {{ ModuleName }}) {
|
|
34
|
+
{{ moduleName }}ToDelete.value = item
|
|
35
|
+
isDeleteModalOpen.value = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function handleFormSaved() {
|
|
39
|
+
isFormModalOpen.value = false
|
|
40
|
+
loadItems()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleDeleted() {
|
|
44
|
+
isDeleteModalOpen.value = false
|
|
45
|
+
{{ moduleName }}ToDelete.value = null
|
|
46
|
+
loadItems()
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<div class="{{ moduleSlug }}-view">
|
|
52
|
+
<div class="flex items-center justify-between mb-4">
|
|
53
|
+
<h1 class="text-xl font-semibold">{{ ModuleNamePlural }}</h1>
|
|
54
|
+
<button
|
|
55
|
+
class="px-4 py-2 bg-blue-600 text-white rounded text-sm"
|
|
56
|
+
@click="handleCreate"
|
|
57
|
+
>
|
|
58
|
+
New {{ ModuleName }}
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<{{ ModuleName }}List
|
|
63
|
+
:items="store.items"
|
|
64
|
+
:loading="store.loading"
|
|
65
|
+
@edit="handleEdit"
|
|
66
|
+
@delete="handleDelete"
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<{{ ModuleName }}FormModal
|
|
70
|
+
:open="isFormModalOpen"
|
|
71
|
+
:item="selected{{ ModuleName }}"
|
|
72
|
+
@close="isFormModalOpen = false"
|
|
73
|
+
@saved="handleFormSaved"
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
<{{ ModuleName }}DeleteModal
|
|
77
|
+
:open="isDeleteModalOpen"
|
|
78
|
+
:item="{{ moduleName }}ToDelete"
|
|
79
|
+
@close="isDeleteModalOpen = false"
|
|
80
|
+
@deleted="handleDeleted"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ConfirmDeleteModal } from '@devapps/vue-kit/ui'
|
|
3
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
4
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
open: boolean
|
|
8
|
+
item: {{ ModuleName }} | null
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const emit = defineEmits<{
|
|
12
|
+
close: []
|
|
13
|
+
deleted: []
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const store = use{{ ModuleName }}Store()
|
|
17
|
+
|
|
18
|
+
async function handleConfirm() {
|
|
19
|
+
if (!props.item) return
|
|
20
|
+
await store.remove(props.item.uuid)
|
|
21
|
+
emit('deleted')
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<ConfirmDeleteModal
|
|
27
|
+
:open="open"
|
|
28
|
+
title="Delete {{ ModuleName }}"
|
|
29
|
+
description="This action cannot be undone. Please type the identifier to confirm."
|
|
30
|
+
:confirmation-label="item?.uuid ?? ''"
|
|
31
|
+
:loading="store.deleting"
|
|
32
|
+
@close="emit('close')"
|
|
33
|
+
@confirm="handleConfirm"
|
|
34
|
+
/>
|
|
35
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive, watch } from 'vue'
|
|
3
|
+
import { Form } from '@devapps/vue-kit'
|
|
4
|
+
import type { {{ ModuleName }}, {{ ModuleName }}Form } from '../types/{{ moduleSlug }}.types'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
item?: {{ ModuleName }} | null
|
|
8
|
+
loading?: boolean
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const emit = defineEmits<{
|
|
12
|
+
submit: [data: {{ ModuleName }}Form]
|
|
13
|
+
cancel: []
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
const form = reactive(
|
|
17
|
+
new Form<{{ ModuleName }}Form>({
|
|
18
|
+
// TODO: initialise form fields
|
|
19
|
+
}),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
watch(
|
|
23
|
+
() => props.item,
|
|
24
|
+
(item) => {
|
|
25
|
+
if (item) {
|
|
26
|
+
// TODO: populate form from item
|
|
27
|
+
// Object.assign(form.data, { field: item.field })
|
|
28
|
+
} else {
|
|
29
|
+
form.reset()
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{ immediate: true },
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
function handleSubmit() {
|
|
36
|
+
emit('submit', form.data)
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<form class="{{ moduleSlug }}-form space-y-4" @submit.prevent="handleSubmit">
|
|
42
|
+
<!-- TODO: add form fields -->
|
|
43
|
+
|
|
44
|
+
<div class="flex justify-end gap-2">
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
class="px-4 py-2 rounded border text-sm"
|
|
48
|
+
:disabled="loading"
|
|
49
|
+
@click="emit('cancel')"
|
|
50
|
+
>
|
|
51
|
+
Cancel
|
|
52
|
+
</button>
|
|
53
|
+
<button
|
|
54
|
+
type="submit"
|
|
55
|
+
class="px-4 py-2 rounded bg-blue-600 text-white text-sm disabled:opacity-50"
|
|
56
|
+
:disabled="loading"
|
|
57
|
+
>
|
|
58
|
+
{{ loading ? 'Saving...' : 'Save' }}
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
</form>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
items: {{ ModuleName }}[]
|
|
6
|
+
loading?: boolean
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const emit = defineEmits<{
|
|
10
|
+
edit: [item: {{ ModuleName }}]
|
|
11
|
+
delete: [item: {{ ModuleName }}]
|
|
12
|
+
}>()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="{{ moduleSlug }}-list">
|
|
17
|
+
<div v-if="loading" class="py-8 text-center text-gray-500">Loading...</div>
|
|
18
|
+
|
|
19
|
+
<table v-else class="w-full text-sm">
|
|
20
|
+
<thead>
|
|
21
|
+
<tr class="border-b text-left text-gray-500">
|
|
22
|
+
<th class="pb-2">UUID</th>
|
|
23
|
+
<!-- TODO: add columns -->
|
|
24
|
+
<th class="pb-2 text-right">Actions</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
<tr v-for="item in items" :key="item.uuid" class="border-b hover:bg-gray-50">
|
|
29
|
+
<td class="py-2 font-mono text-xs">{{ item.uuid }}</td>
|
|
30
|
+
<!-- TODO: add cells -->
|
|
31
|
+
<td class="py-2 text-right space-x-2">
|
|
32
|
+
<button class="text-blue-600 hover:underline text-xs" @click="emit('edit', item)">Edit</button>
|
|
33
|
+
<button class="text-red-600 hover:underline text-xs" @click="emit('delete', item)">Delete</button>
|
|
34
|
+
</td>
|
|
35
|
+
</tr>
|
|
36
|
+
<tr v-if="items.length === 0">
|
|
37
|
+
<td colspan="3" class="py-8 text-center text-gray-400">No records found.</td>
|
|
38
|
+
</tr>
|
|
39
|
+
</tbody>
|
|
40
|
+
</table>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import { useRouter } from 'vue-router'
|
|
4
|
+
import { Form } from '@devapps/vue-kit'
|
|
5
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
6
|
+
import {{ ModuleName }}Form from '../components/{{ ModuleName }}Form.vue'
|
|
7
|
+
import type { {{ ModuleName }}Form as {{ ModuleName }}FormData } from '../types/{{ moduleSlug }}.types'
|
|
8
|
+
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const store = use{{ ModuleName }}Store()
|
|
11
|
+
|
|
12
|
+
async function handleSubmit(data: {{ ModuleName }}FormData) {
|
|
13
|
+
await store.create(data)
|
|
14
|
+
router.push({ name: '{{ moduleSlugPlural }}.index' })
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div class="{{ moduleSlug }}-create-page">
|
|
20
|
+
<h1 class="text-xl font-semibold mb-4">New {{ ModuleName }}</h1>
|
|
21
|
+
<{{ ModuleName }}Form
|
|
22
|
+
:loading="store.saving"
|
|
23
|
+
@submit="handleSubmit"
|
|
24
|
+
@cancel="router.push({ name: '{{ moduleSlugPlural }}.index' })"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted } from 'vue'
|
|
3
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
4
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
5
|
+
import {{ ModuleName }}Form from '../components/{{ ModuleName }}Form.vue'
|
|
6
|
+
import type { {{ ModuleName }}Form as {{ ModuleName }}FormData } from '../types/{{ moduleSlug }}.types'
|
|
7
|
+
|
|
8
|
+
const route = useRoute()
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const store = use{{ ModuleName }}Store()
|
|
11
|
+
|
|
12
|
+
const uuid = route.params['uuid'] as string
|
|
13
|
+
|
|
14
|
+
onMounted(() => store.fetchOne(uuid))
|
|
15
|
+
|
|
16
|
+
async function handleSubmit(data: {{ ModuleName }}FormData) {
|
|
17
|
+
await store.update(uuid, data)
|
|
18
|
+
router.push({ name: '{{ moduleSlugPlural }}.index' })
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div class="{{ moduleSlug }}-edit-page">
|
|
24
|
+
<h1 class="text-xl font-semibold mb-4">Edit {{ ModuleName }}</h1>
|
|
25
|
+
<div v-if="store.loading">Loading...</div>
|
|
26
|
+
<{{ ModuleName }}Form
|
|
27
|
+
v-else
|
|
28
|
+
:item="store.item"
|
|
29
|
+
:loading="store.saving"
|
|
30
|
+
@submit="handleSubmit"
|
|
31
|
+
@cancel="router.push({ name: '{{ moduleSlugPlural }}.index' })"
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, onMounted } from 'vue'
|
|
3
|
+
import { useRouter } from 'vue-router'
|
|
4
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
5
|
+
import {{ ModuleName }}List from '../components/{{ ModuleName }}List.vue'
|
|
6
|
+
import {{ ModuleName }}DeleteModal from '../components/{{ ModuleName }}DeleteModal.vue'
|
|
7
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
8
|
+
|
|
9
|
+
const router = useRouter()
|
|
10
|
+
const store = use{{ ModuleName }}Store()
|
|
11
|
+
|
|
12
|
+
const isDeleteModalOpen = ref(false)
|
|
13
|
+
const {{ moduleName }}ToDelete = ref<{{ ModuleName }} | null>(null)
|
|
14
|
+
|
|
15
|
+
onMounted(() => store.fetchAll())
|
|
16
|
+
|
|
17
|
+
function handleEdit(item: {{ ModuleName }}) {
|
|
18
|
+
router.push({ name: '{{ moduleSlugPlural }}.edit', params: { uuid: item.uuid } })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function handleDelete(item: {{ ModuleName }}) {
|
|
22
|
+
{{ moduleName }}ToDelete.value = item
|
|
23
|
+
isDeleteModalOpen.value = true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function handleDeleted() {
|
|
27
|
+
isDeleteModalOpen.value = false
|
|
28
|
+
{{ moduleName }}ToDelete.value = null
|
|
29
|
+
store.fetchAll()
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div class="{{ moduleSlug }}-list-page">
|
|
35
|
+
<div class="flex items-center justify-between mb-4">
|
|
36
|
+
<h1 class="text-xl font-semibold">{{ ModuleNamePlural }}</h1>
|
|
37
|
+
<button
|
|
38
|
+
class="px-4 py-2 bg-blue-600 text-white rounded text-sm"
|
|
39
|
+
@click="router.push({ name: '{{ moduleSlugPlural }}.create' })"
|
|
40
|
+
>
|
|
41
|
+
New {{ ModuleName }}
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<{{ ModuleName }}List
|
|
46
|
+
:items="store.items"
|
|
47
|
+
:loading="store.loading"
|
|
48
|
+
@edit="handleEdit"
|
|
49
|
+
@delete="handleDelete"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<{{ ModuleName }}DeleteModal
|
|
53
|
+
:open="isDeleteModalOpen"
|
|
54
|
+
:item="{{ moduleName }}ToDelete"
|
|
55
|
+
@close="isDeleteModalOpen = false"
|
|
56
|
+
@deleted="handleDeleted"
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted } from 'vue'
|
|
3
|
+
import { useRoute } from 'vue-router'
|
|
4
|
+
import { use{{ ModuleName }}Store } from '../stores/{{ moduleSlug }}.store'
|
|
5
|
+
|
|
6
|
+
const route = useRoute()
|
|
7
|
+
const store = use{{ ModuleName }}Store()
|
|
8
|
+
|
|
9
|
+
const uuid = route.params['uuid'] as string
|
|
10
|
+
|
|
11
|
+
onMounted(() => store.fetchOne(uuid))
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div class="{{ moduleSlug }}-show-page">
|
|
16
|
+
<h1 class="text-xl font-semibold mb-4">{{ ModuleName }} Details</h1>
|
|
17
|
+
|
|
18
|
+
<div v-if="store.loading" class="py-8 text-gray-500">Loading...</div>
|
|
19
|
+
|
|
20
|
+
<div v-else-if="store.item" class="bg-white p-6 rounded shadow space-y-4">
|
|
21
|
+
<div>
|
|
22
|
+
<span class="text-sm text-gray-500">UUID</span>
|
|
23
|
+
<p class="font-mono">{{ store.item.uuid }}</p>
|
|
24
|
+
</div>
|
|
25
|
+
<!-- TODO: show fields -->
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
const routes: RouteRecordRaw[] = [
|
|
4
|
+
{
|
|
5
|
+
path: '/{{ moduleSlugPlural }}',
|
|
6
|
+
name: '{{ moduleSlugPlural }}.index',
|
|
7
|
+
component: () => import('./pages/{{ ModuleName }}ListPage.vue'),
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
path: '/{{ moduleSlugPlural }}/create',
|
|
11
|
+
name: '{{ moduleSlugPlural }}.create',
|
|
12
|
+
component: () => import('./pages/{{ ModuleName }}CreatePage.vue'),
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: '/{{ moduleSlugPlural }}/:uuid/edit',
|
|
16
|
+
name: '{{ moduleSlugPlural }}.edit',
|
|
17
|
+
component: () => import('./pages/{{ ModuleName }}EditPage.vue'),
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
path: '/{{ moduleSlugPlural }}/:uuid',
|
|
21
|
+
name: '{{ moduleSlugPlural }}.show',
|
|
22
|
+
component: () => import('./pages/{{ ModuleName }}ShowPage.vue'),
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
export default routes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CrudService } from '@devapps/vue-kit'
|
|
2
|
+
import type { HttpClient } from '@devapps/vue-kit'
|
|
3
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
4
|
+
|
|
5
|
+
export class {{ ModuleName }}Service extends CrudService<{{ ModuleName }}> {
|
|
6
|
+
constructor(http: HttpClient) {
|
|
7
|
+
super(http, '{{ resourcePath }}')
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineCrudStore } from '@devapps/vue-kit'
|
|
2
|
+
import { {{ ModuleName }}Service } from '../services/{{ moduleSlug }}.service'
|
|
3
|
+
import type { {{ ModuleName }} } from '../types/{{ moduleSlug }}.types'
|
|
4
|
+
import { useHttpClient } from '@/composables/useHttpClient' // adjust to your project
|
|
5
|
+
|
|
6
|
+
export const use{{ ModuleName }}Store = defineCrudStore<{{ ModuleName }}>(
|
|
7
|
+
'{{ moduleName }}',
|
|
8
|
+
() => new {{ ModuleName }}Service(useHttpClient()),
|
|
9
|
+
)
|