@meeovi/layer-lists 1.0.6 → 1.0.10

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/lists/createList.js +25 -0
  26. package/app/composables/lists/deleteList.js +14 -0
  27. package/app/composables/lists/updateList.js +20 -0
  28. package/app/composables/lists/useBookmarks.js +69 -0
  29. package/app/composables/lists/useLists.js +120 -0
  30. package/app/composables/lists/usePlaylist.js +64 -0
  31. package/app/composables/lists/useSaved.js +29 -0
  32. package/app/composables/lists/useTasks.js +86 -0
  33. package/app/composables/lists/useWishlist.js +51 -0
  34. package/app/composables/module.ts +75 -0
  35. package/app/composables/providers/directus.ts +145 -0
  36. package/app/composables/providers/memory.ts +127 -0
  37. package/app/composables/registry.ts +18 -0
  38. package/app/composables/types.ts +44 -0
  39. package/app/composables/useLists.ts +20 -0
  40. package/app/composables/utils/health.ts +16 -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/package.json +5 -13
  47. package/tsconfig.json +20 -0
@@ -0,0 +1,29 @@
1
+ export const useSaved = () => {
2
+ const { $directus } = useNuxtApp();
3
+ const user = useSupabaseUser();
4
+
5
+ const isSaved = async (productId) => {
6
+ if (!user.value) return false;
7
+
8
+ const lists = await $directus.items('lists').readItems({
9
+ filter: { user: { _eq: user.value.id } },
10
+ fields: ['id']
11
+ });
12
+
13
+ const listIds = lists.map((l) => l.id);
14
+
15
+ if (listIds.length === 0) return false;
16
+
17
+ const saved = await $directus.items('list_items').readItems({
18
+ filter: {
19
+ list: { _in: listIds },
20
+ product: { _eq: productId }
21
+ }
22
+ });
23
+
24
+ return saved.length > 0;
25
+ };
26
+
27
+ return { isSaved };
28
+ };
29
+
@@ -0,0 +1,86 @@
1
+ export const useTasks = () => {
2
+ const { createList, addToList, getUserLists, updateListItem } = useLists()
3
+
4
+ const createTaskList = async (name, description = '') => {
5
+ return await createList({
6
+ name,
7
+ description,
8
+ type: 'tasks',
9
+ visibility: 'private'
10
+ })
11
+ }
12
+
13
+ const addTask = async (listId, taskData) => {
14
+ const { title, description, priority, due_date, labels } = taskData
15
+
16
+ return await addToList(listId, {
17
+ type: 'task',
18
+ title,
19
+ description,
20
+ priority: priority || 'medium',
21
+ due_date,
22
+ labels: labels || [],
23
+ completed: false,
24
+ date_created: new Date().toISOString(),
25
+ subtasks: []
26
+ })
27
+ }
28
+
29
+ const completeTask = async (itemId) => {
30
+ return await updateListItem(itemId, {
31
+ 'content.completed': true,
32
+ 'content.date_completed': new Date().toISOString()
33
+ })
34
+ }
35
+
36
+ const uncompleteTask = async (itemId) => {
37
+ return await updateListItem(itemId, {
38
+ 'content.completed': false,
39
+ 'content.date_completed': null
40
+ })
41
+ }
42
+
43
+ const updateTaskPriority = async (itemId, priority) => {
44
+ return await updateListItem(itemId, {
45
+ 'content.priority': priority
46
+ })
47
+ }
48
+
49
+ const updateTaskDueDate = async (itemId, dueDate) => {
50
+ return await updateListItem(itemId, {
51
+ 'content.due_date': dueDate
52
+ })
53
+ }
54
+
55
+ const addSubtask = async (itemId, subtaskTitle) => {
56
+ const { getListById } = useLists()
57
+ const item = await getListById(itemId)
58
+ const subtasks = item.content.subtasks || []
59
+
60
+ subtasks.push({
61
+ id: Date.now(),
62
+ title: subtaskTitle,
63
+ completed: false,
64
+ date_created: new Date().toISOString()
65
+ })
66
+
67
+ return await updateListItem(itemId, {
68
+ 'content.subtasks': subtasks
69
+ })
70
+ }
71
+
72
+ const getUserTaskLists = async () => {
73
+ return await getUserLists('tasks')
74
+ }
75
+
76
+ return {
77
+ createTaskList,
78
+ addTask,
79
+ completeTask,
80
+ uncompleteTask,
81
+ updateTaskPriority,
82
+ updateTaskDueDate,
83
+ addSubtask,
84
+ getUserTaskLists
85
+ }
86
+ }
@@ -0,0 +1,51 @@
1
+ export const useWishlist = () => {
2
+ const { createList, addToList, getUserLists, removeFromList } = useLists()
3
+
4
+ const createWishlist = async (name = 'My Wishlist', description = '') => {
5
+ return await createList({
6
+ name,
7
+ description,
8
+ type: 'wishlist',
9
+ visibility: 'private'
10
+ })
11
+ }
12
+
13
+ const addToWishlist = async (wishlistId, itemData) => {
14
+ const { title, url, price, image, description, category } = itemData
15
+
16
+ return await addToList(wishlistId, {
17
+ type: 'product',
18
+ title,
19
+ url,
20
+ price,
21
+ image,
22
+ description,
23
+ category,
24
+ date_added: new Date().toISOString(),
25
+ priority: itemData.priority || 'medium'
26
+ })
27
+ }
28
+
29
+ const getUserWishlists = async () => {
30
+ return await getUserLists('wishlist')
31
+ }
32
+
33
+ const removeFromWishlist = async (itemId) => {
34
+ return await removeFromList(itemId)
35
+ }
36
+
37
+ const updateWishlistItemPriority = async (itemId, priority) => {
38
+ const { updateListItem } = useLists()
39
+ return await updateListItem(itemId, {
40
+ 'content.priority': priority
41
+ })
42
+ }
43
+
44
+ return {
45
+ createWishlist,
46
+ addToWishlist,
47
+ getUserWishlists,
48
+ removeFromWishlist,
49
+ updateWishlistItemPriority
50
+ }
51
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ defineAlternateModule,
3
+ useAlternateEventBus,
4
+ useAlternateContext,
5
+ type AlternateContext,
6
+ type ListsAdapter
7
+ } from '@meeovi/core'
8
+
9
+ import { getListsConfig } from './config'
10
+ import { registerListsProviderRuntime } from './registry'
11
+
12
+ export default defineAlternateModule({
13
+ id: 'lists',
14
+ adapters: {},
15
+
16
+ async setup(ctx: AlternateContext) {
17
+ const bus = useAlternateEventBus()
18
+ const config = getListsConfig()
19
+
20
+ // If a core adapter was registered under the `lists` key, register it
21
+ // into the local provider registry so UI code can consume it via `useLists()`.
22
+ // If a core adapter was registered under the `lists` key, adapt it
23
+ // into the `ListsProvider` shape and register it into the local registry.
24
+ try {
25
+ const runtimeAdapter = ctx.getAdapter('lists' as any) as ListsAdapter | undefined
26
+ if (runtimeAdapter) {
27
+ const provider = {
28
+ getList: (id: string) => runtimeAdapter.getList(id),
29
+ listLists: (params?: Record<string, unknown>) => runtimeAdapter.listLists(params),
30
+ createList: (data: Partial<Record<string, unknown>>) => runtimeAdapter.createList(data),
31
+ updateList: (id: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateList(id, data),
32
+ deleteList: (id: string) => runtimeAdapter.deleteList(id),
33
+
34
+ addItem: (listId: string, item: Partial<Record<string, unknown>>) => runtimeAdapter.addItem(listId, item),
35
+ updateItem: (listId: string, itemId: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateItem(listId, itemId, data),
36
+ deleteItem: (listId: string, itemId: string) => runtimeAdapter.deleteItem(listId, itemId),
37
+
38
+ reorderItems: runtimeAdapter.reorderItems ? (listId: string, itemIds: string[]) => runtimeAdapter.reorderItems!(listId, itemIds) : undefined
39
+ }
40
+
41
+ registerListsProviderRuntime('core', provider)
42
+ }
43
+ } catch (e) {
44
+ // noop
45
+ }
46
+
47
+ // Listen for runtime adapter registrations from ModuleRegistry
48
+ bus.on('adapter:registered' as any, (payload: any) => {
49
+ try {
50
+ if (payload?.key === 'lists') {
51
+ const runtimeAdapter = ctx.getAdapter('lists' as any) as ListsAdapter | undefined
52
+ if (runtimeAdapter) {
53
+ const provider = {
54
+ getList: (id: string) => runtimeAdapter.getList(id),
55
+ listLists: (params?: Record<string, unknown>) => runtimeAdapter.listLists(params),
56
+ createList: (data: Partial<Record<string, unknown>>) => runtimeAdapter.createList(data),
57
+ updateList: (id: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateList(id, data),
58
+ deleteList: (id: string) => runtimeAdapter.deleteList(id),
59
+
60
+ addItem: (listId: string, item: Partial<Record<string, unknown>>) => runtimeAdapter.addItem(listId, item),
61
+ updateItem: (listId: string, itemId: string, data: Partial<Record<string, unknown>>) => runtimeAdapter.updateItem(listId, itemId, data),
62
+ deleteItem: (listId: string, itemId: string) => runtimeAdapter.deleteItem(listId, itemId),
63
+
64
+ reorderItems: runtimeAdapter.reorderItems ? (listId: string, itemIds: string[]) => runtimeAdapter.reorderItems!(listId, itemIds) : undefined
65
+ }
66
+
67
+ registerListsProviderRuntime('core', provider)
68
+ }
69
+ }
70
+ } catch (e) {
71
+ /* noop */
72
+ }
73
+ })
74
+ }
75
+ })
@@ -0,0 +1,145 @@
1
+ import { registerListsProvider } from '../registry'
2
+ import type { ListsProvider } from '../types'
3
+ import { getListsConfig } from '../config'
4
+
5
+ async function directusFetch(path: string, options: RequestInit = {}) {
6
+ const cfg = getListsConfig()
7
+ const base = cfg.baseUrl?.replace(/\/$/, '') || ''
8
+
9
+ const res = await fetch(`${base}${path}`, {
10
+ ...options,
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ ...(cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}),
14
+ ...(options.headers || {})
15
+ }
16
+ })
17
+
18
+ if (!res.ok) {
19
+ const err: any = new Error(`Directus error: ${res.status}`)
20
+ err.status = res.status
21
+ err.response = res
22
+ throw err
23
+ }
24
+
25
+ return res.json()
26
+ }
27
+
28
+ const DirectusListsProvider: ListsProvider = {
29
+ async getList(id) {
30
+ const json = await directusFetch(`/lists/${id}`)
31
+ return json.list ?? json
32
+ },
33
+
34
+ async listLists() {
35
+ const json = await directusFetch(`/lists`)
36
+ return json.lists ?? json
37
+ },
38
+
39
+ async createList(data) {
40
+ const json = await directusFetch(`/lists`, { method: 'POST', body: JSON.stringify(data) })
41
+ return json.list ?? json
42
+ },
43
+
44
+ async updateList(id, data) {
45
+ const json = await directusFetch(`/lists/${id}`, { method: 'PUT', body: JSON.stringify(data) })
46
+ return json.list ?? json
47
+ },
48
+
49
+ async deleteList(id) {
50
+ await directusFetch(`/lists/${id}`, { method: 'DELETE' })
51
+ },
52
+
53
+ async addItem(listId, item) {
54
+ const json = await directusFetch(`/lists/${listId}/items`, { method: 'POST', body: JSON.stringify(item) })
55
+ return json.item ?? json
56
+ },
57
+
58
+ async updateItem(listId, itemId, data) {
59
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify(data) })
60
+ return json.item ?? json
61
+ },
62
+
63
+ async deleteItem(listId, itemId) {
64
+ await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'DELETE' })
65
+ },
66
+
67
+ async reorderItems(listId, itemIds) {
68
+ await directusFetch(`/lists/${listId}/reorder`, { method: 'POST', body: JSON.stringify({ items: itemIds }) })
69
+ },
70
+
71
+ async toggleComplete(listId, itemId, completed = true) {
72
+ try {
73
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/toggle`, { method: 'POST', body: JSON.stringify({ completed }) })
74
+ return json.item ?? json
75
+ } catch (e) {
76
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ completed }) })
77
+ return json.item ?? json
78
+ }
79
+ },
80
+
81
+ async setDueDate(listId, itemId, dueDate) {
82
+ try {
83
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/due`, { method: 'POST', body: JSON.stringify({ dueDate }) })
84
+ return json.item ?? json
85
+ } catch (e) {
86
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { dueDate } }) })
87
+ return json.item ?? json
88
+ }
89
+ },
90
+
91
+ async setReminder(listId, itemId, reminder) {
92
+ try {
93
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/reminder`, { method: 'POST', body: JSON.stringify({ reminder }) })
94
+ return json.item ?? json
95
+ } catch (e) {
96
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { reminder } }) })
97
+ return json.item ?? json
98
+ }
99
+ },
100
+
101
+ async setPriority(listId, itemId, priority) {
102
+ try {
103
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}/priority`, { method: 'POST', body: JSON.stringify({ priority }) })
104
+ return json.item ?? json
105
+ } catch (e) {
106
+ const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { priority } }) })
107
+ return json.item ?? json
108
+ }
109
+ },
110
+
111
+ async shareList(listId, userId, role = 'editor') {
112
+ try {
113
+ await directusFetch(`/lists/${listId}/share`, { method: 'POST', body: JSON.stringify({ userId, role }) })
114
+ } catch (e) {
115
+ const json = await directusFetch(`/lists/${listId}`)
116
+ const list = json.list ?? json
117
+ const collaborators = (list.metadata?.collaborators || []).concat({ userId, role })
118
+ await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), collaborators } }) })
119
+ }
120
+ },
121
+
122
+ async searchItems(listId, query) {
123
+ try {
124
+ const json = await directusFetch(`/lists/${listId}/search?q=${encodeURIComponent(String(query))}`)
125
+ return json.items ?? json
126
+ } catch (e) {
127
+ const json = await directusFetch(`/lists/${listId}`)
128
+ const list = json.list ?? json
129
+ const q = String(query).toLowerCase()
130
+ return list.items.filter((i: any) => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q))
131
+ }
132
+ },
133
+
134
+ async archiveList(listId) {
135
+ try {
136
+ await directusFetch(`/lists/${listId}/archive`, { method: 'POST' })
137
+ } catch (e) {
138
+ const json = await directusFetch(`/lists/${listId}`)
139
+ const list = json.list ?? json
140
+ await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), archived: true } }) })
141
+ }
142
+ }
143
+ }
144
+
145
+ registerListsProvider('directus', DirectusListsProvider)
@@ -0,0 +1,127 @@
1
+ import { registerListsProvider } from '../registry'
2
+ import type { List, ListItem, ListsProvider } from '../types'
3
+ import { nanoid } from 'nanoid'
4
+
5
+ const lists = new Map<string, List>()
6
+
7
+ const MemoryListsProvider: ListsProvider = {
8
+ async getList(id) {
9
+ const list = lists.get(id)
10
+ if (!list) throw new Error(`List ${id} not found`)
11
+ return list
12
+ },
13
+
14
+ async listLists() {
15
+ return Array.from(lists.values())
16
+ },
17
+
18
+ async createList(data) {
19
+ const id = nanoid()
20
+ const list: List = {
21
+ id,
22
+ title: data.title || 'Untitled List',
23
+ type: data.type || 'list',
24
+ items: [],
25
+ metadata: data.metadata || {},
26
+ createdAt: new Date().toISOString(),
27
+ updatedAt: new Date().toISOString()
28
+ }
29
+ lists.set(id, list)
30
+ return list
31
+ },
32
+
33
+ async updateList(id, data) {
34
+ const list = await this.getList(id)
35
+ const updated = {
36
+ ...list,
37
+ ...data,
38
+ updatedAt: new Date().toISOString()
39
+ }
40
+ lists.set(id, updated)
41
+ return updated
42
+ },
43
+
44
+ async deleteList(id) {
45
+ lists.delete(id)
46
+ },
47
+
48
+ async addItem(listId, item) {
49
+ const list = await this.getList(listId)
50
+ const newItem: ListItem = {
51
+ id: nanoid(),
52
+ title: item.title || '',
53
+ description: item.description,
54
+ completed: item.completed || false,
55
+ position: list.items.length,
56
+ parentId: item.parentId,
57
+ metadata: item.metadata || {},
58
+ createdAt: new Date().toISOString(),
59
+ updatedAt: new Date().toISOString()
60
+ }
61
+ list.items.push(newItem)
62
+ return newItem
63
+ },
64
+
65
+ async updateItem(listId, itemId, data) {
66
+ const list = await this.getList(listId)
67
+ const item = list.items.find(i => i.id === itemId)
68
+ if (!item) throw new Error(`Item ${itemId} not found`)
69
+ Object.assign(item, data, { updatedAt: new Date().toISOString() })
70
+ return item
71
+ },
72
+
73
+ async deleteItem(listId, itemId) {
74
+ const list = await this.getList(listId)
75
+ list.items = list.items.filter(i => i.id !== itemId)
76
+ },
77
+
78
+ async reorderItems(listId, itemIds) {
79
+ const list = await this.getList(listId)
80
+ const newOrder = itemIds.map(id => list.items.find(i => i.id === id)!)
81
+ list.items = newOrder.map((item, index) => ({
82
+ ...item,
83
+ position: index
84
+ }))
85
+ },
86
+
87
+ async toggleComplete(listId, itemId, completed = true) {
88
+ const item = await this.updateItem(listId, itemId, { completed })
89
+ return item
90
+ },
91
+
92
+ async setDueDate(listId, itemId, dueDate) {
93
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), dueDate } })
94
+ return item
95
+ },
96
+
97
+ async setReminder(listId, itemId, reminder) {
98
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), reminder } })
99
+ return item
100
+ },
101
+
102
+ async setPriority(listId, itemId, priority) {
103
+ const item = await this.updateItem(listId, itemId, { metadata: { ...( (await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {} ), priority } })
104
+ return item
105
+ },
106
+
107
+ async shareList(listId, userId, role = 'editor') {
108
+ // memory store: attach collaborators in list.metadata.collaborators
109
+ const list = await this.getList(listId)
110
+ const collaborators = (list.metadata?.collaborators as any[]) || []
111
+ collaborators.push({ userId, role })
112
+ list.metadata = { ...(list.metadata || {}), collaborators }
113
+ },
114
+
115
+ async searchItems(listId, query) {
116
+ const list = await this.getList(listId)
117
+ const q = String(query).toLowerCase()
118
+ return list.items.filter(i => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q))
119
+ },
120
+
121
+ async archiveList(listId) {
122
+ const list = await this.getList(listId)
123
+ list.metadata = { ...(list.metadata || {}), archived: true }
124
+ }
125
+ }
126
+
127
+ registerListsProvider('memory', MemoryListsProvider)
@@ -0,0 +1,18 @@
1
+ import type { ListsProvider } from './types'
2
+
3
+ const providers: Record<string, ListsProvider> = {}
4
+
5
+ export function registerListsProvider(name: string, provider: ListsProvider) {
6
+ providers[name] = provider
7
+ }
8
+
9
+ // Allow runtime registration from other modules (e.g. via core ModuleRegistry adapters)
10
+ export function registerListsProviderRuntime(name: string, provider: ListsProvider) {
11
+ registerListsProvider(name, provider)
12
+ }
13
+
14
+ export function getListsProvider(name: string): ListsProvider {
15
+ const provider = providers[name]
16
+ if (!provider) throw new Error(`Lists provider "${name}" not found`)
17
+ return provider
18
+ }
@@ -0,0 +1,44 @@
1
+ export interface ListItem {
2
+ id: string
3
+ title: string
4
+ description?: string
5
+ completed?: boolean
6
+ position?: number
7
+ parentId?: string
8
+ metadata?: Record<string, any>
9
+ createdAt?: string
10
+ updatedAt?: string
11
+ }
12
+
13
+ export interface List {
14
+ id: string
15
+ title: string
16
+ type: 'checklist' | 'kanban' | 'list' | string
17
+ items: ListItem[]
18
+ metadata?: Record<string, any>
19
+ createdAt?: string
20
+ updatedAt?: string
21
+ }
22
+
23
+ export interface ListsProvider {
24
+ getList(id: string): Promise<List>
25
+ listLists(params?: Record<string, any>): Promise<List[]>
26
+ createList(data: Partial<List>): Promise<List>
27
+ updateList(id: string, data: Partial<List>): Promise<List>
28
+ deleteList(id: string): Promise<void>
29
+
30
+ addItem(listId: string, item: Partial<ListItem>): Promise<ListItem>
31
+ updateItem(listId: string, itemId: string, data: Partial<ListItem>): Promise<ListItem>
32
+ deleteItem(listId: string, itemId: string): Promise<void>
33
+
34
+ reorderItems?(listId: string, itemIds: string[]): Promise<void>
35
+
36
+ // Optional advanced features commonly found in task apps
37
+ toggleComplete?(listId: string, itemId: string, completed: boolean): Promise<ListItem>
38
+ setDueDate?(listId: string, itemId: string, dueDate: string | null): Promise<ListItem>
39
+ setReminder?(listId: string, itemId: string, reminder: string | null): Promise<ListItem>
40
+ setPriority?(listId: string, itemId: string, priority: number | null): Promise<ListItem>
41
+ shareList?(listId: string, userId: string, role?: string): Promise<void>
42
+ searchItems?(listId: string, query: string): Promise<ListItem[]>
43
+ archiveList?(listId: string): Promise<void>
44
+ }
@@ -0,0 +1,20 @@
1
+ import { getListsConfig } from './config'
2
+ import { getListsProvider } from './registry'
3
+
4
+ export function useLists() {
5
+ const { provider } = getListsConfig()
6
+ const lists = getListsProvider(provider)
7
+
8
+ return {
9
+ getList: lists.getList,
10
+ listLists: lists.listLists,
11
+ createList: lists.createList,
12
+ updateList: lists.updateList,
13
+ deleteList: lists.deleteList,
14
+
15
+ addItem: lists.addItem,
16
+ updateItem: lists.updateItem,
17
+ deleteItem: lists.deleteItem,
18
+ reorderItems: lists.reorderItems
19
+ }
20
+ }
@@ -0,0 +1,16 @@
1
+ import { getListsProvider } from '../registry'
2
+ import { getListsConfig } from '../config'
3
+
4
+ export async function checkListsProviderHealth(providerName?: string) {
5
+ try {
6
+ const cfg = getListsConfig()
7
+ const name = providerName || cfg.provider || 'directus'
8
+ const provider = getListsProvider(name)
9
+ const lists = await provider.listLists()
10
+ return { ok: true, count: Array.isArray(lists) ? lists.length : null }
11
+ } catch (e: any) {
12
+ return { ok: false, error: e?.message || String(e) }
13
+ }
14
+ }
15
+
16
+ export default checkListsProviderHealth
@@ -0,0 +1,42 @@
1
+ import type { List, ListItem } from '../types'
2
+
3
+ /**
4
+ * Normalize a raw provider list into Meeovi's List shape.
5
+ */
6
+ export function transformList(raw: any): List {
7
+ return {
8
+ id: raw.id,
9
+ title: raw.title ?? raw.name ?? 'Untitled',
10
+ type: raw.type ?? 'list',
11
+ items: Array.isArray(raw.items)
12
+ ? raw.items.map(transformItem)
13
+ : [],
14
+ metadata: raw.metadata ?? {},
15
+ createdAt: raw.createdAt ?? raw.created_at ?? null,
16
+ updatedAt: raw.updatedAt ?? raw.updated_at ?? null
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Normalize a raw provider item into Meeovi's ListItem shape.
22
+ */
23
+ export function transformItem(raw: any): ListItem {
24
+ return {
25
+ id: raw.id,
26
+ title: raw.title ?? raw.name ?? '',
27
+ description: raw.description ?? raw.body ?? '',
28
+ completed: raw.completed ?? raw.done ?? false,
29
+ position: raw.position ?? raw.order ?? 0,
30
+ parentId: raw.parentId ?? raw.parent_id ?? null,
31
+ metadata: raw.metadata ?? {},
32
+ createdAt: raw.createdAt ?? raw.created_at ?? null,
33
+ updatedAt: raw.updatedAt ?? raw.updated_at ?? null
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Normalize arrays safely.
39
+ */
40
+ export function transformListArray(raw: any[]): List[] {
41
+ return raw.map(transformList)
42
+ }
@@ -0,0 +1,21 @@
1
+ import type { List, ListItem } from '../types'
2
+
3
+ export function validateListInput(data: Partial<List>) {
4
+ if (!data.title || typeof data.title !== 'string') {
5
+ throw new Error('List title is required and must be a string')
6
+ }
7
+
8
+ if (data.type && typeof data.type !== 'string') {
9
+ throw new Error('List type must be a string')
10
+ }
11
+ }
12
+
13
+ export function validateItemInput(data: Partial<ListItem>) {
14
+ if (!data.title || typeof data.title !== 'string') {
15
+ throw new Error('Item title is required and must be a string')
16
+ }
17
+
18
+ if (data.completed !== undefined && typeof data.completed !== 'boolean') {
19
+ throw new Error('Item completed must be a boolean')
20
+ }
21
+ }