Skip to content

Commit d868276

Browse files
committed
Refactor ArticleEditor and ArticleEditorDrawer components to improve tag management functionality. Integrate tag selection in the ArticleEditorDrawer, allowing users to categorize articles effectively. Update imports for better organization and enhance debugging capabilities by displaying article data during editing.
1 parent af7f706 commit d868276

File tree

3 files changed

+114
-56
lines changed

3 files changed

+114
-56
lines changed

src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
import ArticleEditor from "@/components/Editor/ArticleEditor";
2-
import * as articleActions from "@/backend/services/article.actions";
3-
import React from "react";
4-
import * as sessionActions from "@/backend/services/session.actions";
5-
import { notFound } from "next/navigation";
1+
import { Article, ArticleTag, Tag, User } from "@/backend/models/domain-models";
62
import { persistenceRepository } from "@/backend/persistence-repositories";
3+
import { DatabaseTableName } from "@/backend/persistence/persistence-contracts";
74
import {
8-
eq,
95
and,
10-
manyToManyJoin,
11-
leftJoin,
6+
eq,
127
inArray,
8+
leftJoin,
139
} from "@/backend/persistence/persistence-where-operator";
14-
import { Article, ArticleTag, Tag, User } from "@/backend/models/domain-models";
15-
import { DatabaseTableName } from "@/backend/persistence/persistence-contracts";
10+
import * as sessionActions from "@/backend/services/session.actions";
11+
import ArticleEditor from "@/components/Editor/ArticleEditor";
12+
import { notFound } from "next/navigation";
13+
import React from "react";
1614

1715
interface Props {
1816
params: Promise<{ uuid: string }>;

src/components/Editor/ArticleEditorDrawer.tsx

Lines changed: 104 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"use client";
22

3-
import { Article } from "@/backend/models/domain-models";
3+
import { Article, Tag } from "@/backend/models/domain-models";
44
import * as articleActions from "@/backend/services/article.actions";
5+
import * as tagActions from "@/backend/services/tag.action";
56
import { ArticleRepositoryInput } from "@/backend/services/inputs/article.input";
67
import MultipleSelector from "@/components/ui/multi-select";
78
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
89
import { useTranslation } from "@/i18n/use-translation";
910
import { useSession } from "@/store/session.atom";
1011
import { zodResolver } from "@hookform/resolvers/zod";
11-
import { useMutation } from "@tanstack/react-query";
12+
import { useMutation, useQuery } from "@tanstack/react-query";
1213
import { LinkIcon, Loader } from "lucide-react";
1314
import { useRouter } from "next/navigation";
14-
import React from "react";
15+
import React, { useState } from "react";
1516
import { SubmitHandler, useForm } from "react-hook-form";
1617
import { z } from "zod";
1718
import { Button } from "../ui/button";
@@ -52,6 +53,8 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
5253
},
5354
});
5455

