@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.
Files changed (47) hide show
  1. package/app/components/features/archived.vue +64 -0
  2. package/app/components/features/bookmarks.vue +64 -0
  3. package/app/components/features/lists.vue +61 -0
  4. package/app/components/features/starred.vue +64 -0
  5. package/app/components/lists/ListItemCard.vue +190 -0
  6. package/app/components/lists/add-bookmark.vue +52 -0
  7. package/app/components/lists/add-list-item.vue +88 -0
  8. package/app/components/lists/add-list.vue +57 -0
  9. package/app/components/lists/lists.vue +6 -0
  10. package/app/components/lists/listsettings.vue +145 -0
  11. package/app/components/lists/update-bookmark.vue +267 -0
  12. package/app/components/lists/update-list.vue +192 -0
  13. package/app/components/media/MediaPlayer.vue +302 -0
  14. package/app/components/partials/addtolist.vue +233 -0
  15. package/app/components/partials/createListBtn.vue +95 -0
  16. package/app/components/partials/listBtn.vue +35 -0
  17. package/app/components/related/list.vue +33 -0
  18. package/app/components/related/relatedlists.vue +43 -0
  19. package/app/components/tasks/TaskItem.vue +204 -0
  20. package/app/composables/bookmarks/createBookmark.js +30 -0
  21. package/app/composables/bookmarks/deleteBookmark.js +15 -0
  22. package/app/composables/bookmarks/updateBookmark.js +15 -0
  23. package/app/composables/config.ts +17 -0
  24. package/app/composables/content/uploadFiles.js +41 -0
  25. package/app/composables/globals/useDirectusForm.ts +1 -0
  26. package/app/composables/lists/createList.js +25 -0
  27. package/app/composables/lists/deleteList.js +14 -0
  28. package/app/composables/lists/updateList.js +20 -0
  29. package/app/composables/lists/useBookmarks.js +69 -0
  30. package/app/composables/lists/useLists.js +120 -0
  31. package/app/composables/lists/usePlaylist.js +64 -0
  32. package/app/composables/lists/useSaved.js +29 -0
  33. package/app/composables/lists/useTasks.js +86 -0
  34. package/app/composables/lists/useWishlist.js +51 -0
  35. package/app/composables/providers/atproto.ts +156 -0
  36. package/app/composables/providers/directus.ts +49 -0
  37. package/app/composables/providers/memory.ts +88 -0
  38. package/app/composables/registry.ts +13 -0
  39. package/app/composables/types.ts +35 -0
  40. package/app/composables/useLists.ts +20 -0
  41. package/app/composables/utils/transforms.ts +42 -0
  42. package/app/composables/utils/validation.ts +21 -0
  43. package/app/pages/lists/bookmark/[id].vue +76 -0
  44. package/app/pages/lists/index.vue +152 -0
  45. package/app/pages/lists/list/[...slug].vue +233 -0
  46. package/nuxt.config.ts +11 -0
  47. 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
@@ -0,0 +1,11 @@
1
+ import {
2
+ defineNuxtConfig
3
+ } from 'nuxt/config'
4
+
5
+ export default defineNuxtConfig({
6
+ $meta: {
7
+ name: 'lists',
8
+ },
9
+
10
+ runtimeConfig: {}
11
+ })
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
+ }