@meeovi/layer-lists 1.0.2 → 1.0.3
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/README.md +111 -0
- package/app/composables/config.ts +1 -1
- package/app/composables/module.ts +75 -0
- package/app/composables/providers/directus.ts +112 -16
- package/app/composables/providers/memory.ts +39 -0
- package/app/composables/registry.ts +5 -0
- package/app/composables/types.ts +9 -0
- package/app/composables/utils/health.ts +16 -0
- package/dist/app/composables/config.d.ts +7 -0
- package/dist/app/composables/config.js +9 -0
- package/dist/app/composables/globals/useDirectusForm.d.ts +4 -0
- package/dist/app/composables/globals/useDirectusForm.js +10 -0
- package/dist/app/composables/module.d.ts +7 -0
- package/dist/app/composables/module.js +60 -0
- package/dist/app/composables/providers/atproto.d.ts +1 -0
- package/dist/app/composables/providers/atproto.js +133 -0
- package/dist/app/composables/providers/directus.d.ts +1 -0
- package/dist/app/composables/providers/directus.js +130 -0
- package/dist/app/composables/providers/memory.d.ts +1 -0
- package/dist/app/composables/providers/memory.js +110 -0
- package/dist/app/composables/registry.d.ts +4 -0
- package/dist/app/composables/registry.js +14 -0
- package/dist/app/composables/types.d.ts +38 -0
- package/dist/app/composables/types.js +1 -0
- package/dist/app/composables/useLists.d.ts +11 -0
- package/dist/app/composables/useLists.js +17 -0
- package/dist/app/composables/utils/health.d.ts +10 -0
- package/dist/app/composables/utils/health.js +15 -0
- package/dist/app/composables/utils/transforms.d.ts +13 -0
- package/dist/app/composables/utils/transforms.js +38 -0
- package/dist/app/composables/utils/validation.d.ts +3 -0
- package/dist/app/composables/utils/validation.js +16 -0
- package/dist/layers/lists/app/composables/config.d.ts +7 -0
- package/dist/layers/lists/app/composables/config.js +9 -0
- package/dist/layers/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
- package/dist/layers/lists/app/composables/globals/useDirectusForm.js +1 -0
- package/dist/layers/lists/app/composables/module.d.ts +7 -0
- package/dist/layers/lists/app/composables/module.js +60 -0
- package/dist/layers/lists/app/composables/providers/atproto.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/atproto.js +133 -0
- package/dist/layers/lists/app/composables/providers/directus.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/directus.js +130 -0
- package/dist/layers/lists/app/composables/providers/memory.d.ts +1 -0
- package/dist/layers/lists/app/composables/providers/memory.js +110 -0
- package/dist/layers/lists/app/composables/registry.d.ts +4 -0
- package/dist/layers/lists/app/composables/registry.js +14 -0
- package/dist/layers/lists/app/composables/types.d.ts +38 -0
- package/dist/layers/lists/app/composables/types.js +1 -0
- package/dist/layers/lists/app/composables/useLists.d.ts +11 -0
- package/dist/layers/lists/app/composables/useLists.js +17 -0
- package/dist/layers/lists/app/composables/utils/health.d.ts +10 -0
- package/dist/layers/lists/app/composables/utils/health.js +15 -0
- package/dist/layers/lists/app/composables/utils/transforms.d.ts +13 -0
- package/dist/layers/lists/app/composables/utils/transforms.js +38 -0
- package/dist/layers/lists/app/composables/utils/validation.d.ts +3 -0
- package/dist/layers/lists/app/composables/utils/validation.js +16 -0
- package/dist/layers/lists/nuxt.config.d.ts +2 -0
- package/dist/layers/lists/nuxt.config.js +7 -0
- package/dist/layers/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
- package/dist/layers/shared/app/composables/globals/useDirectusForm.js +66 -0
- package/dist/lists/app/composables/config.d.ts +7 -0
- package/dist/lists/app/composables/config.js +9 -0
- package/dist/lists/app/composables/globals/useDirectusForm.d.ts +1 -0
- package/dist/lists/app/composables/globals/useDirectusForm.js +1 -0
- package/dist/lists/app/composables/module.d.ts +7 -0
- package/dist/lists/app/composables/module.js +60 -0
- package/dist/lists/app/composables/providers/atproto.d.ts +1 -0
- package/dist/lists/app/composables/providers/atproto.js +133 -0
- package/dist/lists/app/composables/providers/directus.d.ts +1 -0
- package/dist/lists/app/composables/providers/directus.js +130 -0
- package/dist/lists/app/composables/providers/memory.d.ts +1 -0
- package/dist/lists/app/composables/providers/memory.js +110 -0
- package/dist/lists/app/composables/registry.d.ts +4 -0
- package/dist/lists/app/composables/registry.js +14 -0
- package/dist/lists/app/composables/types.d.ts +38 -0
- package/dist/lists/app/composables/types.js +1 -0
- package/dist/lists/app/composables/useLists.d.ts +11 -0
- package/dist/lists/app/composables/useLists.js +17 -0
- package/dist/lists/app/composables/utils/health.d.ts +10 -0
- package/dist/lists/app/composables/utils/health.js +15 -0
- package/dist/lists/app/composables/utils/transforms.d.ts +13 -0
- package/dist/lists/app/composables/utils/transforms.js +38 -0
- package/dist/lists/app/composables/utils/validation.d.ts +3 -0
- package/dist/lists/app/composables/utils/validation.js +16 -0
- package/dist/lists/nuxt.config.d.ts +2 -0
- package/dist/lists/nuxt.config.js +7 -0
- package/dist/nuxt.config.d.ts +2 -0
- package/dist/nuxt.config.js +7 -0
- package/dist/packages/core/index.d.ts +17 -0
- package/dist/packages/core/index.js +18 -0
- package/dist/packages/core/src/adapters/auth.d.ts +9 -0
- package/dist/packages/core/src/adapters/auth.js +1 -0
- package/dist/packages/core/src/adapters/cart.d.ts +22 -0
- package/dist/packages/core/src/adapters/cart.js +1 -0
- package/dist/packages/core/src/adapters/catalog.d.ts +17 -0
- package/dist/packages/core/src/adapters/catalog.js +1 -0
- package/dist/packages/core/src/adapters/common.d.ts +9 -0
- package/dist/packages/core/src/adapters/common.js +1 -0
- package/dist/packages/core/src/adapters/lists.d.ts +17 -0
- package/dist/packages/core/src/adapters/lists.js +1 -0
- package/dist/packages/core/src/adapters/search.d.ts +21 -0
- package/dist/packages/core/src/adapters/search.js +1 -0
- package/dist/packages/core/src/plugins/defineAdapter.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineAdapter.js +3 -0
- package/dist/packages/core/src/plugins/defineModule.d.ts +2 -0
- package/dist/packages/core/src/plugins/defineModule.js +3 -0
- package/dist/packages/core/src/plugins/registry.d.ts +14 -0
- package/dist/packages/core/src/plugins/registry.js +46 -0
- package/dist/packages/core/src/runtime/app.d.ts +2 -0
- package/dist/packages/core/src/runtime/app.js +20 -0
- package/dist/packages/core/src/runtime/config.d.ts +7 -0
- package/dist/packages/core/src/runtime/config.js +9 -0
- package/dist/packages/core/src/runtime/context.d.ts +9 -0
- package/dist/packages/core/src/runtime/context.js +11 -0
- package/dist/packages/core/src/runtime/hooks.d.ts +5 -0
- package/dist/packages/core/src/runtime/hooks.js +18 -0
- package/dist/packages/core/src/runtime/lifecycle.d.ts +4 -0
- package/dist/packages/core/src/runtime/lifecycle.js +5 -0
- package/dist/packages/core/src/types/adapters.d.ts +14 -0
- package/dist/packages/core/src/types/adapters.js +1 -0
- package/dist/packages/core/src/types/app.d.ts +13 -0
- package/dist/packages/core/src/types/app.js +1 -0
- package/dist/packages/core/src/types/config.d.ts +8 -0
- package/dist/packages/core/src/types/config.js +1 -0
- package/dist/packages/core/src/types/events.d.ts +20 -0
- package/dist/packages/core/src/types/events.js +22 -0
- package/dist/packages/core/src/types/module.d.ts +15 -0
- package/dist/packages/core/src/types/module.js +1 -0
- package/dist/shared/app/composables/globals/useDirectusForm.d.ts +10 -0
- package/dist/shared/app/composables/globals/useDirectusForm.js +66 -0
- package/global.d.ts +1 -0
- package/package.json +4 -1
- package/shims.d.ts +7 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ref, provide } from 'vue';
|
|
2
|
+
export function useDirectusForm(collectionName, fieldsRef, opts) {
|
|
3
|
+
const form = ref({});
|
|
4
|
+
const formError = ref(null);
|
|
5
|
+
const formSuccess = ref(null);
|
|
6
|
+
// provide form context for DirectusFormElement children
|
|
7
|
+
provide('directusForm', {
|
|
8
|
+
form,
|
|
9
|
+
fields: fieldsRef,
|
|
10
|
+
});
|
|
11
|
+
const submitForm = async () => {
|
|
12
|
+
formError.value = null;
|
|
13
|
+
formSuccess.value = null;
|
|
14
|
+
// Validate form data against field validation rules
|
|
15
|
+
for (const field of fieldsRef.value) {
|
|
16
|
+
if (field.meta?.validation) {
|
|
17
|
+
try {
|
|
18
|
+
const validation = field.meta.validation;
|
|
19
|
+
if (validation._and) {
|
|
20
|
+
for (const rule of validation._and) {
|
|
21
|
+
const fieldName = Object.keys(rule)[0];
|
|
22
|
+
if (!fieldName)
|
|
23
|
+
continue;
|
|
24
|
+
const ruleDef = rule[fieldName];
|
|
25
|
+
if (ruleDef && ruleDef._regex) {
|
|
26
|
+
const regex = new RegExp(ruleDef._regex);
|
|
27
|
+
const valueToTest = String(form.value[field.field] ?? '');
|
|
28
|
+
if (!regex.test(valueToTest)) {
|
|
29
|
+
formError.value = field.meta.validation_message || field.meta.field + ' failed validation';
|
|
30
|
+
console.error(`Validation failed for ${field.field}: ${field.meta.validation_message || 'Invalid format'}`);
|
|
31
|
+
return; // Stop submission if validation fails
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.error(`Error parsing validation for ${field.field}:`, err);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const { $directus, $createItem } = useNuxtApp();
|
|
43
|
+
const result = await $directus.request($createItem(collectionName, form.value));
|
|
44
|
+
if (result.error) {
|
|
45
|
+
formError.value = result.error.message;
|
|
46
|
+
console.error(`Error creating ${collectionName}:`, result.error);
|
|
47
|
+
return; // Stop submission if error occurs
|
|
48
|
+
}
|
|
49
|
+
formSuccess.value = `${collectionName} created successfully`;
|
|
50
|
+
if (opts?.clearOnSuccess) {
|
|
51
|
+
form.value = {};
|
|
52
|
+
}
|
|
53
|
+
if (opts?.closeDialogRef) {
|
|
54
|
+
try {
|
|
55
|
+
opts.closeDialogRef.value = false;
|
|
56
|
+
}
|
|
57
|
+
catch (e) { }
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
form,
|
|
62
|
+
formError,
|
|
63
|
+
formSuccess,
|
|
64
|
+
submitForm,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '#shared/app/composables/globals/useDirectusForm';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '#shared/app/composables/globals/useDirectusForm';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { defineAlternateModule, useAlternateEventBus } from '@meeovi/core';
|
|
2
|
+
import { getListsConfig } from './config';
|
|
3
|
+
import { registerListsProviderRuntime } from './registry';
|
|
4
|
+
export default defineAlternateModule({
|
|
5
|
+
id: 'lists',
|
|
6
|
+
adapters: {},
|
|
7
|
+
async setup(ctx) {
|
|
8
|
+
const bus = useAlternateEventBus();
|
|
9
|
+
const config = getListsConfig();
|
|
10
|
+
// If a core adapter was registered under the `lists` key, register it
|
|
11
|
+
// into the local provider registry so UI code can consume it via `useLists()`.
|
|
12
|
+
// If a core adapter was registered under the `lists` key, adapt it
|
|
13
|
+
// into the `ListsProvider` shape and register it into the local registry.
|
|
14
|
+
try {
|
|
15
|
+
const runtimeAdapter = ctx.getAdapter('lists');
|
|
16
|
+
if (runtimeAdapter) {
|
|
17
|
+
const provider = {
|
|
18
|
+
getList: (id) => runtimeAdapter.getList(id),
|
|
19
|
+
listLists: (params) => runtimeAdapter.listLists(params),
|
|
20
|
+
createList: (data) => runtimeAdapter.createList(data),
|
|
21
|
+
updateList: (id, data) => runtimeAdapter.updateList(id, data),
|
|
22
|
+
deleteList: (id) => runtimeAdapter.deleteList(id),
|
|
23
|
+
addItem: (listId, item) => runtimeAdapter.addItem(listId, item),
|
|
24
|
+
updateItem: (listId, itemId, data) => runtimeAdapter.updateItem(listId, itemId, data),
|
|
25
|
+
deleteItem: (listId, itemId) => runtimeAdapter.deleteItem(listId, itemId),
|
|
26
|
+
reorderItems: runtimeAdapter.reorderItems ? (listId, itemIds) => runtimeAdapter.reorderItems(listId, itemIds) : undefined
|
|
27
|
+
};
|
|
28
|
+
registerListsProviderRuntime('core', provider);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
// noop
|
|
33
|
+
}
|
|
34
|
+
// Listen for runtime adapter registrations from ModuleRegistry
|
|
35
|
+
bus.on('adapter:registered', (payload) => {
|
|
36
|
+
try {
|
|
37
|
+
if (payload?.key === 'lists') {
|
|
38
|
+
const runtimeAdapter = ctx.getAdapter('lists');
|
|
39
|
+
if (runtimeAdapter) {
|
|
40
|
+
const provider = {
|
|
41
|
+
getList: (id) => runtimeAdapter.getList(id),
|
|
42
|
+
listLists: (params) => runtimeAdapter.listLists(params),
|
|
43
|
+
createList: (data) => runtimeAdapter.createList(data),
|
|
44
|
+
updateList: (id, data) => runtimeAdapter.updateList(id, data),
|
|
45
|
+
deleteList: (id) => runtimeAdapter.deleteList(id),
|
|
46
|
+
addItem: (listId, item) => runtimeAdapter.addItem(listId, item),
|
|
47
|
+
updateItem: (listId, itemId, data) => runtimeAdapter.updateItem(listId, itemId, data),
|
|
48
|
+
deleteItem: (listId, itemId) => runtimeAdapter.deleteItem(listId, itemId),
|
|
49
|
+
reorderItems: runtimeAdapter.reorderItems ? (listId, itemIds) => runtimeAdapter.reorderItems(listId, itemIds) : undefined
|
|
50
|
+
};
|
|
51
|
+
registerListsProviderRuntime('core', provider);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
/* noop */
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { wrapSocialRequest } from '@meeovi/social';
|
|
3
|
+
import { transformList, transformItem } from '../utils/transforms';
|
|
4
|
+
import { validateListInput, validateItemInput } from '../utils/validation';
|
|
5
|
+
import { getListsConfig } from '../config';
|
|
6
|
+
async function atprotoFetch(path, options = {}) {
|
|
7
|
+
const { baseUrl, apiKey } = getListsConfig();
|
|
8
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
9
|
+
...options,
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
13
|
+
...(options.headers || {})
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok) {
|
|
17
|
+
const error = new Error(`ATProto error: ${res.status}`);
|
|
18
|
+
error.status = res.status;
|
|
19
|
+
error.response = res;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
const AtprotoListsProvider = {
|
|
25
|
+
async getList(id) {
|
|
26
|
+
return wrapSocialRequest('atproto', async () => {
|
|
27
|
+
const data = await atprotoFetch(`/xrpc/app.bsky.graph.getList?list=${id}`);
|
|
28
|
+
return transformList(data.list);
|
|
29
|
+
}, {
|
|
30
|
+
cacheKey: `atproto:list:${id}`,
|
|
31
|
+
ttlMs: 1000 * 30,
|
|
32
|
+
retry: true,
|
|
33
|
+
swr: true
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
async listLists() {
|
|
37
|
+
return wrapSocialRequest('atproto', async () => {
|
|
38
|
+
const data = await atprotoFetch(`/xrpc/app.bsky.graph.getLists`);
|
|
39
|
+
return data.lists.map(transformList);
|
|
40
|
+
}, {
|
|
41
|
+
cacheKey: `atproto:lists`,
|
|
42
|
+
ttlMs: 1000 * 30,
|
|
43
|
+
retry: true,
|
|
44
|
+
swr: true
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
async createList(data) {
|
|
48
|
+
validateListInput(data);
|
|
49
|
+
return wrapSocialRequest('atproto', async () => {
|
|
50
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.createList`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
name: data.title,
|
|
54
|
+
purpose: data.type ?? 'list',
|
|
55
|
+
description: data.metadata?.description ?? ''
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
return transformList(result);
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
async updateList(id, data) {
|
|
62
|
+
validateListInput(data);
|
|
63
|
+
return wrapSocialRequest('atproto', async () => {
|
|
64
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.updateList`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
list: id,
|
|
68
|
+
name: data.title,
|
|
69
|
+
description: data.metadata?.description
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
return transformList(result);
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
async deleteList(id) {
|
|
76
|
+
return wrapSocialRequest('atproto', async () => {
|
|
77
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.deleteList`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: JSON.stringify({ list: id })
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
async addItem(listId, item) {
|
|
84
|
+
validateItemInput(item);
|
|
85
|
+
return wrapSocialRequest('atproto', async () => {
|
|
86
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.addListItem`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
list: listId,
|
|
90
|
+
subject: item.title // ATProto uses "subject" for list entries
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
return transformItem(result);
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
async updateItem(listId, itemId, data) {
|
|
97
|
+
validateItemInput(data);
|
|
98
|
+
return wrapSocialRequest('atproto', async () => {
|
|
99
|
+
const result = await atprotoFetch(`/xrpc/app.bsky.graph.updateListItem`, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
list: listId,
|
|
103
|
+
item: itemId,
|
|
104
|
+
...data
|
|
105
|
+
})
|
|
106
|
+
});
|
|
107
|
+
return transformItem(result);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async deleteItem(listId, itemId) {
|
|
111
|
+
return wrapSocialRequest('atproto', async () => {
|
|
112
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.deleteListItem`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
list: listId,
|
|
116
|
+
item: itemId
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
async reorderItems(listId, itemIds) {
|
|
122
|
+
return wrapSocialRequest('atproto', async () => {
|
|
123
|
+
await atprotoFetch(`/xrpc/app.bsky.graph.reorderListItems`, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
list: listId,
|
|
127
|
+
items: itemIds
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
registerListsProvider('atproto', AtprotoListsProvider);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { getListsConfig } from '../config';
|
|
3
|
+
async function directusFetch(path, options = {}) {
|
|
4
|
+
const cfg = getListsConfig();
|
|
5
|
+
const base = cfg.baseUrl?.replace(/\/$/, '') || '';
|
|
6
|
+
const res = await fetch(`${base}${path}`, {
|
|
7
|
+
...options,
|
|
8
|
+
headers: {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
...(cfg.apiKey ? { Authorization: `Bearer ${cfg.apiKey}` } : {}),
|
|
11
|
+
...(options.headers || {})
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
const err = new Error(`Directus error: ${res.status}`);
|
|
16
|
+
err.status = res.status;
|
|
17
|
+
err.response = res;
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
return res.json();
|
|
21
|
+
}
|
|
22
|
+
const DirectusListsProvider = {
|
|
23
|
+
async getList(id) {
|
|
24
|
+
const json = await directusFetch(`/lists/${id}`);
|
|
25
|
+
return json.list ?? json;
|
|
26
|
+
},
|
|
27
|
+
async listLists() {
|
|
28
|
+
const json = await directusFetch(`/lists`);
|
|
29
|
+
return json.lists ?? json;
|
|
30
|
+
},
|
|
31
|
+
async createList(data) {
|
|
32
|
+
const json = await directusFetch(`/lists`, { method: 'POST', body: JSON.stringify(data) });
|
|
33
|
+
return json.list ?? json;
|
|
34
|
+
},
|
|
35
|
+
async updateList(id, data) {
|
|
36
|
+
const json = await directusFetch(`/lists/${id}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
37
|
+
return json.list ?? json;
|
|
38
|
+
},
|
|
39
|
+
async deleteList(id) {
|
|
40
|
+
await directusFetch(`/lists/${id}`, { method: 'DELETE' });
|
|
41
|
+
},
|
|
42
|
+
async addItem(listId, item) {
|
|
43
|
+
const json = await directusFetch(`/lists/${listId}/items`, { method: 'POST', body: JSON.stringify(item) });
|
|
44
|
+
return json.item ?? json;
|
|
45
|
+
},
|
|
46
|
+
async updateItem(listId, itemId, data) {
|
|
47
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
48
|
+
return json.item ?? json;
|
|
49
|
+
},
|
|
50
|
+
async deleteItem(listId, itemId) {
|
|
51
|
+
await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'DELETE' });
|
|
52
|
+
},
|
|
53
|
+
async reorderItems(listId, itemIds) {
|
|
54
|
+
await directusFetch(`/lists/${listId}/reorder`, { method: 'POST', body: JSON.stringify({ items: itemIds }) });
|
|
55
|
+
},
|
|
56
|
+
async toggleComplete(listId, itemId, completed = true) {
|
|
57
|
+
try {
|
|
58
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/toggle`, { method: 'POST', body: JSON.stringify({ completed }) });
|
|
59
|
+
return json.item ?? json;
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ completed }) });
|
|
63
|
+
return json.item ?? json;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
async setDueDate(listId, itemId, dueDate) {
|
|
67
|
+
try {
|
|
68
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/due`, { method: 'POST', body: JSON.stringify({ dueDate }) });
|
|
69
|
+
return json.item ?? json;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { dueDate } }) });
|
|
73
|
+
return json.item ?? json;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
async setReminder(listId, itemId, reminder) {
|
|
77
|
+
try {
|
|
78
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/reminder`, { method: 'POST', body: JSON.stringify({ reminder }) });
|
|
79
|
+
return json.item ?? json;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { reminder } }) });
|
|
83
|
+
return json.item ?? json;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async setPriority(listId, itemId, priority) {
|
|
87
|
+
try {
|
|
88
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}/priority`, { method: 'POST', body: JSON.stringify({ priority }) });
|
|
89
|
+
return json.item ?? json;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
const json = await directusFetch(`/lists/${listId}/items/${itemId}`, { method: 'PUT', body: JSON.stringify({ metadata: { priority } }) });
|
|
93
|
+
return json.item ?? json;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
async shareList(listId, userId, role = 'editor') {
|
|
97
|
+
try {
|
|
98
|
+
await directusFetch(`/lists/${listId}/share`, { method: 'POST', body: JSON.stringify({ userId, role }) });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
102
|
+
const list = json.list ?? json;
|
|
103
|
+
const collaborators = (list.metadata?.collaborators || []).concat({ userId, role });
|
|
104
|
+
await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), collaborators } }) });
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async searchItems(listId, query) {
|
|
108
|
+
try {
|
|
109
|
+
const json = await directusFetch(`/lists/${listId}/search?q=${encodeURIComponent(String(query))}`);
|
|
110
|
+
return json.items ?? json;
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
114
|
+
const list = json.list ?? json;
|
|
115
|
+
const q = String(query).toLowerCase();
|
|
116
|
+
return list.items.filter((i) => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q));
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
async archiveList(listId) {
|
|
120
|
+
try {
|
|
121
|
+
await directusFetch(`/lists/${listId}/archive`, { method: 'POST' });
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
const json = await directusFetch(`/lists/${listId}`);
|
|
125
|
+
const list = json.list ?? json;
|
|
126
|
+
await directusFetch(`/lists/${listId}`, { method: 'PUT', body: JSON.stringify({ metadata: { ...(list.metadata || {}), archived: true } }) });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
registerListsProvider('directus', DirectusListsProvider);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { registerListsProvider } from '../registry';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
const lists = new Map();
|
|
4
|
+
const MemoryListsProvider = {
|
|
5
|
+
async getList(id) {
|
|
6
|
+
const list = lists.get(id);
|
|
7
|
+
if (!list)
|
|
8
|
+
throw new Error(`List ${id} not found`);
|
|
9
|
+
return list;
|
|
10
|
+
},
|
|
11
|
+
async listLists() {
|
|
12
|
+
return Array.from(lists.values());
|
|
13
|
+
},
|
|
14
|
+
async createList(data) {
|
|
15
|
+
const id = nanoid();
|
|
16
|
+
const list = {
|
|
17
|
+
id,
|
|
18
|
+
title: data.title || 'Untitled List',
|
|
19
|
+
type: data.type || 'list',
|
|
20
|
+
items: [],
|
|
21
|
+
metadata: data.metadata || {},
|
|
22
|
+
createdAt: new Date().toISOString(),
|
|
23
|
+
updatedAt: new Date().toISOString()
|
|
24
|
+
};
|
|
25
|
+
lists.set(id, list);
|
|
26
|
+
return list;
|
|
27
|
+
},
|
|
28
|
+
async updateList(id, data) {
|
|
29
|
+
const list = await this.getList(id);
|
|
30
|
+
const updated = {
|
|
31
|
+
...list,
|
|
32
|
+
...data,
|
|
33
|
+
updatedAt: new Date().toISOString()
|
|
34
|
+
};
|
|
35
|
+
lists.set(id, updated);
|
|
36
|
+
return updated;
|
|
37
|
+
},
|
|
38
|
+
async deleteList(id) {
|
|
39
|
+
lists.delete(id);
|
|
40
|
+
},
|
|
41
|
+
async addItem(listId, item) {
|
|
42
|
+
const list = await this.getList(listId);
|
|
43
|
+
const newItem = {
|
|
44
|
+
id: nanoid(),
|
|
45
|
+
title: item.title || '',
|
|
46
|
+
description: item.description,
|
|
47
|
+
completed: item.completed || false,
|
|
48
|
+
position: list.items.length,
|
|
49
|
+
parentId: item.parentId,
|
|
50
|
+
metadata: item.metadata || {},
|
|
51
|
+
createdAt: new Date().toISOString(),
|
|
52
|
+
updatedAt: new Date().toISOString()
|
|
53
|
+
};
|
|
54
|
+
list.items.push(newItem);
|
|
55
|
+
return newItem;
|
|
56
|
+
},
|
|
57
|
+
async updateItem(listId, itemId, data) {
|
|
58
|
+
const list = await this.getList(listId);
|
|
59
|
+
const item = list.items.find(i => i.id === itemId);
|
|
60
|
+
if (!item)
|
|
61
|
+
throw new Error(`Item ${itemId} not found`);
|
|
62
|
+
Object.assign(item, data, { updatedAt: new Date().toISOString() });
|
|
63
|
+
return item;
|
|
64
|
+
},
|
|
65
|
+
async deleteItem(listId, itemId) {
|
|
66
|
+
const list = await this.getList(listId);
|
|
67
|
+
list.items = list.items.filter(i => i.id !== itemId);
|
|
68
|
+
},
|
|
69
|
+
async reorderItems(listId, itemIds) {
|
|
70
|
+
const list = await this.getList(listId);
|
|
71
|
+
const newOrder = itemIds.map(id => list.items.find(i => i.id === id));
|
|
72
|
+
list.items = newOrder.map((item, index) => ({
|
|
73
|
+
...item,
|
|
74
|
+
position: index
|
|
75
|
+
}));
|
|
76
|
+
},
|
|
77
|
+
async toggleComplete(listId, itemId, completed = true) {
|
|
78
|
+
const item = await this.updateItem(listId, itemId, { completed });
|
|
79
|
+
return item;
|
|
80
|
+
},
|
|
81
|
+
async setDueDate(listId, itemId, dueDate) {
|
|
82
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), dueDate } });
|
|
83
|
+
return item;
|
|
84
|
+
},
|
|
85
|
+
async setReminder(listId, itemId, reminder) {
|
|
86
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), reminder } });
|
|
87
|
+
return item;
|
|
88
|
+
},
|
|
89
|
+
async setPriority(listId, itemId, priority) {
|
|
90
|
+
const item = await this.updateItem(listId, itemId, { metadata: { ...((await this.getList(listId)).items.find(i => i.id === itemId)?.metadata || {}), priority } });
|
|
91
|
+
return item;
|
|
92
|
+
},
|
|
93
|
+
async shareList(listId, userId, role = 'editor') {
|
|
94
|
+
// memory store: attach collaborators in list.metadata.collaborators
|
|
95
|
+
const list = await this.getList(listId);
|
|
96
|
+
const collaborators = list.metadata?.collaborators || [];
|
|
97
|
+
collaborators.push({ userId, role });
|
|
98
|
+
list.metadata = { ...(list.metadata || {}), collaborators };
|
|
99
|
+
},
|
|
100
|
+
async searchItems(listId, query) {
|
|
101
|
+
const list = await this.getList(listId);
|
|
102
|
+
const q = String(query).toLowerCase();
|
|
103
|
+
return list.items.filter(i => (i.title || '').toLowerCase().includes(q) || (i.description || '').toLowerCase().includes(q));
|
|
104
|
+
},
|
|
105
|
+
async archiveList(listId) {
|
|
106
|
+
const list = await this.getList(listId);
|
|
107
|
+
list.metadata = { ...(list.metadata || {}), archived: true };
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
registerListsProvider('memory', MemoryListsProvider);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ListsProvider } from './types';
|
|
2
|
+
export declare function registerListsProvider(name: string, provider: ListsProvider): void;
|
|
3
|
+
export declare function registerListsProviderRuntime(name: string, provider: ListsProvider): void;
|
|
4
|
+
export declare function getListsProvider(name: string): ListsProvider;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const providers = {};
|
|
2
|
+
export function registerListsProvider(name, provider) {
|
|
3
|
+
providers[name] = provider;
|
|
4
|
+
}
|
|
5
|
+
// Allow runtime registration from other modules (e.g. via core ModuleRegistry adapters)
|
|
6
|
+
export function registerListsProviderRuntime(name, provider) {
|
|
7
|
+
registerListsProvider(name, provider);
|
|
8
|
+
}
|
|
9
|
+
export function getListsProvider(name) {
|
|
10
|
+
const provider = providers[name];
|
|
11
|
+
if (!provider)
|
|
12
|
+
throw new Error(`Lists provider "${name}" not found`);
|
|
13
|
+
return provider;
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
export interface List {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
type: 'checklist' | 'kanban' | 'list' | string;
|
|
16
|
+
items: ListItem[];
|
|
17
|
+
metadata?: Record<string, any>;
|
|
18
|
+
createdAt?: string;
|
|
19
|
+
updatedAt?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ListsProvider {
|
|
22
|
+
getList(id: string): Promise<List>;
|
|
23
|
+
listLists(params?: Record<string, any>): Promise<List[]>;
|
|
24
|
+
createList(data: Partial<List>): Promise<List>;
|
|
25
|
+
updateList(id: string, data: Partial<List>): Promise<List>;
|
|
26
|
+
deleteList(id: string): Promise<void>;
|
|
27
|
+
addItem(listId: string, item: Partial<ListItem>): Promise<ListItem>;
|
|
28
|
+
updateItem(listId: string, itemId: string, data: Partial<ListItem>): Promise<ListItem>;
|
|
29
|
+
deleteItem(listId: string, itemId: string): Promise<void>;
|
|
30
|
+
reorderItems?(listId: string, itemIds: string[]): Promise<void>;
|
|
31
|
+
toggleComplete?(listId: string, itemId: string, completed: boolean): Promise<ListItem>;
|
|
32
|
+
setDueDate?(listId: string, itemId: string, dueDate: string | null): Promise<ListItem>;
|
|
33
|
+
setReminder?(listId: string, itemId: string, reminder: string | null): Promise<ListItem>;
|
|
34
|
+
setPriority?(listId: string, itemId: string, priority: number | null): Promise<ListItem>;
|
|
35
|
+
shareList?(listId: string, userId: string, role?: string): Promise<void>;
|
|
36
|
+
searchItems?(listId: string, query: string): Promise<ListItem[]>;
|
|
37
|
+
archiveList?(listId: string): Promise<void>;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function useLists(): {
|
|
2
|
+
getList: (id: string) => Promise<import("./types").List>;
|
|
3
|
+
listLists: (params?: Record<string, any>) => Promise<import("./types").List[]>;
|
|
4
|
+
createList: (data: Partial<import("./types").List>) => Promise<import("./types").List>;
|
|
5
|
+
updateList: (id: string, data: Partial<import("./types").List>) => Promise<import("./types").List>;
|
|
6
|
+
deleteList: (id: string) => Promise<void>;
|
|
7
|
+
addItem: (listId: string, item: Partial<import("./types").ListItem>) => Promise<import("./types").ListItem>;
|
|
8
|
+
updateItem: (listId: string, itemId: string, data: Partial<import("./types").ListItem>) => Promise<import("./types").ListItem>;
|
|
9
|
+
deleteItem: (listId: string, itemId: string) => Promise<void>;
|
|
10
|
+
reorderItems: ((listId: string, itemIds: string[]) => Promise<void>) | undefined;
|
|
11
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getListsConfig } from './config';
|
|
2
|
+
import { getListsProvider } from './registry';
|
|
3
|
+
export function useLists() {
|
|
4
|
+
const { provider } = getListsConfig();
|
|
5
|
+
const lists = getListsProvider(provider);
|
|
6
|
+
return {
|
|
7
|
+
getList: lists.getList,
|
|
8
|
+
listLists: lists.listLists,
|
|
9
|
+
createList: lists.createList,
|
|
10
|
+
updateList: lists.updateList,
|
|
11
|
+
deleteList: lists.deleteList,
|
|
12
|
+
addItem: lists.addItem,
|
|
13
|
+
updateItem: lists.updateItem,
|
|
14
|
+
deleteItem: lists.deleteItem,
|
|
15
|
+
reorderItems: lists.reorderItems
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getListsProvider } from '../registry';
|
|
2
|
+
import { getListsConfig } from '../config';
|
|
3
|
+
export async function checkListsProviderHealth(providerName) {
|
|
4
|
+
try {
|
|
5
|
+
const cfg = getListsConfig();
|
|
6
|
+
const name = providerName || cfg.provider || 'directus';
|
|
7
|
+
const provider = getListsProvider(name);
|
|
8
|
+
const lists = await provider.listLists();
|
|
9
|
+
return { ok: true, count: Array.isArray(lists) ? lists.length : null };
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
return { ok: false, error: e?.message || String(e) };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export default checkListsProviderHealth;
|