@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,152 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" class="header1 cid-v0HengyO4j" id="header01-0">
|
|
4
|
+
<div class="container-fluid">
|
|
5
|
+
<div class="row justify-content-left auto-text">
|
|
6
|
+
<div v-if="page?.image?.filename_disk" class="col-12 col-md-12 col-lg-8 image-wrapper">
|
|
7
|
+
<img class="w-100" :src="`${$directus.url}assets/${page?.image?.filename_disk}`"
|
|
8
|
+
:alt="page?.name">
|
|
9
|
+
</div>
|
|
10
|
+
<div v-else class="col-12 col-md-12 col-lg-8 image-wrapper">
|
|
11
|
+
<img class="w-100" src="../../assets/images/background1.jpg" :alt="page?.name">
|
|
12
|
+
</div>
|
|
13
|
+
<div class="col-12 col-lg col-md-12">
|
|
14
|
+
<div class="text-wrapper align-left rightTextColumn">
|
|
15
|
+
<h1
|
|
16
|
+
class="mbr-section-title text-black align-center mbr-fonts-style mb-4 display-2 auto-text">
|
|
17
|
+
<strong>{{ page?.name }}</strong>
|
|
18
|
+
</h1>
|
|
19
|
+
<p class="mbr-text mbr-fonts-style mb-4 display-7 text-black align-center auto-text"
|
|
20
|
+
v-dompurify-html="page?.content"></p>
|
|
21
|
+
<createList
|
|
22
|
+
class="mbr-section-btn mt-3 mobi-mbri mobi-mbri-plus mbr-iconfont mbr-iconfont-btn" />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</section>
|
|
28
|
+
|
|
29
|
+
<v-sheet class="mx-auto sliderLists row align-items-stretch items-row justify-content-center">
|
|
30
|
+
<v-toolbar color="transparent">
|
|
31
|
+
<v-toolbar-title>{{ page?.repeaterTextBox?.[0]?.name }}</v-toolbar-title>
|
|
32
|
+
</v-toolbar>
|
|
33
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
34
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }" v-for="(list, index) in myLists"
|
|
35
|
+
:key="index">
|
|
36
|
+
<listCard :class="['ma-4', selectedClass]" :list="list" v-if="isSelected" @click="toggle" />
|
|
37
|
+
</v-slide-group-item>
|
|
38
|
+
</v-slide-group>
|
|
39
|
+
</v-sheet>
|
|
40
|
+
|
|
41
|
+
<RelatedLists />
|
|
42
|
+
|
|
43
|
+
<v-sheet class="mx-auto sliderLists row align-items-stretch items-row justify-content-center">
|
|
44
|
+
<v-toolbar color="transparent">
|
|
45
|
+
<v-toolbar-title>{{ page?.repeaterTextBox?.[2]?.name }}</v-toolbar-title>
|
|
46
|
+
</v-toolbar>
|
|
47
|
+
<v-slide-group v-model="model" class="pa-4" selected-class="bg-success" show-arrows>
|
|
48
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
49
|
+
v-for="(bookmarks, index) in myBookmarks" :key="index">
|
|
50
|
+
<listCard :class="['ma-4', selectedClass]" :list="bookmarks" v-if="isSelected" @click="toggle" />
|
|
51
|
+
</v-slide-group-item>
|
|
52
|
+
</v-slide-group>
|
|
53
|
+
</v-sheet>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup>
|
|
58
|
+
import {
|
|
59
|
+
ref
|
|
60
|
+
} from 'vue'
|
|
61
|
+
import listCard from '~/components/related/list.vue'
|
|
62
|
+
import RelatedLists from '~/components/related/relatedlists.vue'
|
|
63
|
+
import createList from '~/components/lists/add-list.vue'
|
|
64
|
+
import {
|
|
65
|
+
useUserStore
|
|
66
|
+
} from '../../../../auth/app/stores/user'
|
|
67
|
+
|
|
68
|
+
const userStore = useUserStore()
|
|
69
|
+
const userDisplayName = computed(() => {
|
|
70
|
+
return userStore.user?.name || userStore.user?.username || 'User'
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const model = ref(null)
|
|
74
|
+
|
|
75
|
+
const {
|
|
76
|
+
$directus,
|
|
77
|
+
$readItems
|
|
78
|
+
} = useNuxtApp()
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
data: lists
|
|
82
|
+
} = await useAsyncData('lists', () => {
|
|
83
|
+
return $directus.request($readItems('lists', {
|
|
84
|
+
filter: {
|
|
85
|
+
status: {
|
|
86
|
+
_eq: 'Public'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
}))
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const {
|
|
93
|
+
data: myLists
|
|
94
|
+
} = await useAsyncData('myLists', () => {
|
|
95
|
+
return $directus.request($readItems('lists', {
|
|
96
|
+
filter: {
|
|
97
|
+
user: {
|
|
98
|
+
user_id: {
|
|
99
|
+
first_name: {
|
|
100
|
+
_eq: `${userDisplayName.firstName.value}`
|
|
101
|
+
},
|
|
102
|
+
last_name: {
|
|
103
|
+
_eq: `${userDisplayName.lastName.value}`
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}))
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const {
|
|
112
|
+
data: myBookmarks
|
|
113
|
+
} = await useAsyncData('myBookmarks', () => {
|
|
114
|
+
return $directus.request($readItems('lists', {
|
|
115
|
+
filter: {
|
|
116
|
+
user: {
|
|
117
|
+
user_id: {
|
|
118
|
+
first_name: {
|
|
119
|
+
_eq: `${userDisplayName.firstName.value}`
|
|
120
|
+
},
|
|
121
|
+
last_name: {
|
|
122
|
+
_eq: `${userDisplayName.lastName.value}`
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
lists_type: {
|
|
126
|
+
lists_type_id: {
|
|
127
|
+
_eq: 'Bookmarks'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
}))
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const {
|
|
136
|
+
data: page
|
|
137
|
+
} = await useAsyncData('page', () => {
|
|
138
|
+
return $directus.request($readItems('pages', {
|
|
139
|
+
filter: {
|
|
140
|
+
id: {
|
|
141
|
+
_eq: 40
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
fields: ['*'],
|
|
145
|
+
limit: 1
|
|
146
|
+
})).then(response => response?.[0]) // Get first item from response
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
useHead({
|
|
150
|
+
title: 'Meeovi Tasks'
|
|
151
|
+
})
|
|
152
|
+
</script>
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container fluid>
|
|
3
|
+
<div v-if="pending" class="text-center py-8">
|
|
4
|
+
<v-progress-circular indeterminate color="primary" />
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div v-else-if="error" class="text-center py-8">
|
|
8
|
+
<v-alert type="error">Failed to load list</v-alert>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div v-else-if="list">
|
|
12
|
+
<!-- List Header -->
|
|
13
|
+
<v-toolbar :color="getListColor(list.type)" extended>
|
|
14
|
+
<v-toolbar-title>{{ list.name }}</v-toolbar-title>
|
|
15
|
+
|
|
16
|
+
<template v-slot:extension>
|
|
17
|
+
<p v-dompurify-html="list?.description"></p>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<v-chip>{{ list?.type }}</v-chip>
|
|
21
|
+
<v-chip :color="list?.status === 'public' ? 'success' : 'default'" class="ml-3" variant="outlined">
|
|
22
|
+
{{ list?.status }}
|
|
23
|
+
</v-chip>
|
|
24
|
+
|
|
25
|
+
<v-btn color="primary" prepend-icon="fas fa-plus" @click="showAddDialog = true">
|
|
26
|
+
Add Item
|
|
27
|
+
</v-btn>
|
|
28
|
+
|
|
29
|
+
<!-- List Items -->
|
|
30
|
+
<addList />
|
|
31
|
+
</v-toolbar>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Add Item Dialog -->
|
|
35
|
+
<listItem v-model="showAddDialog" :list-type="list?.type" :list-id="list?.id" @added="refreshList" />
|
|
36
|
+
|
|
37
|
+
<section data-bs-version="5.1" class="mbr-section features20 cid-txNnCwzel4" id="features20-4t"
|
|
38
|
+
data-sortbtn="btn-primary">
|
|
39
|
+
<div class="container-fluid">
|
|
40
|
+
<h2 class="mbr-section-title text-center mbr-fonts-style display-5 auto-text">
|
|
41
|
+
Items in this {{ list?.type }}</h2>
|
|
42
|
+
<div class="underline align-center pb-3">
|
|
43
|
+
<div class="line"></div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<v-sheet class="mx-auto">
|
|
47
|
+
<v-slide-group v-model="model" class="pa-4" show-arrows v-if="list?.products?.length">
|
|
48
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
|
|
49
|
+
v-for="(products, index) in list?.products" :key="index">
|
|
50
|
+
<productCard :product="products?.products_id" :class="['ma-4', selectedClass]" @click="toggle" />
|
|
51
|
+
|
|
52
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
53
|
+
<v-scale-transition>
|
|
54
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline" size="48"></v-icon>
|
|
55
|
+
</v-scale-transition>
|
|
56
|
+
</div>
|
|
57
|
+
</v-slide-group-item>
|
|
58
|
+
</v-slide-group>
|
|
59
|
+
|
|
60
|
+
<v-slide-group v-model="model" class="pa-4" show-arrows v-else-if="list?.spaces?.length">
|
|
61
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }" v-for="(spaces, index) in list?.spaces"
|
|
62
|
+
:key="index">
|
|
63
|
+
<spaceCard :space="spaces" :class="['ma-4', selectedClass]" @click="toggle" />
|
|
64
|
+
|
|
65
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
66
|
+
<v-scale-transition>
|
|
67
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline" size="48"></v-icon>
|
|
68
|
+
</v-scale-transition>
|
|
69
|
+
</div>
|
|
70
|
+
</v-slide-group-item>
|
|
71
|
+
</v-slide-group>
|
|
72
|
+
|
|
73
|
+
<v-slide-group v-model="model" class="pa-4" show-arrows v-else-if="list?.shorts?.length">
|
|
74
|
+
<v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }" v-for="(shorts, index) in list?.shorts"
|
|
75
|
+
:key="index">
|
|
76
|
+
<shortCard :short="shorts?.shorts_id" :class="['ma-4', selectedClass]" @click="toggle" />
|
|
77
|
+
|
|
78
|
+
<div class="d-flex fill-height align-center justify-center">
|
|
79
|
+
<v-scale-transition>
|
|
80
|
+
<v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline" size="48"></v-icon>
|
|
81
|
+
</v-scale-transition>
|
|
82
|
+
</div>
|
|
83
|
+
</v-slide-group-item>
|
|
84
|
+
</v-slide-group>
|
|
85
|
+
|
|
86
|
+
<div v-else class="text-center py-8">
|
|
87
|
+
<p>No items in this {{ list?.type }}</p>
|
|
88
|
+
</div>
|
|
89
|
+
</v-sheet>
|
|
90
|
+
</div>
|
|
91
|
+
</section>
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
</v-container>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<script setup>
|
|
98
|
+
import {
|
|
99
|
+
ref,
|
|
100
|
+
computed
|
|
101
|
+
} from 'vue'
|
|
102
|
+
import {
|
|
103
|
+
useRoute
|
|
104
|
+
} from 'vue-router'
|
|
105
|
+
import {
|
|
106
|
+
useLists
|
|
107
|
+
} from '@/composables/lists/useLists'
|
|
108
|
+
import listItem from '@/components/lists/add-list-item.vue'
|
|
109
|
+
import addList from '@/components/lists/add-list.vue'
|
|
110
|
+
import productCard from '#commerce/app/components/catalog/product/productCard.vue'
|
|
111
|
+
import spaceCard from '#social/app/components/related/space.vue'
|
|
112
|
+
import shortCard from '#social/app/components/related/short.vue'
|
|
113
|
+
|
|
114
|
+
const route = useRoute()
|
|
115
|
+
const {
|
|
116
|
+
updateListItem,
|
|
117
|
+
removeFromList
|
|
118
|
+
} = useLists()
|
|
119
|
+
|
|
120
|
+
const showAddDialog = ref(false)
|
|
121
|
+
|
|
122
|
+
const {
|
|
123
|
+
$directus,
|
|
124
|
+
$readItems
|
|
125
|
+
} = useNuxtApp()
|
|
126
|
+
|
|
127
|
+
const slug = computed(() => {
|
|
128
|
+
const s = route.params.slug
|
|
129
|
+
return Array.isArray(s) ? s[0] : s
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const {
|
|
133
|
+
data: listRaw,
|
|
134
|
+
pending,
|
|
135
|
+
error,
|
|
136
|
+
refresh: refreshList
|
|
137
|
+
} = await useAsyncData('list', () => {
|
|
138
|
+
return $directus.request(
|
|
139
|
+
$readItems('lists', {
|
|
140
|
+
fields: [
|
|
141
|
+
'*',
|
|
142
|
+
'category.categories_id.*',
|
|
143
|
+
'department.departments_id',
|
|
144
|
+
'spaces.spaces_id.*',
|
|
145
|
+
'products.products_id.*',
|
|
146
|
+
'products.products_id.image.*',
|
|
147
|
+
'vibez.shorts_id.*',
|
|
148
|
+
'list_template.templates.*',
|
|
149
|
+
'image.*',
|
|
150
|
+
'media.*',
|
|
151
|
+
'list_items.list_items_id.*',
|
|
152
|
+
'list_products.list_products_id.*',
|
|
153
|
+
'user.directus_users.*'
|
|
154
|
+
],
|
|
155
|
+
filter: {
|
|
156
|
+
slug: {
|
|
157
|
+
_eq: slug.value
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
limit: 1
|
|
161
|
+
})
|
|
162
|
+
)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const list = computed(() => listRaw.value?.[0] || null)
|
|
166
|
+
|
|
167
|
+
const mediaItems = computed(() => {
|
|
168
|
+
if (list.value?.type !== 'playlist') return []
|
|
169
|
+
return list.value.items?.filter(item =>
|
|
170
|
+
item.content.type === 'media' && ['audio', 'video'].includes(item.content.media_type)
|
|
171
|
+
).map(item => item.content) || []
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const getListIcon = (type) => {
|
|
175
|
+
const icons = {
|
|
176
|
+
default: 'mdi-format-list-bulleted',
|
|
177
|
+
playlist: 'mdi-playlist-music',
|
|
178
|
+
wishlist: 'mdi-heart',
|
|
179
|
+
bookmarks: 'mdi-bookmark',
|
|
180
|
+
tasks: 'mdi-check-circle'
|
|
181
|
+
}
|
|
182
|
+
return icons[type] || icons.default
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const getListColor = (type) => {
|
|
186
|
+
const colors = {
|
|
187
|
+
default: 'primary',
|
|
188
|
+
playlist: 'purple',
|
|
189
|
+
wishlist: 'pink',
|
|
190
|
+
bookmarks: 'orange',
|
|
191
|
+
tasks: 'green'
|
|
192
|
+
}
|
|
193
|
+
return colors[type] || colors.default
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const updateTask = async (itemId, taskData) => {
|
|
197
|
+
try {
|
|
198
|
+
await updateListItem(itemId, {
|
|
199
|
+
content: taskData
|
|
200
|
+
})
|
|
201
|
+
await refreshList()
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('Failed to update task:', error)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const editItem = (item) => {
|
|
208
|
+
// Handle edit functionality
|
|
209
|
+
console.log('Edit item:', item)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const duplicateItem = (item) => {
|
|
213
|
+
// Handle duplicate functionality
|
|
214
|
+
console.log('Duplicate item:', item)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const deleteItem = async (itemId) => {
|
|
218
|
+
try {
|
|
219
|
+
await removeFromList(itemId)
|
|
220
|
+
await refreshList()
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error('Failed to delete item:', error)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
useHead({
|
|
227
|
+
title: computed(() => list.value?.name || 'List')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
definePageMeta({
|
|
231
|
+
//middleware: ['authenticated']
|
|
232
|
+
})
|
|
233
|
+
</script>
|
package/nuxt.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meeovi/layer-lists",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Official Lists module for the M Framework.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"lists",
|
|
10
|
+
"alternate-framework",
|
|
11
|
+
"nuxt",
|
|
12
|
+
"layer"
|
|
13
|
+
],
|
|
14
|
+
"author": "Meeovi",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@meeovi/api": "^1.0.1",
|
|
19
|
+
"@meeovi/layer-social": "^1.0.2",
|
|
20
|
+
"list.js": "^2.3.1",
|
|
21
|
+
"nanoid": "^5.1.6"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"nuxt": "^4.3.0"
|
|
25
|
+
}
|
|
26
|
+
}
|