@@ -272,7 +272,9 @@ const ArticleEditor: React.FC
= ({ article, uuid }) => {
autoFocus
rows={1}
value={watchedTitle}
- disabled={articleCreateMutation.isPending}
+ disabled={
+ articleCreateMutation.isPending || updateMyArticleMutation.isPending
+ }
className="w-full text-2xl focus:outline-none bg-background resize-none"
ref={titleRef}
onBlur={(e) => handleSaveArticleOnBlurTitle(e.target.value)}
@@ -286,6 +288,10 @@ const ArticleEditor: React.FC = ({ article, uuid }) => {
{editorMode === "write" ? (
);
};
diff --git a/src/components/Editor/ArticleEditorDrawer.tsx b/src/components/Editor/ArticleEditorDrawer.tsx
index b4f619e..72a8793 100644
--- a/src/components/Editor/ArticleEditorDrawer.tsx
+++ b/src/components/Editor/ArticleEditorDrawer.tsx
@@ -5,6 +5,7 @@ import * as articleActions from "@/backend/services/article.actions";
import * as tagActions from "@/backend/services/tag.action";
import { ArticleRepositoryInput } from "@/backend/services/inputs/article.input";
import MultipleSelector from "@/components/ui/multi-select";
+import { useImmer } from "use-immer";
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
import { useTranslation } from "@/i18n/use-translation";
import { useSession } from "@/store/session.atom";
@@ -53,7 +54,7 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => {
},
});
- const [selectedTags, setSelectedTags] = useState(article.tags ?? []);
+ const [selectedTags, setSelectedTags] = useImmer(article.tags ?? []);
const setDebounceHandler = useDebouncedCallback(async (slug: string) => {
const handle = await articleActions.getUniqueArticleHandle(slug);
@@ -167,6 +168,7 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => {
{_t("Select tags to help categorize your article.")}
+ {/* https://shadcnui-expansions.typeart.cc/docs/multiple-selector#Async%20Search%20and%20Creatable%20and%20Group */}
{
@@ -175,12 +177,12 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => {
page: 1,
search: searchTerm,
});
-
- const searchResult = res ?? [];
- return searchResult?.map((tag) => ({
- label: tag.name,
- value: tag.id,
- }));
+ return (
+ res?.map((tag) => ({
+ label: tag.name,
+ value: tag.id,
+ })) ?? []
+ );
}}
value={
selectedTags?.map((option) => ({
@@ -188,7 +190,28 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => {
value: option.id,
})) ?? []
}
+ creatable
+ onCreate={async (data) => {
+ const createdResponse = await tagActions.createTag({
+ name: data,
+ });
+ const old_tags = form.watch("tag_ids") ?? [];
+
+ setSelectedTags(function (draft) {
+ draft.push({
+ id: createdResponse?.id!,
+ name: data,
+ created_at: new Date(),
+ updated_at: new Date(),
+ });
+ });
+ form.setValue("tag_ids", [
+ ...old_tags,
+ createdResponse?.id! as string,
+ ]);
+ }}
onChange={(e) => {
+ console.log(e);
setSelectedTags(
e.map((option) => ({
id: option.value,
diff --git a/src/components/ui/multi-select.tsx b/src/components/ui/multi-select.tsx
index f410e52..0a2e3ea 100644
--- a/src/components/ui/multi-select.tsx
+++ b/src/components/ui/multi-select.tsx
@@ -55,6 +55,8 @@ interface MultipleSelectorProps {
**/
onSearchSync?: (value: string) => Option[];
onChange?: (options: Option[]) => void;
+ onCreate?: (value: string) => void;
+
/** Limit the maximum number of selected options. */
maxSelected?: number;
/** When the number of selected options exceeds the limit, the onMaxSelected will be called. */
@@ -185,6 +187,7 @@ const MultipleSelector = React.forwardRef<
{
value,
onChange,
+ onCreate,
placeholder,
defaultOptions: arrayDefaultOptions = [],
options: arrayOptions,
@@ -381,9 +384,11 @@ const MultipleSelector = React.forwardRef<
return;
}
setInputValue("");
- const newOptions = [...selected, { value, label: value }];
- setSelected(newOptions);
- onChange?.(newOptions);
+ onCreate?.(value);
+
+ // const newOptions = [...selected, { value, label: value }];
+ // setSelected(newOptions);
+ // onChange?.(newOptions);
}}
>
{`Create "${inputValue}"`}
diff --git a/src/i18n/_t.ts b/src/i18n/_t.ts
index acd4bfd..2105bdf 100644
--- a/src/i18n/_t.ts
+++ b/src/i18n/_t.ts
@@ -1,6 +1,6 @@
"use server";
-import { cookies } from "next/headers";
+import {cookies} from "next/headers";
import bn from "@/i18n/bn.json";
const dictionaries: {
@@ -15,8 +15,7 @@ const _t = async (key: string) => {
export const getLang = async () => {
const cookiesStore = await cookies();
- const _lang = cookiesStore.get("language")?.value || "en";
- return _lang;
+ return cookiesStore.get("language")?.value || "en";
};
export default _t;
diff --git a/src/i18n/bn.json b/src/i18n/bn.json
index 8d2099b..3e2b615 100644
--- a/src/i18n/bn.json
+++ b/src/i18n/bn.json
@@ -51,7 +51,6 @@
"Articles": "আর্টিক্যাল সমূহ",
"Draft": "খসড়া",
"Edit": "সম্পাদনা",
- "Make Unpublished": "অপ্রকাশিত করুন",
"Sure to unpublish": "অপ্রকাশিত করতে চাচ্ছেন?",
"If you unpublish the article, this will be excluded in home page and search results, however direct links to the article will still work": "আপনি যদি আর্টিকেলটি আনপাব্লিশ করেন তবে এটি হোম পেজে এবং সার্চ ফলাফলে দেখাবে না, তবে আর্টিকেলটির সরাসরি লিঙ্ক সব সময় কাজ করবে",
"Published on": "প্রকাশিত হয়েছে",
diff --git a/src/i18n/use-translation.ts b/src/i18n/use-translation.ts
index 89f9d70..b95624c 100644
--- a/src/i18n/use-translation.ts
+++ b/src/i18n/use-translation.ts
@@ -11,9 +11,9 @@ export const useTranslation = () => {
return {
_t: (key: string) => dictionaries?.[lang || "en"]?.[key] || key,
lang,
- toggle: () => {
+ toggle: async () => {
setLang(lang === "en" ? "bn" : "en");
- setLanguage(lang === "en" ? "bn" : "en");
+ await setLanguage(lang === "en" ? "bn" : "en");
},
};
};