Skip to content

Commit 4f6d12b

Browse files
committed
Add article tagging functionality: Introduce ArticleTag model and repository, update Article interface to include optional tags, and enhance article update logic to manage tags. Refactor ArticleEditor to support tag selection during article creation and updates.
1 parent aa225d7 commit 4f6d12b

File tree

7 files changed

+52
-11
lines changed

7 files changed

+52
-11
lines changed

src/app/api/play/route.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import * as articleActions from "@/backend/services/article.actions";
22
import { NextResponse } from "next/server";
33

44
export async function GET(request: Request) {
5-
return NextResponse.json({
6-
handle: await articleActions.getUniqueArticleHandle(
7-
"untitled",
8-
"fc6cfc91-f017-4923-9706-8813ae8df621"
9-
),
10-
});
5+
// return NextResponse.json({
6+
// handle: await articleActions.updateArticle({
7+
// article_id: "5f0a1c0c-a8c8-4f5b-b8d8-c4d4d4d4d4d4",
8+
// }),
9+
// });
1110
}

src/backend/models/domain-models.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface Article {
5555
id: string;
5656
title: string;
5757
handle: string;
58+
tags?: Tag[];
5859
excerpt?: string | null;
5960
body?: string | null;
6061
cover_image?: IServerFile | null;
@@ -100,3 +101,11 @@ export interface Tag {
100101
created_at: Date;
101102
updated_at: Date;
102103
}
104+
105+
export interface ArticleTag {
106+
id: string;
107+
article_id: string;
108+
tag_id: string;
109+
created_at: Date;
110+
updated_at: Date;
111+
}

src/backend/persistence-repositories.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Article,
3+
ArticleTag,
34
Series,
45
SeriesItem,
56
Tag,
@@ -23,6 +24,10 @@ export const tagRepository = new PersistentRepository<Tag>(
2324
DatabaseTableName.tags,
2425
pgClient
2526
);
27+
export const articleTagRepository = new PersistentRepository<ArticleTag>(
28+
DatabaseTableName.article_tag,
29+
pgClient
30+
);
2631
export const userSocialRepository = new PersistentRepository<UserSocial>(
2732
DatabaseTableName.user_socials,
2833
pgClient
@@ -47,6 +52,7 @@ export const persistenceRepository = {
4752
userSocial: userSocialRepository,
4853
userSession: userSessionRepository,
4954
article: articleRepository,
55+
articleTag: articleTagRepository,
5056
tags: tagRepository,
5157
series: seriesRepository,
5258
seriesItems: seriesItemsRepository,

src/backend/persistence/persistence-contracts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,5 @@ export enum DatabaseTableName {
115115
series = "series",
116116
series_items = "series_items",
117117
tags = "tags",
118+
article_tag = "article_tag",
118119
}

src/backend/services/article.actions.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { slugify } from "@/lib/slug-helper.util";
44
import { removeMarkdownSyntax } from "@/lib/utils";
55
import { z } from "zod";
6-
import { Article, User } from "../models/domain-models";
6+
import { Article, Tag, User } from "../models/domain-models";
77
import { pgClient } from "../persistence/database-drivers/pg.client";
88
import {
99
and,
@@ -12,6 +12,7 @@ import {
1212
joinTable,
1313
like,
1414
neq,
15+
notInArray,
1516
or,
1617
} from "../persistence/persistence-where-operator";
1718
import { PersistentRepository } from "../persistence/persistence.repository";
@@ -22,6 +23,7 @@ import {
2223
import { ArticleRepositoryInput } from "./inputs/article.input";
2324
import { getSessionUserId } from "./session.actions";
2425
import { DatabaseTableName } from "../persistence/persistence-contracts";
26+
import { persistenceRepository } from "../persistence-repositories";
2527

2628
const articleRepository = new PersistentRepository<Article>(
2729
DatabaseTableName.articles,
@@ -227,6 +229,23 @@ export async function updateMyArticle(
227229
metadata: input.metadata,
228230
},
229231
});
232+
233+
if (input.tag_ids) {
234+
await persistenceRepository.articleTag.deleteRows({
235+
where: and(
236+
eq("article_id", article.id),
237+
notInArray("tag_id", input.tag_ids)
238+
),
239+
});
240+
241+
await input.tag_ids.forEach((tag_id) => {
242+
persistenceRepository.articleTag.createOne({
243+
article_id: article.id,
244+
tag_id: tag_id,
245+
});
246+
});
247+
}
248+
230249
return article;
231250
} catch (error) {
232251
handleRepositoryException(error);
@@ -417,6 +436,12 @@ export async function articleDetailByHandle(article_handle: string) {
417436
foreignField: "id",
418437
columns: ["id", "name", "username", "profile_photo"],
419438
}),
439+
// joinTable<Article, Tag>({
440+
// as: "tags",
441+
// joinTo: "tags",
442+
// localField: "author_id",
443+
// foreignField: "id",
444+
// }),
420445
],
421446
limit: 1,
422447
});

src/backend/services/inputs/article.input.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export const ArticleRepositoryInput = {
6666
// Optional boolean flag for publish status
6767
is_published: z.boolean().optional(),
6868

69+
tag_ids: z.array(z.string()).optional().nullable(),
70+
6971
// Optional metadata object
7072
metadata: z
7173
.object({

src/components/Editor/ArticleEditorDrawer.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { Article } from "@/backend/models/domain-models";
44
import * as articleActions from "@/backend/services/article.actions";
55
import { ArticleRepositoryInput } from "@/backend/services/inputs/article.input";
6-
import MultipleSelector, { Option } from "@/components/ui/multi-select";
6+
import MultipleSelector from "@/components/ui/multi-select";
77
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
88
import { useTranslation } from "@/i18n/use-translation";
99
import { useSession } from "@/store/session.atom";
@@ -25,7 +25,6 @@ import {
2525
FormMessage,
2626
} from "../ui/form";
2727
import { Input } from "../ui/input";
28-
import { InputTags } from "../ui/input-tags";
2928
import { Sheet, SheetContent } from "../ui/sheet";
3029
import { Textarea } from "../ui/textarea";
3130

@@ -70,6 +69,7 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
7069
title: article?.title ?? "",
7170
handle: article?.handle ?? "",
7271
excerpt: article?.excerpt ?? "",
72+
tag_ids: article?.tags?.map((tag) => tag.id) ?? [],
7373
metadata: {
7474
seo: {
7575
title: article?.metadata?.seo?.title ?? "",
@@ -84,12 +84,11 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
8484
const handleOnSubmit: SubmitHandler<
8585
z.infer<typeof ArticleRepositoryInput.updateMyArticleInput>
8686
> = (payload) => {
87-
console.log(payload);
88-
8987
updateMyArticleMutation.mutate({
9088
article_id: article?.id ?? "",
9189
excerpt: payload.excerpt,
9290
handle: payload.handle,
91+
tag_ids: payload.tag_ids,
9392
metadata: {
9493
seo: {
9594
title: payload.metadata?.seo?.title ?? "",

0 commit comments

Comments
 (0)