@meeovi/layer-lists 1.0.2
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/app/components/features/archived.vue +64 -0
- package/app/components/features/bookmarks.vue +64 -0
- package/app/components/features/lists.vue +61 -0
- package/app/components/features/starred.vue +64 -0
- package/app/components/lists/ListItemCard.vue +190 -0
- package/app/components/lists/add-bookmark.vue +52 -0
- package/app/components/lists/add-list-item.vue +88 -0
- package/app/components/lists/add-list.vue +57 -0
- package/app/components/lists/lists.vue +6 -0
- package/app/components/lists/listsettings.vue +145 -0
- package/app/components/lists/update-bookmark.vue +267 -0
- package/app/components/lists/update-list.vue +192 -0
- package/app/components/media/MediaPlayer.vue +302 -0
- package/app/components/partials/addtolist.vue +233 -0
- package/app/components/partials/createListBtn.vue +95 -0
- package/app/components/partials/listBtn.vue +35 -0
- package/app/components/related/list.vue +33 -0
- package/app/components/related/relatedlists.vue +43 -0
- package/app/components/tasks/TaskItem.vue +204 -0
- package/app/composables/bookmarks/createBookmark.js +30 -0
- package/app/composables/bookmarks/deleteBookmark.js +15 -0
- package/app/composables/bookmarks/updateBookmark.js +15 -0
- package/app/composables/config.ts +17 -0
- package/app/composables/content/uploadFiles.js +41 -0
- package/app/composables/globals/useDirectusForm.ts +1 -0
- package/app/composables/lists/createList.js +25 -0
- package/app/composables/lists/deleteList.js +14 -0
- package/app/composables/lists/updateList.js +20 -0
- package/app/composables/lists/useBookmarks.js +69 -0
- package/app/composables/lists/useLists.js +120 -0
- package/app/composables/lists/usePlaylist.js +64 -0
- package/app/composables/lists/useSaved.js +29 -0
- package/app/composables/lists/useTasks.js +86 -0
- package/app/composables/lists/useWishlist.js +51 -0
- package/app/composables/providers/atproto.ts +156 -0
- package/app/composables/providers/directus.ts +49 -0
- package/app/composables/providers/memory.ts +88 -0
- package/app/composables/registry.ts +13 -0
- package/app/composables/types.ts +35 -0
- package/app/composables/useLists.ts +20 -0
- package/app/composables/utils/transforms.ts +42 -0
- package/app/composables/utils/validation.ts +21 -0
- package/app/pages/lists/bookmark/[id].vue +76 -0
- package/app/pages/lists/index.vue +152 -0
- package/app/pages/lists/list/[...slug].vue +233 -0
- package/nuxt.config.ts +11 -0
- package/package.json +26 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" class="features4 start cid-v0Her0Ajsb" id="features04-1">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="row justify-content-center">
|
|
6
|
+
<div class="col-12 content-head">
|
|
7
|
+
<div class="mbr-section-head mb-5">
|
|
8
|
+
<h4 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
|
|
9
|
+
<strong>My Archived Lists</strong>
|
|
10
|
+
</h4>
|
|
11
|
+
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="item features-image col-12 col-md-6 col-lg-4" v-for="list in archived" :key="list.id">
|
|
14
|
+
<listCard :list="list" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import {
|
|
27
|
+
ref
|
|
28
|
+
} from 'vue'
|
|
29
|
+
import listCard from '~/components/related/list.vue'
|
|
30
|
+
import {
|
|
31
|
+
useUserStore
|
|
32
|
+
} from '#auth/app/stores/user'
|
|
33
|
+
|
|
34
|
+
const userStore = useUserStore()
|
|
35
|
+
const userDisplayName = computed(() => {
|
|
36
|
+
return userStore.user?.name || userStore.user?.username || 'User'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const model = ref(null)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
$directus,
|
|
43
|
+
$readItems
|
|
44
|
+
} = useNuxtApp()
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
data: archived
|
|
48
|
+
} = await useAsyncData('archived', () => {
|
|
49
|
+
return $directus.request($readItems('lists', {
|
|
50
|
+
filter: {
|
|
51
|
+
user: {
|
|
52
|
+
_eq: userDisplayName.value,
|
|
53
|
+
},
|
|
54
|
+
status: {
|
|
55
|
+
_eq: "Archived",
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
}))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
useHead({
|
|
62
|
+
title: 'My Archived Lists - Meeovi Tasks'
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" class="features4 start cid-v0Her0Ajsb" id="features04-1">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="row justify-content-center">
|
|
6
|
+
<div class="col-12 content-head">
|
|
7
|
+
<div class="mbr-section-head mb-5">
|
|
8
|
+
<h4 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
|
|
9
|
+
<strong>My Bookmarks</strong>
|
|
10
|
+
</h4>
|
|
11
|
+
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="item features-image col-12 col-md-6 col-lg-4" v-for="bookmark in bookmarks" :key="bookmark.id">
|
|
14
|
+
<bookmarkCard :bookmark="bookmark" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import {
|
|
27
|
+
ref
|
|
28
|
+
} from 'vue'
|
|
29
|
+
import bookmarkCard from '~/components/related/bookmark.vue'
|
|
30
|
+
import {
|
|
31
|
+
useUserStore
|
|
32
|
+
} from '#auth/app/stores/user'
|
|
33
|
+
|
|
34
|
+
const userStore = useUserStore()
|
|
35
|
+
const userDisplayName = computed(() => {
|
|
36
|
+
return userStore.user?.name || userStore.user?.username || 'User'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const model = ref(null)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
$directus,
|
|
43
|
+
$readItems
|
|
44
|
+
} = useNuxtApp()
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
data: bookmarks
|
|
48
|
+
} = await useAsyncData('bookmarks', () => {
|
|
49
|
+
return $directus.request($readItems('lists', {
|
|
50
|
+
filter: {
|
|
51
|
+
creator: {
|
|
52
|
+
_eq: userDisplayName.value,
|
|
53
|
+
},
|
|
54
|
+
type: {
|
|
55
|
+
_eq: "bookmark",
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
}))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
useHead({
|
|
62
|
+
title: 'My Bookmarks Lists - Meeovi Tasks'
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" class="features4 start cid-v0Her0Ajsb" id="features04-1">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="row justify-content-center">
|
|
6
|
+
<div class="col-12 content-head">
|
|
7
|
+
<div class="mbr-section-head mb-5">
|
|
8
|
+
<h4 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
|
|
9
|
+
<strong>My Lists</strong>
|
|
10
|
+
</h4>
|
|
11
|
+
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="item features-image col-12 col-md-6 col-lg-4" v-for="list in myLists" :key="list.id">
|
|
14
|
+
<listCard :list="list" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import {
|
|
27
|
+
ref
|
|
28
|
+
} from 'vue'
|
|
29
|
+
import listCard from '~/components/related/list.vue'
|
|
30
|
+
import {
|
|
31
|
+
useUserStore
|
|
32
|
+
} from '#auth/app/stores/user'
|
|
33
|
+
|
|
34
|
+
const userStore = useUserStore()
|
|
35
|
+
const userDisplayName = computed(() => {
|
|
36
|
+
return userStore.user?.name || userStore.user?.username || 'User'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const model = ref(null)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
$directus,
|
|
43
|
+
$readItems
|
|
44
|
+
} = useNuxtApp()
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
data: myLists
|
|
48
|
+
} = await useAsyncData('myLists', () => {
|
|
49
|
+
return $directus.request($readItems('lists', {
|
|
50
|
+
filter: {
|
|
51
|
+
user: {
|
|
52
|
+
_eq: userDisplayName.value,
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}))
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
useHead({
|
|
59
|
+
title: 'My Lists - Meeovi Tasks'
|
|
60
|
+
})
|
|
61
|
+
</script>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" class="features4 start cid-v0Her0Ajsb" id="features04-1">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="row justify-content-center">
|
|
6
|
+
<div class="col-12 content-head">
|
|
7
|
+
<div class="mbr-section-head mb-5">
|
|
8
|
+
<h4 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
|
|
9
|
+
<strong>My Starred Lists</strong>
|
|
10
|
+
</h4>
|
|
11
|
+
|
|
12
|
+
<div class="row">
|
|
13
|
+
<div class="item features-image col-12 col-md-6 col-lg-4" v-for="list in starred" :key="list.id">
|
|
14
|
+
<listCard :list="list" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import {
|
|
27
|
+
ref
|
|
28
|
+
} from 'vue'
|
|
29
|
+
import listCard from '~/components/related/list.vue'
|
|
30
|
+
import {
|
|
31
|
+
useUserStore
|
|
32
|
+
} from '#auth/app/stores/user'
|
|
33
|
+
|
|
34
|
+
const userStore = useUserStore()
|
|
35
|
+
const userDisplayName = computed(() => {
|
|
36
|
+
return userStore.user?.name || userStore.user?.username || 'User'
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const model = ref(null)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
$directus,
|
|
43
|
+
$readItems
|
|
44
|
+
} = useNuxtApp()
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
data: starred
|
|
48
|
+
} = await useAsyncData('starred', () => {
|
|
49
|
+
return $directus.request($readItems('lists', {
|
|
50
|
+
filter: {
|
|
51
|
+
user: {
|
|
52
|
+
_eq: userDisplayName.value,
|
|
53
|
+
},
|
|
54
|
+
favorite: {
|
|
55
|
+
_eq: "yes",
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
}))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
useHead({
|
|
62
|
+
title: 'My Starred Lists - Meeovi Tasks'
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="list-item-card" :class="`item-type-${item.content.type}`">
|
|
3
|
+
<!-- Media Item -->
|
|
4
|
+
<div v-if="item.content.type === 'media'">
|
|
5
|
+
<v-img
|
|
6
|
+
:src="item.content.thumbnail || '/default-media.png'"
|
|
7
|
+
height="200"
|
|
8
|
+
cover
|
|
9
|
+
>
|
|
10
|
+
<div class="d-flex align-end fill-height">
|
|
11
|
+
<v-chip color="primary" size="small" class="ma-2">
|
|
12
|
+
{{ item.content.media_type }}
|
|
13
|
+
</v-chip>
|
|
14
|
+
</div>
|
|
15
|
+
</v-img>
|
|
16
|
+
|
|
17
|
+
<v-card-title class="text-truncate">{{ item.content.title }}</v-card-title>
|
|
18
|
+
<v-card-subtitle v-if="item.content.metadata?.artist">
|
|
19
|
+
{{ item.content.metadata.artist }}
|
|
20
|
+
</v-card-subtitle>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Bookmark Item -->
|
|
24
|
+
<div v-else-if="item.content.type === 'bookmark'">
|
|
25
|
+
<v-card-title class="d-flex align-center">
|
|
26
|
+
<v-img
|
|
27
|
+
:src="item.content.favicon || '/default-favicon.png'"
|
|
28
|
+
width="16"
|
|
29
|
+
height="16"
|
|
30
|
+
class="me-2"
|
|
31
|
+
/>
|
|
32
|
+
<span class="text-truncate">{{ item.content.title }}</span>
|
|
33
|
+
</v-card-title>
|
|
34
|
+
|
|
35
|
+
<v-card-text>
|
|
36
|
+
<p class="text-body-2 text-truncate">{{ item.content.description }}</p>
|
|
37
|
+
<div class="d-flex align-center mt-2">
|
|
38
|
+
<v-chip
|
|
39
|
+
v-for="tag in item.content.tags?.slice(0, 2)"
|
|
40
|
+
:key="tag"
|
|
41
|
+
size="x-small"
|
|
42
|
+
class="me-1"
|
|
43
|
+
>
|
|
44
|
+
{{ tag }}
|
|
45
|
+
</v-chip>
|
|
46
|
+
<v-chip
|
|
47
|
+
v-if="item.content.read"
|
|
48
|
+
color="success"
|
|
49
|
+
size="x-small"
|
|
50
|
+
variant="outlined"
|
|
51
|
+
>
|
|
52
|
+
Read
|
|
53
|
+
</v-chip>
|
|
54
|
+
</div>
|
|
55
|
+
</v-card-text>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- Product/Wishlist Item -->
|
|
59
|
+
<div v-else-if="item.content.type === 'product'">
|
|
60
|
+
<v-img
|
|
61
|
+
:src="item.content.image || '/default-product.png'"
|
|
62
|
+
height="200"
|
|
63
|
+
cover
|
|
64
|
+
>
|
|
65
|
+
<div class="d-flex align-end fill-height">
|
|
66
|
+
<v-chip
|
|
67
|
+
:color="getPriorityColor(item.content.priority)"
|
|
68
|
+
size="small"
|
|
69
|
+
class="ma-2"
|
|
70
|
+
>
|
|
71
|
+
{{ item.content.priority }}
|
|
72
|
+
</v-chip>
|
|
73
|
+
</div>
|
|
74
|
+
</v-img>
|
|
75
|
+
|
|
76
|
+
<v-card-title class="text-truncate">{{ item.content.title }}</v-card-title>
|
|
77
|
+
<v-card-subtitle v-if="item.content.price">
|
|
78
|
+
${{ item.content.price }}
|
|
79
|
+
</v-card-subtitle>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Default Item -->
|
|
83
|
+
<div v-else>
|
|
84
|
+
<v-img
|
|
85
|
+
v-if="item.content.image"
|
|
86
|
+
:src="item.content.image"
|
|
87
|
+
height="200"
|
|
88
|
+
cover
|
|
89
|
+
/>
|
|
90
|
+
|
|
91
|
+
<v-card-title class="text-truncate">{{ item.content.title }}</v-card-title>
|
|
92
|
+
<v-card-text v-if="item.content.description">
|
|
93
|
+
<p class="text-body-2">{{ item.content.description }}</p>
|
|
94
|
+
</v-card-text>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- Actions -->
|
|
98
|
+
<v-card-actions>
|
|
99
|
+
<v-btn
|
|
100
|
+
v-if="item.content.url"
|
|
101
|
+
:href="item.content.url"
|
|
102
|
+
target="_blank"
|
|
103
|
+
variant="text"
|
|
104
|
+
size="small"
|
|
105
|
+
prepend-icon="mdi-open-in-new"
|
|
106
|
+
>
|
|
107
|
+
Open
|
|
108
|
+
</v-btn>
|
|
109
|
+
|
|
110
|
+
<v-spacer />
|
|
111
|
+
|
|
112
|
+
<v-menu>
|
|
113
|
+
<template v-slot:activator="{ props }">
|
|
114
|
+
<v-btn
|
|
115
|
+
icon="mdi-dots-vertical"
|
|
116
|
+
variant="text"
|
|
117
|
+
size="small"
|
|
118
|
+
v-bind="props"
|
|
119
|
+
/>
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<v-list>
|
|
123
|
+
<v-list-item @click="$emit('edit', item)">
|
|
124
|
+
<template v-slot:prepend>
|
|
125
|
+
<v-icon icon="mdi-pencil" />
|
|
126
|
+
</template>
|
|
127
|
+
<v-list-item-title>Edit</v-list-item-title>
|
|
128
|
+
</v-list-item>
|
|
129
|
+
|
|
130
|
+
<v-divider />
|
|
131
|
+
|
|
132
|
+
<v-list-item @click="$emit('delete', item.id)" class="text-error">
|
|
133
|
+
<template v-slot:prepend>
|
|
134
|
+
<v-icon icon="mdi-delete" color="error" />
|
|
135
|
+
</template>
|
|
136
|
+
<v-list-item-title>Delete</v-list-item-title>
|
|
137
|
+
</v-list-item>
|
|
138
|
+
</v-list>
|
|
139
|
+
</v-menu>
|
|
140
|
+
</v-card-actions>
|
|
141
|
+
</v-card>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<script setup>
|
|
145
|
+
const props = defineProps({
|
|
146
|
+
item: {
|
|
147
|
+
type: Object,
|
|
148
|
+
required: true
|
|
149
|
+
},
|
|
150
|
+
listType: {
|
|
151
|
+
type: String,
|
|
152
|
+
required: true
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const emit = defineEmits(['edit', 'delete'])
|
|
157
|
+
|
|
158
|
+
const getPriorityColor = (priority) => {
|
|
159
|
+
const colors = {
|
|
160
|
+
low: 'blue',
|
|
161
|
+
medium: 'orange',
|
|
162
|
+
high: 'red'
|
|
163
|
+
}
|
|
164
|
+
return colors[priority] || 'grey'
|
|
165
|
+
}
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
<style scoped>
|
|
169
|
+
.list-item-card {
|
|
170
|
+
height: 100%;
|
|
171
|
+
transition: all 0.2s ease;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.list-item-card:hover {
|
|
175
|
+
transform: translateY(-2px);
|
|
176
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.item-type-media {
|
|
180
|
+
border-left: 4px solid rgb(var(--v-theme-purple));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.item-type-bookmark {
|
|
184
|
+
border-left: 4px solid rgb(var(--v-theme-orange));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.item-type-product {
|
|
188
|
+
border-left: 4px solid rgb(var(--v-theme-pink));
|
|
189
|
+
}
|
|
190
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row justify="center">
|
|
3
|
+
<v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
|
|
4
|
+
<template v-slot:activator="{ props }">
|
|
5
|
+
<v-btn v-bind="props" class="rightAddBtn">
|
|
6
|
+
<v-icon start icon="fas:fa fa-plus"></v-icon>Create a Bookmark
|
|
7
|
+
</v-btn>
|
|
8
|
+
</template>
|
|
9
|
+
<v-card class="b-1">
|
|
10
|
+
<v-card-title>
|
|
11
|
+
<h3>Create New Bookmark</h3>
|
|
12
|
+
</v-card-title>
|
|
13
|
+
|
|
14
|
+
<v-card-text>
|
|
15
|
+
<div v-if="formError" class="error">{{ formError }}</div>
|
|
16
|
+
<div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
|
|
17
|
+
<form @submit.prevent="submitForm">
|
|
18
|
+
<DirectusFormElement v-for="field in websiteFields" :key="field.field" :field="field" v-model="form[field.field]" />
|
|
19
|
+
<v-btn type="submit">Submit</v-btn>
|
|
20
|
+
</form>
|
|
21
|
+
</v-card-text>
|
|
22
|
+
</v-card>
|
|
23
|
+
</v-dialog>
|
|
24
|
+
</v-row>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup>
|
|
28
|
+
import { ref } from 'vue'
|
|
29
|
+
import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
|
|
30
|
+
import { useDirectusForm } from '../../composables/globals/useDirectusForm'
|
|
31
|
+
|
|
32
|
+
const dialog = ref(false)
|
|
33
|
+
const { $directus, $readFieldsByCollection } = useNuxtApp()
|
|
34
|
+
|
|
35
|
+
const { data, error } = await useAsyncData('websites', async () => {
|
|
36
|
+
return $directus.request($readFieldsByCollection('websites'))
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// guard against undefined/null data.value and empty arrays
|
|
40
|
+
if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
|
|
41
|
+
console.error(error)
|
|
42
|
+
throw createError({
|
|
43
|
+
statusCode: 404,
|
|
44
|
+
statusMessage: 'Bookmark not found'
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const websiteFields = data
|
|
49
|
+
|
|
50
|
+
// use composable for form handling (validation, submit, provide context)
|
|
51
|
+
const { form, formError, formSuccess, submitForm } = useDirectusForm('websites', websiteFields, { clearOnSuccess: true, closeDialogRef: dialog })
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="list?.items?.length === 0" class="text-center py-8">
|
|
4
|
+
<v-icon size="64" color="grey-lighten-1">{{ getListIcon(list?.type) }}</v-icon>
|
|
5
|
+
<h3 class="text-h6 mt-4 mb-2">No items yet</h3>
|
|
6
|
+
<p class="text-body-2 text-medium-emphasis mb-4">
|
|
7
|
+
Add your first item to get started
|
|
8
|
+
</p>
|
|
9
|
+
<v-btn color="primary" @click="showAddDialog = true">
|
|
10
|
+
Add Item
|
|
11
|
+
</v-btn>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div v-else>
|
|
15
|
+
<!-- Task Items -->
|
|
16
|
+
<div v-if="list?.type === 'tasks'">
|
|
17
|
+
<TaskItem v-for="item in list?.items" :key="item?.id" :task="item?.content"
|
|
18
|
+
@update="updateTask(item?.id, $event)" @edit="editItem(item)" @duplicate="duplicateItem(item)"
|
|
19
|
+
@delete="deleteItem(item?.id)" />
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- Other List Types -->
|
|
23
|
+
<v-row v-else>
|
|
24
|
+
<v-col v-for="item in list?.items" :key="item?.id" cols="12" sm="6" md="4" lg="3">
|
|
25
|
+
<ListItemCard :item="item" :list-type="list?.type" @edit="editItem(item)"
|
|
26
|
+
@delete="deleteItem(item?.id)" />
|
|
27
|
+
</v-col>
|
|
28
|
+
</v-row>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import {
|
|
35
|
+
ref,
|
|
36
|
+
computed
|
|
37
|
+
} from 'vue'
|
|
38
|
+
//import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
|
|
39
|
+
import {
|
|
40
|
+
useDirectusForm
|
|
41
|
+
} from '../../composables/globals/useDirectusForm'
|
|
42
|
+
import ListItemCard from './ListItemCard.vue'
|
|
43
|
+
|
|
44
|
+
const props = defineProps({
|
|
45
|
+
modelValue: Boolean,
|
|
46
|
+
listType: String,
|
|
47
|
+
listId: String
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const dialog = ref(false)
|
|
51
|
+
const {
|
|
52
|
+
$directus,
|
|
53
|
+
$readFieldsByCollection
|
|
54
|
+
} = useNuxtApp()
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
data,
|
|
58
|
+
error
|
|
59
|
+
} = await useAsyncData('listItemsFields', async () => {
|
|
60
|
+
return $directus.request($readFieldsByCollection('list_items'))
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// normalize response: Directus may return { data: [...] } or an array directly
|
|
64
|
+
const listItemFields = computed(() => {
|
|
65
|
+
const resp = data?.value
|
|
66
|
+
return resp?.data ?? resp ?? []
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// guard against undefined/null and empty arrays
|
|
70
|
+
if (error.value || listItemFields.value == null || listItemFields.value.length === 0) {
|
|
71
|
+
console.error(error)
|
|
72
|
+
throw createError({
|
|
73
|
+
statusCode: 404,
|
|
74
|
+
statusMessage: 'List not found'
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// use composable for form handling (validation, submit, provide context)
|
|
79
|
+
const {
|
|
80
|
+
form,
|
|
81
|
+
formError,
|
|
82
|
+
formSuccess,
|
|
83
|
+
submitForm
|
|
84
|
+
} = useDirectusForm('list_items', listItemFields, {
|
|
85
|
+
clearOnSuccess: true,
|
|
86
|
+
closeDialogRef: dialog
|
|
87
|
+
})
|
|
88
|
+
</script>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row justify="center">
|
|
3
|
+
<v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
|
|
4
|
+
<template v-slot:activator="{ props }">
|
|
5
|
+
<v-btn v-bind="props" class="rightAddBtn">
|
|
6
|
+
<v-icon start icon="fas:fa fa-plus"></v-icon>Create a List
|
|
7
|
+
</v-btn>
|
|
8
|
+
</template>
|
|
9
|
+
<v-card class="b-1">
|
|
10
|
+
<v-card-title>
|
|
11
|
+
<h3>Create New List</h3>
|
|
12
|
+
</v-card-title>
|
|
13
|
+
|
|
14
|
+
<v-card-text>
|
|
15
|
+
<div v-if="formError" class="error">{{ formError }}</div>
|
|
16
|
+
<div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
|
|
17
|
+
<form @submit.prevent="submitForm">
|
|
18
|
+
<DirectusFormElement v-for="field in listFields" :key="field.field" :field="field" v-model="form[field.field]" />
|
|
19
|
+
<v-btn type="submit">Submit</v-btn>
|
|
20
|
+
</form>
|
|
21
|
+
</v-card-text>
|
|
22
|
+
</v-card>
|
|
23
|
+
</v-dialog>
|
|
24
|
+
</v-row>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup>
|
|
28
|
+
import { ref, computed } from 'vue'
|
|
29
|
+
import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
|
|
30
|
+
import { useDirectusForm } from '../../composables/globals/useDirectusForm'
|
|
31
|
+
|
|
32
|
+
const dialog = ref(false)
|
|
33
|
+
const { $directus, $readFieldsByCollection } = useNuxtApp()
|
|
34
|
+
|
|
35
|
+
const { data, error } = await useAsyncData('listsFields', async () => {
|
|
36
|
+
return $directus.request($readFieldsByCollection('lists'))
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// normalize response: Directus may return { data: [...] } or an array directly
|
|
40
|
+
const listFields = computed(() => {
|
|
41
|
+
const resp = data?.value
|
|
42
|
+
return resp?.data ?? resp ?? []
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// guard against undefined/null and empty arrays
|
|
46
|
+
if (error.value || listFields.value == null || listFields.value.length === 0) {
|
|
47
|
+
console.error(error)
|
|
48
|
+
throw createError({
|
|
49
|
+
statusCode: 404,
|
|
50
|
+
statusMessage: 'List not found'
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// use composable for form handling (validation, submit, provide context)
|
|
56
|
+
const { form, formError, formSuccess, submitForm } = useDirectusForm('lists', listFields, { clearOnSuccess: true, closeDialogRef: dialog })
|
|
57
|
+
</script>
|