Skip to content

Commit 4663977

Browse files
committed
Refactor code structure for improved readability and maintainability
1 parent d403393 commit 4663977

File tree

7 files changed

+1461
-28
lines changed

7 files changed

+1461
-28
lines changed

bun.lock

Lines changed: 1378 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"sqlkit": "^1.0.13",
5454
"tailwind-merge": "^3.0.2",
5555
"tw-animate-css": "^1.2.4",
56+
"use-immer": "^0.11.0",
5657
"zod": "^3.24.2"
5758
},
5859
"devDependencies": {

src/backend/persistence/persistence-repositories.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,44 @@ const repositoryConfig = {
1919
export const userRepository = new Repository<User>(
2020
DatabaseTableName.users,
2121
pgClient,
22-
repositoryConfig,
22+
repositoryConfig
2323
);
2424
export const articleRepository = new Repository<Article>(
2525
DatabaseTableName.articles,
2626
pgClient,
27-
repositoryConfig,
27+
repositoryConfig
2828
);
2929
export const tagRepository = new Repository<Tag>(
3030
DatabaseTableName.tags,
3131
pgClient,
32-
repositoryConfig,
32+
repositoryConfig
3333
);
3434
export const articleTagRepository = new Repository<ArticleTag>(
3535
DatabaseTableName.article_tag,
3636
pgClient,
37-
repositoryConfig,
37+
repositoryConfig
3838
);
3939
export const userSocialRepository = new Repository<UserSocial>(
4040
DatabaseTableName.user_socials,
4141
pgClient,
42-
repositoryConfig,
42+
repositoryConfig
4343
);
4444
export const userSessionRepository = new Repository<UserSession>(
4545
DatabaseTableName.user_sessions,
4646
pgClient,
47-
repositoryConfig,
47+
repositoryConfig
4848
);
4949

5050
const seriesRepository = new Repository<Series>(
5151
DatabaseTableName.series,
5252
pgClient,
53-
repositoryConfig,
53+
repositoryConfig
5454
);
5555

5656
const seriesItemsRepository = new Repository<SeriesItem>(
5757
DatabaseTableName.series_items,
5858
pgClient,
59-
repositoryConfig,
59+
repositoryConfig
6060
);
6161

6262
export const persistenceRepository = {

src/backend/services/tag.action.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,45 @@
11
"use server";
22

3-
import {and, eq, inArray, like} from "sqlkit";
4-
import {z} from "zod";
5-
import {persistenceRepository} from "../persistence/persistence-repositories";
6-
import {TagRepositoryInput} from "./inputs/tag.input";
7-
import {handleRepositoryException} from "./RepositoryException";
3+
import { and, eq, inArray, like } from "sqlkit";
4+
import { z } from "zod";
5+
import { persistenceRepository } from "../persistence/persistence-repositories";
6+
import { TagRepositoryInput } from "./inputs/tag.input";
7+
import { handleRepositoryException } from "./RepositoryException";
88

99
export const getTags = async (
1010
_input: z.infer<typeof TagRepositoryInput.findAllInput>
1111
) => {
1212
try {
1313
const input = await TagRepositoryInput.findAllInput.parseAsync(_input);
14-
return await persistenceRepository.tags.find({
15-
where: input.search ? like("name", `%${input.search}%`) : undefined,
14+
return persistenceRepository.tags.find({
15+
where: input.search
16+
? like("name", `%${input.search.toLowerCase()}%`)
17+
: undefined,
1618
});
1719
} catch (error) {
1820
handleRepositoryException(error);
1921
}
2022
};
2123

24+
export const createTag = async (
25+
_input: z.infer<typeof TagRepositoryInput.createInput>
26+
) => {
27+
try {
28+
const input = await TagRepositoryInput.createInput.parseAsync(_input);
29+
const response = await persistenceRepository.tags.insert([
30+
{
31+
name: input.name,
32+
description: input.description,
33+
color: input.color,
34+
},
35+
]);
36+
37+
return response.rows[0];
38+
} catch (error) {
39+
handleRepositoryException(error);
40+
}
41+
};
42+
2243
export const syncTagsWithArticles = async (
2344
_input: z.infer<typeof TagRepositoryInput.syncTagsWithArticlesInput>
2445
) => {

src/components/Editor/ArticleEditor.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ const ArticleEditor: React.FC<ArticleEditorProps> = ({ article, uuid }) => {
200200
);
201201

202202
return (
203-
<>
203+
<div className="relative">
204204
<div className="flex bg-background gap-2 items-center justify-between mt-2 mb-10 sticky z-30 p-5">
205205
<div className="flex items-center gap-2 text-sm text-forground-muted">
206206
<div className="flex gap-4 items-center">
@@ -272,7 +272,9 @@ const ArticleEditor: React.FC<ArticleEditorProps> = ({ article, uuid }) => {
272272
autoFocus
273273
rows={1}
274274
value={watchedTitle}
275-
disabled={articleCreateMutation.isPending}
275+
disabled={
276+
articleCreateMutation.isPending || updateMyArticleMutation.isPending
277+
}
276278
className="w-full text-2xl focus:outline-none bg-background resize-none"
277279
ref={titleRef}
278280
onBlur={(e) => handleSaveArticleOnBlurTitle(e.target.value)}
@@ -286,6 +288,10 @@ const ArticleEditor: React.FC<ArticleEditorProps> = ({ article, uuid }) => {
286288
<div className="w-full">
287289
{editorMode === "write" ? (
288290
<textarea
291+
disabled={
292+
articleCreateMutation.isPending ||
293+
updateMyArticleMutation.isPending
294+
}
289295
tabIndex={2}
290296
className="focus:outline-none h-[calc(100vh-120px)] bg-background w-full resize-none"
291297
placeholder={_t("Write something stunning...")}
@@ -311,7 +317,7 @@ const ArticleEditor: React.FC<ArticleEditorProps> = ({ article, uuid }) => {
311317
}}
312318
/>
313319
)}
314-
</>
320+
</div>
315321
);
316322
};
317323

src/components/Editor/ArticleEditorDrawer.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as articleActions from "@/backend/services/article.actions";
55
import * as tagActions from "@/backend/services/tag.action";
66
import { ArticleRepositoryInput } from "@/backend/services/inputs/article.input";
77
import MultipleSelector from "@/components/ui/multi-select";
8+
import { useImmer } from "use-immer";
89
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
910
import { useTranslation } from "@/i18n/use-translation";
1011
import { useSession } from "@/store/session.atom";
@@ -53,7 +54,7 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
5354
},
5455
});
5556

56-
const [selectedTags, setSelectedTags] = useState<Tag[]>(article.tags ?? []);
57+
const [selectedTags, setSelectedTags] = useImmer<Tag[]>(article.tags ?? []);
5758

5859
const setDebounceHandler = useDebouncedCallback(async (slug: string) => {
5960
const handle = await articleActions.getUniqueArticleHandle(slug);
@@ -176,20 +177,41 @@ const ArticleEditorDrawer: React.FC<Props> = ({ article, open, onClose }) => {
176177
page: 1,
177178
search: searchTerm,
178179
});
179-
180-
const searchResult = res ?? [];
181-
return searchResult?.map((tag) => ({
182-
label: tag.name,
183-
value: tag.id,
184-
}));
180+
return (
181+
res?.map((tag) => ({
182+
label: tag.name,
183+
value: tag.id,
184+
})) ?? []
185+
);
185186
}}
186187
value={
187188
selectedTags?.map((option) => ({
188189
label: option.name,
189190
value: option.id,
190191
})) ?? []
191192
}
193+
creatable
194+
onCreate={async (data) => {
195+
const createdResponse = await tagActions.createTag({
196+
name: data,
197+
});
198+
const old_tags = form.watch("tag_ids") ?? [];
199+
200+
setSelectedTags(function (draft) {
201+
draft.push({
202+
id: createdResponse?.id!,
203+
name: data,
204+
created_at: new Date(),
205+
updated_at: new Date(),
206+
});
207+
});
208+
form.setValue("tag_ids", [
209+
...old_tags,
210+
createdResponse?.id! as string,
211+
]);
212+
}}
192213
onChange={(e) => {
214+
console.log(e);
193215
setSelectedTags(
194216
e.map((option) => ({
195217
id: option.value,

src/components/ui/multi-select.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ interface MultipleSelectorProps {
5555
**/
5656
onSearchSync?: (value: string) => Option[];
5757
onChange?: (options: Option[]) => void;
58+
onCreate?: (value: string) => void;
59+
5860
/** Limit the maximum number of selected options. */
5961
maxSelected?: number;
6062
/** When the number of selected options exceeds the limit, the onMaxSelected will be called. */
@@ -185,6 +187,7 @@ const MultipleSelector = React.forwardRef<
185187
{
186188
value,
187189
onChange,
190+
onCreate,
188191
placeholder,
189192
defaultOptions: arrayDefaultOptions = [],
190193
options: arrayOptions,
@@ -381,9 +384,11 @@ const MultipleSelector = React.forwardRef<
381384
return;
382385
}
383386
setInputValue("");
384-
const newOptions = [...selected, { value, label: value }];
385-
setSelected(newOptions);
386-
onChange?.(newOptions);
387+
onCreate?.(value);
388+
389+
// const newOptions = [...selected, { value, label: value }];
390+
// setSelected(newOptions);
391+
// onChange?.(newOptions);
387392
}}
388393
>
389394
{`Create "${inputValue}"`}

0 commit comments

Comments
 (0)