56+
const [selectedTags, setSelectedTags] = useState<Tag[]>(article.tags ?? []);
57+
5558
const setDebounceHandler = useDebouncedCallback(async (slug: string) => {
5659
const handle = await articleActions.getUniqueArticleHandle(slug);
5760
form.setValue("handle", handle);
@@ -108,49 +111,104 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
108111
onSubmit={form.handleSubmit(handleOnSubmit)}
109112
className="flex flex-col gap-2"
110113
>
111-
{/* {JSON.stringify(form.formState.errors)} */}
112-
<pre>{JSON.stringify(article, null, 2)}</pre>
113-
<FormField
114-
control={form.control}
115-
name="handle"
116-
render={({ field }) => (
117-
<FormItem>
118-
<FormLabel>{_t("Handle")}</FormLabel>
119-
<FormControl>
120-
<Input
121-
placeholder="Your handle"
122-
Prefix={
123-
<LinkIcon className="size-3 text-muted-foreground" />
124-
}
125-
{...field}
126-
onChange={(e) => {
127-
setDebounceHandler(e.target.value);
128-
form.setValue("handle", e.target.value);
129-
}}
130-
/>
131-
</FormControl>
132-
<FormDescription className="-mt-1">
133-
https://www.techdiary.dev/@{session?.user?.username}/
134-
{form.watch("handle")}
135-
</FormDescription>
136-
<FormMessage />
137-
</FormItem>
138-
)}
139-
/>
140-
{/* */}
141-
<FormField
142-
control={form.control}
143-
name="excerpt"
144-
render={({ field }) => (
145-
<FormItem>
146-
<FormLabel>{_t("Excerpt")}</FormLabel>
147-
<FormControl>
148-
<Textarea {...field} />
149-
</FormControl>
150-
<FormMessage />
151-
</FormItem>
152-
)}
153-
/>
114+
{JSON.stringify({
115+
errors: form.formState.errors,
116+
values: form.watch("tag_ids"),
117+
})}
118+
{/* <pre>{JSON.stringify(article, null, 2)}</pre> */}
119+
<div className="flex flex-col gap-6">
120+
<FormField
121+
control={form.control}
122+
name="handle"
123+
render={({ field }) => (
124+
<FormItem>
125+
<FormLabel>{_t("Handle")}</FormLabel>
126+
<FormControl>
127+
<Input
128+
placeholder="Your handle"
129+
Prefix={
130+
<LinkIcon className="size-3 text-muted-foreground" />
131+
}
132+
{...field}
133+
onChange={(e) => {
134+
setDebounceHandler(e.target.value);
135+
form.setValue("handle", e.target.value);
136+
}}
137+
/>
138+
</FormControl>
139+
<FormDescription className="-mt-1">
140+
https://www.techdiary.dev/@{session?.user?.username}/
141+
{form.watch("handle")}
142+
</FormDescription>
143+
<FormMessage />
144+
</FormItem>
145+
)}
146+
/>
147+
<FormField
148+
control={form.control}
149+
name="excerpt"
150+
render={({ field }) => (
151+
<FormItem>
152+
<FormLabel>{_t("Excerpt")}</FormLabel>
153+
<FormControl>
154+
<Textarea {...field} />
155+
</FormControl>
156+
<FormMessage />
157+
</FormItem>
158+
)}
159+
/>
160+
<FormField
161+
control={form.control}
162+
name="tag_ids"
163+
render={({ field }) => (
164+
<FormItem>
165+
<FormLabel>{_t("Tags")}</FormLabel>
166+
<FormDescription className="text-xs">
167+
{_t("Select tags to help categorize your article.")}
168+
</FormDescription>
169+
<FormControl>
170+
<MultipleSelector
171+
maxSelected={10}
172+
onSearch={async (searchTerm) => {
173+
const res = await tagActions.getTags({
174+
limit: -1,
175+
page: 1,
176+
search: searchTerm,
177+
});
178+
179+
const searchResult = res?.nodes ?? [];
180+
return searchResult?.map((tag) => ({
181+
label: tag.name,
182+
value: tag.id,
183+
}));
184+
}}
185+
value={
186+
selectedTags?.map((option) => ({
187+
label: option.name,
188+
value: option.id,
189+
})) ?? []
190+
}
191+
onChange={(e) => {
192+
setSelectedTags(
193+
e.map((option) => ({
194+
id: option.value,
195+
name: option.label,
196+
created_at: new Date(),
197+
updated_at: new Date(),
198+
}))
199+
);
200+
form.setValue(
201+
"tag_ids",
202+
e.map((option) => option.value)
203+
);
204+
}}
205+
/>
206+
</FormControl>
207+
<FormMessage />
208+
</FormItem>
209+
)}
210+
/>
211+
</div>
154212

155213
{/* Seo settings */}
156214
<div className="flex flex-col gap-6 mt-10">

src/components/ui/multi-select.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"use client";
22

3+
// https://shadcnui-expansions.typeart.cc/docs/multiple-selector
4+
35
import { Command as CommandPrimitive, useCommandState } from "cmdk";
46
import { X } from "lucide-react";
57
import * as React from "react";

0 commit comments

Comments
 (0)