@commonpub/layer 0.15.2 → 0.15.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.
|
@@ -12,7 +12,8 @@ interface Comment {
|
|
|
12
12
|
createdAt: string;
|
|
13
13
|
parentId: string | null;
|
|
14
14
|
author: CommentAuthor | null;
|
|
15
|
-
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
replies?: any[];
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const props = defineProps<{
|
|
@@ -38,6 +39,8 @@ const queryParams = computed(() =>
|
|
|
38
39
|
isFederated.value ? undefined : { targetType: props.targetType, targetId: props.targetId, limit: commentLimit },
|
|
39
40
|
);
|
|
40
41
|
|
|
42
|
+
// @ts-ignore TS2589: useFetch<Comment[]> with this query shape hits deep
|
|
43
|
+
// type instantiation in some consumer apps (notably shell). Runtime is fine.
|
|
41
44
|
const { data: comments, refresh } = await useFetch<Comment[]>(commentUrl, { query: queryParams, lazy: true });
|
|
42
45
|
|
|
43
46
|
const allCommentsLoaded = ref(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@aws-sdk/client-s3": "^3.1010.0",
|
|
31
31
|
"@commonpub/explainer": "^0.7.11",
|
|
32
32
|
"@commonpub/schema": "^0.13.0",
|
|
33
|
-
"@commonpub/server": "^2.
|
|
33
|
+
"@commonpub/server": "^2.43.0",
|
|
34
34
|
"@tiptap/core": "^2.11.0",
|
|
35
35
|
"@tiptap/extension-bold": "^2.11.0",
|
|
36
36
|
"@tiptap/extension-bullet-list": "^2.11.0",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"vue": "^3.4.0",
|
|
54
54
|
"vue-router": "^4.3.0",
|
|
55
55
|
"zod": "^4.3.6",
|
|
56
|
-
"@commonpub/auth": "0.5.1",
|
|
57
|
-
"@commonpub/config": "0.10.0",
|
|
58
56
|
"@commonpub/docs": "0.6.2",
|
|
59
|
-
"@commonpub/
|
|
60
|
-
"@commonpub/learning": "0.5.0",
|
|
57
|
+
"@commonpub/config": "0.10.0",
|
|
61
58
|
"@commonpub/protocol": "0.9.9",
|
|
62
|
-
"@commonpub/
|
|
59
|
+
"@commonpub/learning": "0.5.0",
|
|
60
|
+
"@commonpub/ui": "0.8.5",
|
|
61
|
+
"@commonpub/auth": "0.5.1",
|
|
62
|
+
"@commonpub/editor": "0.7.9"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@testing-library/jest-dom": "^6.9.1",
|
package/pages/admin/content.vue
CHANGED
|
@@ -12,7 +12,22 @@ interface Category {
|
|
|
12
12
|
icon: string | null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
interface AdminContentItem {
|
|
16
|
+
id: string;
|
|
17
|
+
source?: string;
|
|
18
|
+
title: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
type: string;
|
|
21
|
+
status: string;
|
|
22
|
+
isEditorial?: boolean;
|
|
23
|
+
isFeatured?: boolean;
|
|
24
|
+
categoryId?: string | null;
|
|
25
|
+
viewCount?: number;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
author?: { id: string; username: string; displayName: string | null } | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { data, refresh } = await useFetch<{ items: AdminContentItem[] }>('/api/content', {
|
|
16
31
|
query: { limit: 50, sort: 'recent' },
|
|
17
32
|
});
|
|
18
33
|
const { data: categories } = await useFetch<Category[]>('/api/categories');
|
|
@@ -22,7 +37,11 @@ const selectAll = ref(false);
|
|
|
22
37
|
|
|
23
38
|
watch(selectAll, (val) => {
|
|
24
39
|
if (val && data.value?.items) {
|
|
25
|
-
selectedIds.value = new Set(
|
|
40
|
+
selectedIds.value = new Set(
|
|
41
|
+
data.value.items
|
|
42
|
+
.filter((i: AdminContentItem) => i.source !== 'federated')
|
|
43
|
+
.map((i: AdminContentItem) => i.id),
|
|
44
|
+
);
|
|
26
45
|
} else {
|
|
27
46
|
selectedIds.value = new Set();
|
|
28
47
|
}
|
package/pages/dashboard.vue
CHANGED
|
@@ -15,8 +15,19 @@ const activeTab = ref<'content' | 'bookmarks' | 'learning'>('content');
|
|
|
15
15
|
const contentSort = ref<'newest' | 'oldest' | 'popular'>('newest');
|
|
16
16
|
const contentTypeFilter = ref('');
|
|
17
17
|
|
|
18
|
+
interface DashContentItem {
|
|
19
|
+
id: string;
|
|
20
|
+
type: string;
|
|
21
|
+
slug: string;
|
|
22
|
+
title: string;
|
|
23
|
+
status: string;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
viewCount?: number;
|
|
26
|
+
likeCount?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
// My content (all statuses)
|
|
19
|
-
const { data: myContent, status: contentStatus } = await useFetch('/api/content', {
|
|
30
|
+
const { data: myContent, status: contentStatus } = await useFetch<{ items: DashContentItem[] }>('/api/content', {
|
|
20
31
|
query: { authorId: user.value?.id },
|
|
21
32
|
headers: reqHeaders,
|
|
22
33
|
});
|
|
@@ -65,19 +76,27 @@ function sortItems<T extends { createdAt: string; viewCount?: number; likeCount?
|
|
|
65
76
|
return sorted;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
const drafts = computed(() =>
|
|
69
|
-
sortItems(
|
|
79
|
+
const drafts = computed<DashContentItem[]>(() =>
|
|
80
|
+
sortItems<DashContentItem>(
|
|
81
|
+
filterByType<DashContentItem>(
|
|
82
|
+
(myContent.value?.items ?? []).filter((i: DashContentItem) => i.status === 'draft'),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
70
85
|
);
|
|
71
|
-
const published = computed(() =>
|
|
72
|
-
sortItems(
|
|
86
|
+
const published = computed<DashContentItem[]>(() =>
|
|
87
|
+
sortItems<DashContentItem>(
|
|
88
|
+
filterByType<DashContentItem>(
|
|
89
|
+
(myContent.value?.items ?? []).filter((i: DashContentItem) => i.status === 'published'),
|
|
90
|
+
),
|
|
91
|
+
),
|
|
73
92
|
);
|
|
74
93
|
|
|
75
94
|
// Stats use ALL items (unfiltered) so totals don't change with filter selection
|
|
76
|
-
const allPublished = computed(() =>
|
|
77
|
-
(myContent.value?.items ?? []).filter((i:
|
|
95
|
+
const allPublished = computed<DashContentItem[]>(() =>
|
|
96
|
+
(myContent.value?.items ?? []).filter((i: DashContentItem) => i.status === 'published'),
|
|
78
97
|
);
|
|
79
|
-
const allDrafts = computed(() =>
|
|
80
|
-
(myContent.value?.items ?? []).filter((i:
|
|
98
|
+
const allDrafts = computed<DashContentItem[]>(() =>
|
|
99
|
+
(myContent.value?.items ?? []).filter((i: DashContentItem) => i.status === 'draft'),
|
|
81
100
|
);
|
|
82
101
|
const totalViews = computed(() =>
|
|
83
102
|
allPublished.value.reduce((sum, item) => sum + (item.viewCount ?? 0), 0),
|
|
@@ -24,9 +24,10 @@ const { data: site, refresh: refreshSite } = await useFetch<{ id: string; name:
|
|
|
24
24
|
|
|
25
25
|
// Version selector
|
|
26
26
|
const selectedVersion = ref('');
|
|
27
|
+
type SiteVersion = { id: string; version: string; isDefault: boolean };
|
|
27
28
|
watch(site, (s) => {
|
|
28
29
|
if (s?.versions?.length && !selectedVersion.value) {
|
|
29
|
-
const def = s.versions.find((v) => v.isDefault) ?? s.versions[0];
|
|
30
|
+
const def = s.versions.find((v: SiteVersion) => v.isDefault) ?? s.versions[0];
|
|
30
31
|
if (def) selectedVersion.value = def.version;
|
|
31
32
|
}
|
|
32
33
|
}, { immediate: true });
|
|
@@ -34,7 +35,7 @@ watch(site, (s) => {
|
|
|
34
35
|
// Resolve selected version string → version UUID for write operations
|
|
35
36
|
const selectedVersionId = computed(() => {
|
|
36
37
|
if (!site.value?.versions?.length || !selectedVersion.value) return undefined;
|
|
37
|
-
return site.value.versions.find((v) => v.version === selectedVersion.value)?.id;
|
|
38
|
+
return site.value.versions.find((v: SiteVersion) => v.version === selectedVersion.value)?.id;
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
const { data: rawPages, refresh: refreshPages } = await useFetch<Array<{ id: string; title: string; slug: string; sortOrder: number; parentId: string | null; content: string | BlockTuple[] | null; format?: string }>>(() => {
|
package/pages/index.vue
CHANGED
|
@@ -58,7 +58,19 @@ const { data: communities, pending: communitiesPending } = await useFetch('/api/
|
|
|
58
58
|
query: { limit: 4 },
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
interface ContestListItem {
|
|
62
|
+
id: string;
|
|
63
|
+
slug: string;
|
|
64
|
+
title: string;
|
|
65
|
+
status: string;
|
|
66
|
+
description?: string | null;
|
|
67
|
+
bannerUrl?: string | null;
|
|
68
|
+
startDate?: string | null;
|
|
69
|
+
endDate?: string | null;
|
|
70
|
+
entryCount?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { data: contests, pending: contestsPending } = await useFetch<{ items: ContestListItem[] }>('/api/contests', {
|
|
62
74
|
query: { limit: 3 },
|
|
63
75
|
});
|
|
64
76
|
|
|
@@ -66,9 +78,9 @@ const heroDismissed = ref(false);
|
|
|
66
78
|
const joinedHubs = ref(new Set<string>());
|
|
67
79
|
|
|
68
80
|
// Active contest for hero banner
|
|
69
|
-
const activeContest = computed(() => {
|
|
81
|
+
const activeContest = computed<ContestListItem | null>(() => {
|
|
70
82
|
const items = contests.value?.items;
|
|
71
|
-
return items?.find((c) => c.status === 'active') ?? null;
|
|
83
|
+
return items?.find((c: ContestListItem) => c.status === 'active') ?? null;
|
|
72
84
|
});
|
|
73
85
|
|
|
74
86
|
const isAuthenticated = computed(() => !!user.value);
|
|
@@ -79,7 +79,7 @@ if (profile.value) {
|
|
|
79
79
|
form.value.bannerUrl = p.bannerUrl || '';
|
|
80
80
|
|
|
81
81
|
if (Array.isArray(p.skills)) {
|
|
82
|
-
skills.value = p.skills.filter((s): s is string => typeof s === 'string');
|
|
82
|
+
skills.value = (p.skills as unknown[]).filter((s): s is string => typeof s === 'string');
|
|
83
83
|
}
|
|
84
84
|
pronouns.value = p.pronouns || '';
|
|
85
85
|
if (p.socialLinks) {
|