@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,145 @@
1
+ <template>
2
+ <div>
3
+ <v-card elevation="0">
4
+ <v-toolbar title="Update A List"></v-toolbar>
5
+ <v-form @submit.prevent="createList">
6
+ <v-container>
7
+ <v-row>
8
+ <v-col cols="6">
9
+ <v-text-field v-model="title" label="List Name" required></v-text-field>
10
+ </v-col>
11
+ <v-col cols="6">
12
+ <v-combobox v-model="type" label="Type" :items="['List', 'Registry', 'Playlist', 'Todo']"></v-combobox>
13
+ </v-col>
14
+ <v-col cols="12">
15
+ <v-file-input clearable label="List Image"></v-file-input>
16
+ </v-col>
17
+ <v-col cols="12">
18
+ <v-textarea v-model="description" label="List Description"></v-textarea>
19
+ </v-col>
20
+ <v-col cols="12">
21
+ <v-card title="Choose a Product for your List">
22
+ <v-card-text>
23
+ <v-text-field density="compact" variant="solo" label="Search Meeovi for products"
24
+ append-inner-icon="fas:fa fa-search" single-line hide-details
25
+ @click:append-inner="onClick"></v-text-field>
26
+ <v-spacer></v-spacer>
27
+ <div class="d-flex pa-4">
28
+ <v-checkbox-btn v-model="includeFiles" class="pe-2" color="orange">
29
+ </v-checkbox-btn>
30
+ <!--<NuxtLink :to="`/product/${products.id}`">
31
+ <v-card class="ma-4" height="580" width="250" @click="toggle">
32
+ <NuxtImg loading="lazy" class="align-end text-white" height="280"
33
+ :src="`${products.featuredAsset.preview}`" :alt="products.name" cover />
34
+
35
+ <v-card-title class="pt-4">
36
+ {{ products.name }}
37
+ </v-card-title>
38
+
39
+ <v-card-text>
40
+ <div>Sku: {{ products.variants.sku }}</div>
41
+ </v-card-text>
42
+
43
+ <v-card-actions>
44
+ <v-card-title>$ {{ products.variants.price }}
45
+ </v-card-title>
46
+ </v-card-actions>
47
+ <div class="d-flex fill-height align-center justify-center">
48
+ <v-scale-transition>
49
+ <v-icon v-if="isSelected" color="white" size="48"
50
+ icon="mdi-close-circle-outline"></v-icon>
51
+ </v-scale-transition>
52
+ </div>
53
+ </v-card>
54
+ </NuxtLink>-->
55
+ </div>
56
+ </v-card-text>
57
+ </v-card>
58
+ </v-col>
59
+ </v-row>
60
+ </v-container>
61
+
62
+ <v-divider class="mt-12"></v-divider>
63
+ <v-card-actions>
64
+ <v-btn color="blue-darken-1" variant="text" type="submit" @click="reset = false">
65
+ Delete
66
+ </v-btn>
67
+ <v-spacer></v-spacer>
68
+ <v-btn color="blue-darken-1" variant="text" type="submit">
69
+ Update
70
+ </v-btn>
71
+ </v-card-actions>
72
+ </v-form>
73
+ </v-card>
74
+ </div>
75
+ </template>
76
+
77
+ <script setup>
78
+ import {
79
+ ref
80
+ } from 'vue'
81
+
82
+
83
+ const config = useRuntimeConfig();
84
+ const dialog = ref(false);
85
+ const includeFiles = ref(true);
86
+ const enabled = ref(false);
87
+ const title = ref('');
88
+ const acf = ref('');
89
+ const ispublic = ref('');
90
+ const description = ref('');
91
+ const type = ref('');
92
+ const products = ref('');
93
+ const owner = ref('');
94
+ const image = ref('');
95
+ const errorMessage = ref('');
96
+ const successMessage = ref('');
97
+
98
+ const updatelist = async () => {
99
+ try {
100
+ const response = await $fetch(`${config.apiUrl}/wp-json/wp/v2/list`, {
101
+ method: 'PUT',
102
+ headers: {
103
+ 'Content-Type': 'application/json',
104
+ 'Authorization': `Bearer ${config.wordpressToken}`
105
+ },
106
+ body: JSON.stringify({
107
+ title: title.value,
108
+ ispublic: ispublic.value,
109
+ description: description.value,
110
+ image: image.value,
111
+ type: type.value,
112
+ products: products.value,
113
+ owner: owner.value,
114
+ status: 'publish',
115
+ })
116
+ })
117
+
118
+ console.log(response);
119
+
120
+ if (response.id) {
121
+ successMessage.value = 'List updated successfully!'
122
+ errorMessage.value = ''
123
+ } else {
124
+ throw new Error('Failed to update list')
125
+ }
126
+ } catch (error) {
127
+ console.error('Error updating list:', error);
128
+ if (error.response) {
129
+ console.error('Error response:', error.response);
130
+ if (error.response.status === 403) {
131
+ errorMessage.value = 'You do not have permission to update a list.'
132
+ } else {
133
+ errorMessage.value = `Error: ${error.response.status} ${error.response.statusText}`
134
+ }
135
+ } else {
136
+ errorMessage.value = error.message
137
+ }
138
+ successMessage.value = ''
139
+ }
140
+ }
141
+
142
+ useHead({
143
+ title: 'Update List',
144
+ })
145
+ </script>
@@ -0,0 +1,267 @@
1
+ <template>
2
+ <div>
3
+ <v-row justify="center">
4
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition" @show="fetchBookmarkData">
5
+ <template v-slot:activator="{ props }">
6
+ <v-btn v-bind="props">
7
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Update Address
8
+ </v-btn>
9
+ </template>
10
+ <v-card>
11
+ <form @submit.prevent="handleSubmit">
12
+ <v-card>
13
+ <v-card-text>
14
+ <v-row>
15
+ <v-col cols="6"><v-text-field v-model="bookmarkData.name" id="bookmarkName"
16
+ label="Bookmark Name*" required /></v-col>
17
+ <v-col cols="6"><v-text-field v-model="bookmarkData.url" id="bookmarkUrl"
18
+ label="Bookmark Url*" required /></v-col>
19
+ <v-col cols="6">
20
+ <v-select v-model="bookmarkData.type" label="What type of bookmark is this?"
21
+ :items="['Website', 'Password']" />
22
+ </v-col>
23
+ <v-col cols="6">
24
+ <v-select v-model="bookmarkData.status"
25
+ label="Is this bookmark public or private?"
26
+ :items="['Public', 'Private']" />
27
+ </v-col>
28
+ <v-col cols="12">
29
+ <v-file-input @change="handleImageUpload" clearable density="compact"
30
+ prepend-icon="fas:fa fa-image" accept="image/*" label="Image"
31
+ variant="solo-inverted" />
32
+ </v-col>
33
+ <v-col cols="12"><v-textarea v-model="bookmarkData.note" label="Note"
34
+ variant="outlined"></v-textarea></v-col>
35
+ </v-row>
36
+ </v-card-text>
37
+ <v-divider class="mt-12"></v-divider>
38
+ <v-card-actions>
39
+ <v-btn color="blue-darken-1" variant="text" @click="isActive.value = false">
40
+ Close
41
+ </v-btn>
42
+ <v-spacer></v-spacer>
43
+ <v-btn color="blue-darken-1" variant="text" type="submit" @click="confirmDelete"
44
+ :loading="deleteLoading">
45
+ Delete Bookmark
46
+ </v-btn>
47
+ <v-btn color="blue-darken-1" variant="text" type="submit">
48
+ Update Bookmark
49
+ </v-btn>
50
+ </v-card-actions>
51
+ </v-card>
52
+ </form>
53
+
54
+ <!-- Delete Confirmation Dialog -->
55
+ <v-dialog v-model="deleteDialog" max-width="500px">
56
+ <v-card>
57
+ <v-card-title class="text-h5">Delete Bookmark</v-card-title>
58
+ <v-card-text>
59
+ Are you sure you want to delete this bookmark? This action cannot be undone.
60
+ </v-card-text>
61
+ <v-card-actions>
62
+ <v-spacer></v-spacer>
63
+ <v-btn color="blue-darken-1" variant="text" @click="deleteDialog = false">
64
+ Cancel
65
+ </v-btn>
66
+ <v-btn color="error" variant="text" @click="deleteBookmark" :loading="deleteLoading">
67
+ Delete
68
+ </v-btn>
69
+ </v-card-actions>
70
+ </v-card>
71
+ </v-dialog>
72
+ </v-card>
73
+ </v-dialog>
74
+ </v-row>
75
+ </div>
76
+ </template>
77
+
78
+ <script>
79
+ export default {
80
+ methods: {
81
+ reset() {
82
+ this.$refs.form.reset()
83
+ },
84
+ },
85
+ }
86
+ </script>
87
+
88
+ <script setup>
89
+ import {
90
+ ref
91
+ } from 'vue';
92
+ import {
93
+ useRoute,
94
+ useRouter
95
+ } from 'vue-router';
96
+ import uploadFiles from '~/app/composables/content/uploadFiles';
97
+ import updateBookmark from '~/app/composables/bookmarks/updateBookmark';
98
+ import {
99
+ updateItem,
100
+ deleteItem
101
+ } from '@meeovi/directus-client';
102
+ import {
103
+ useUserStore
104
+ } from '#auth/app/stores/user'
105
+
106
+ const userStore = useUserStore()
107
+
108
+ const userDisplayName = computed(() => {
109
+ return userStore.user?.name || userStore.user?.username || 'User'
110
+ })
111
+
112
+ const route = useRoute();
113
+
114
+ const id = route.params.id;
115
+
116
+ // Add these new refs for delete functionality
117
+ const deleteDialog = ref(false);
118
+ const deleteLoading = ref(false);
119
+
120
+ const bookmarkData = ref({
121
+ name: '',
122
+ url: '',
123
+ type: '',
124
+ status: '',
125
+ note: '',
126
+ image: null,
127
+ username: userDisplayName,
128
+ })
129
+
130
+ const dialog = ref(false);
131
+ const location = ref('bottom');
132
+
133
+ const imageFile = ref(null);
134
+ const loading = ref(false);
135
+
136
+ // Function to fetch existing bookmark data
137
+ const fetchBookmarkData = async () => {
138
+ try {
139
+ const {
140
+ $directus,
141
+ $readItem
142
+ } = useNuxtApp();
143
+ const bookmarkId = route.params.id;
144
+
145
+ if (!bookmarkId) return;
146
+
147
+ const response = await $directus.request($readItem('websites', bookmarkId));
148
+
149
+ if (response) {
150
+ bookmarkData.value = {
151
+ id: response.id,
152
+ name: response.name || '',
153
+ type: response.type || '',
154
+ status: response.status || '',
155
+ note: response.note || '',
156
+ image: response.image || '',
157
+ url: response.url || '',
158
+ username: response.username || userDisplayName
159
+ };
160
+ }
161
+ } catch (error) {
162
+ console.error('Error fetching bookmark:', error);
163
+ }
164
+ };
165
+
166
+ // Load existing data when component mounts
167
+ onMounted(() => {
168
+ if (route.params.id) {
169
+ fetchBookmarkData();
170
+ }
171
+ });
172
+ // Emit event for parent component updates
173
+ const emit = defineEmits(['bookmark-updated']);
174
+
175
+ const handleImageUpload = (event) => {
176
+ imageFile.value = event.target.files[0];
177
+ };
178
+
179
+ const handleSubmit = async () => {
180
+ try {
181
+ loading.value = true;
182
+
183
+ const {
184
+ $directus
185
+ } = useNuxtApp();
186
+
187
+ // Prepare update data
188
+ const updateData = {
189
+ id: bookmarkData.value.id,
190
+ name: bookmarkData.value.name,
191
+ type: bookmarkData.value.type,
192
+ status: bookmarkData.value.status,
193
+ note: bookmarkData.value.note,
194
+ image: bookmarkData.value.image,
195
+ url: bookmarkData.value.url,
196
+ };
197
+
198
+ // Handle image upload if there's a new image
199
+ if (imageFile.value) {
200
+ const uploadedFiles = await uploadFiles({
201
+ imageFile: imageFile.value,
202
+ });
203
+ updateData.image = uploadedFiles.imageId;
204
+ }
205
+
206
+ // Update the bookmark using Directus updateItem
207
+ const updatedBookmark = await $directus.request(
208
+ updateItem('websites', route.params.id, updateData)
209
+ );
210
+
211
+ if (updatedBookmark) {
212
+ // Refresh the bookmark data
213
+ await fetchBookmarkData();
214
+
215
+ // Show success message
216
+ alert('Bookmark updated successfully');
217
+ } else {
218
+ throw new Error('Failed to update bookmark');
219
+ }
220
+
221
+ } catch (error) {
222
+ console.error('Error updating bookmark:', error);
223
+ alert('Error updating bookmark: ' + error.message);
224
+ } finally {
225
+ loading.value = false;
226
+ }
227
+ };
228
+
229
+ // Remove the onMounted hook since we'll use the dialog @show event instead
230
+ // Instead, watch for dialog changes
231
+ watch(dialog, (newValue) => {
232
+ if (newValue && route.params.id) {
233
+ fetchBookmarkData();
234
+ }
235
+ });
236
+
237
+ // Add these new functions for delete functionality
238
+ const confirmDelete = () => {
239
+ deleteDialog.value = true;
240
+ };
241
+
242
+ const deleteBookmark = async () => {
243
+ try {
244
+ deleteLoading.value = true;
245
+ const {
246
+ $directus
247
+ } = useNuxtApp();
248
+
249
+ // Delete the bookmark using the imported deleteItem function
250
+ await $directus.request(deleteItem('websites', route.params.id));
251
+
252
+ // Close the delete dialog
253
+ deleteDialog.value = false;
254
+
255
+ // Show success message
256
+ alert('Bookmark deleted successfully');
257
+
258
+ // Redirect to bookmarks page
259
+ navigateTo('/commerce/lists');
260
+ } catch (error) {
261
+ console.error('Error deleting bookmark:', error);
262
+ alert('Error deleting bookmark: ' + error.message);
263
+ } finally {
264
+ deleteLoading.value = false;
265
+ }
266
+ };
267
+ </script>
@@ -0,0 +1,192 @@
1
+ <template>
2
+ <div>
3
+ <v-row justify="center">
4
+ <v-card>
5
+ <form @submit.prevent="handleSubmit">
6
+ <v-toolbar dark color="primary">
7
+ <v-btn icon dark @click="dialog = false">
8
+ <v-icon icon="fas:fa fa-circle-xmark"></v-icon>
9
+ </v-btn>
10
+ <v-card-title>
11
+ <span class="text-h6">Create a new Space</span>
12
+ </v-card-title>
13
+ </v-toolbar>
14
+ <v-card-text>
15
+ <v-container>
16
+ <v-row>
17
+ <v-col cols="12">
18
+ <v-text-field v-model="listData.name" label="List Name" required></v-text-field>
19
+ </v-col>
20
+ <v-col cols="6">
21
+ <v-select v-model="listData.type" label="Type"
22
+ :items="['List', 'Registry', 'Playlist', 'Todo']"></v-select>
23
+ </v-col>
24
+ <v-col cols="6">
25
+ <v-select v-model="listData.status" label="Status"
26
+ :items="['Public', 'Private']"></v-select>
27
+ </v-col>
28
+ <v-col cols="12">
29
+ <v-file-input @change="handleImageUpload" clearable
30
+ density="compact" prepend-icon="fas:fa fa-image" accept="image/*"
31
+ label="Image for List" variant="solo-inverted" />
32
+ </v-col>
33
+ <v-col cols="12">
34
+ <v-textarea v-model="listData.description" label="List Description"></v-textarea>
35
+ </v-col>
36
+ <v-col cols="12">
37
+ <v-card title="Choose a Product for your List">
38
+ <v-card-text>
39
+ <v-text-field density="compact" variant="solo"
40
+ label="Search Meeovi for products" append-inner-icon="fas:fa fa-search"
41
+ single-line hide-details></v-text-field>
42
+ <div class="d-flex pa-4">
43
+ <v-checkbox-btn v-model="includeFiles" class="pe-2" color="orange">
44
+ </v-checkbox-btn>
45
+ <!--<NuxtLink :to="`/product/${products.id}`">
46
+ <v-card class="ma-4" height="580" width="250" @click="toggle">
47
+ <NuxtImg loading="lazy" class="align-end text-white" height="280"
48
+ :src="`${products.featuredAsset.preview}`" :alt="products.name" cover />
49
+
50
+ <v-card-title class="pt-4">
51
+ {{ products.name }}
52
+ </v-card-title>
53
+
54
+ <v-card-text>
55
+ <div>Sku: {{ products.variants.sku }}</div>
56
+ </v-card-text>
57
+
58
+ <v-card-actions>
59
+ <v-card-title>$ {{ products.variants.price }}
60
+ </v-card-title>
61
+ </v-card-actions>
62
+ <div class="d-flex fill-height align-center justify-center">
63
+ <v-scale-transition>
64
+ <v-icon v-if="isSelected" color="white" size="48"
65
+ icon="mdi-close-circle-outline"></v-icon>
66
+ </v-scale-transition>
67
+ </div>
68
+ </v-card>
69
+ </NuxtLink>-->
70
+ </div>
71
+ </v-card-text>
72
+ </v-card>
73
+ </v-col>
74
+ </v-row>
75
+ </v-container>
76
+ <small>*indicates required field</small>
77
+ </v-card-text>
78
+ <v-divider class="mt-12"></v-divider>
79
+ <v-card-actions>
80
+ <v-btn color="blue-darken-1" variant="text" type="submit" @click="resetForm = false">
81
+ Reset
82
+ </v-btn>
83
+ <v-btn color="blue-darken-1" variant="text" type="submit">
84
+ Update
85
+ </v-btn>
86
+ </v-card-actions>
87
+ </form>
88
+ </v-card>
89
+ </v-row>
90
+ </div>
91
+ </template>
92
+
93
+ <script setup>
94
+ import { ref, onMounted } from 'vue';
95
+ import { useRoute, useRouter } from 'vue-router';
96
+ import uploadFiles from '~//composables/uploadFiles';
97
+ import updateList from '~/app/composables/lists/updateList';
98
+
99
+ const route = useRoute();
100
+ const router = useRouter();
101
+
102
+ const listData = ref({
103
+ id: '', // Add this to store the list ID
104
+ name: '',
105
+ type: '',
106
+ status: '',
107
+ description: '',
108
+ image: null,
109
+ });
110
+
111
+ const dialog = ref(false);
112
+ const includeFiles = ref(true);
113
+ const imageFile = ref(null);
114
+ const loading = ref(false);
115
+
116
+ // Function to fetch existing list data
117
+ const fetchListData = async () => {
118
+ try {
119
+ const { $directus, $readItem } = useNuxtApp();
120
+ const listId = route.params.id; // Assuming you're passing the ID in the route
121
+ const response = await $directus.request(readItem('lists', listId));
122
+
123
+ // Populate the form with existing data
124
+ listData.value = {
125
+ id: response.id,
126
+ name: response.name,
127
+ type: response.type,
128
+ status: response.status,
129
+ description: response.description,
130
+ image: response.image
131
+ };
132
+ } catch (error) {
133
+ console.error('Error fetching list:', error);
134
+ }
135
+ };
136
+
137
+ // Load existing data when component mounts
138
+ onMounted(() => {
139
+ if (route.params.id) {
140
+ fetchListData();
141
+ }
142
+ });
143
+
144
+ const handleImageUpload = (event) => {
145
+ imageFile.value = event.target.files[0];
146
+ };
147
+
148
+ const resetForm = () => {
149
+ listData.value = {
150
+ name: '',
151
+ type: '',
152
+ status: '',
153
+ description: '',
154
+ image: null,
155
+ };
156
+ imageFile.value = null;
157
+ };
158
+
159
+ const handleSubmit = async () => {
160
+ try {
161
+ loading.value = true;
162
+
163
+ // Handle image upload if there's a new image
164
+ if (imageFile.value) {
165
+ const uploadedFiles = await uploadFiles({
166
+ imageFile: imageFile.value,
167
+ });
168
+ listData.value.image = uploadedFiles.imageId;
169
+ }
170
+
171
+ // Update the list
172
+ const updatedList = await updateList(route.params.id, {
173
+ name: listData.value.name,
174
+ type: listData.value.type,
175
+ status: listData.value.status,
176
+ description: listData.value.description,
177
+ image: listData.value.image,
178
+ });
179
+
180
+ console.log('List updated successfully:', updatedList);
181
+
182
+ // Show success message (you can implement your preferred notification system)
183
+ alert('List updated successfully');
184
+
185
+ } catch (error) {
186
+ console.error('Error updating list:', error);
187
+ alert('Error updating list: ' + error.message);
188
+ } finally {
189
+ loading.value = false;
190
+ }
191
+ };
192
+ </script>