Skip to content

Commit f907415

Browse files
lwinmoepaingclaude
andcommitted
✨ feat: fix floating toolbar with @floating-ui/react, add i18n to blog forms and sign-in prompt
Replace manual position calculation in FloatingToolbar with @floating-ui/react for correct viewport-aware positioning with flip/ shift/offset middleware. Add EN/MM translations for blog write/edit form labels, placeholders, and action buttons. Modernize SignInPrompt with prismatic card container, gradient ring icon, and outline gradient button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d0cb301 commit f907415

9 files changed

Lines changed: 244 additions & 110 deletions

File tree

bun.lockb

24 Bytes
Binary file not shown.

messages/en.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,24 @@
195195
"unpublish": "Unpublish",
196196
"editBlog": "Edit Blog",
197197
"deleteBlog": "Delete Blog",
198-
"communityPost": "Community"
198+
"communityPost": "Community",
199+
"writeNewBlog": "Write New Blog",
200+
"formTitle": "Title",
201+
"formTitlePlaceholder": "Post title",
202+
"formDescription": "Description",
203+
"formDescriptionPlaceholder": "Brief summary of your post",
204+
"formCoverImage": "Cover Image URL",
205+
"formCoverImagePlaceholder": "https://example.com/image.jpg (optional)",
206+
"formTags": "Tags",
207+
"formTagsPlaceholder": "Add tags (press Enter)",
208+
"formContent": "Content",
209+
"formContentPlaceholder": "Start writing your post... (type / for commands)",
210+
"saving": "Saving...",
211+
"autoSaveNote": "Auto-saves every 5s",
212+
"published": "Published",
213+
"draft": "Draft",
214+
"maxPostsPerDay": "Max 3 posts/day",
215+
"loadMore": "Load more"
199216
},
200217
"auth": {
201218
"signIn": "Sign In",

messages/mm.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,24 @@
195195
"unpublish": "ပြန်ရုပ်ရန်",
196196
"editBlog": "ဘလော့ဂ် ပြင်ရန်",
197197
"deleteBlog": "ဘလော့ဂ် ဖျက်ရန်",
198-
"communityPost": "အသိုင်းအဝိုင်း"
198+
"communityPost": "အသိုင်းအဝိုင်း",
199+
"writeNewBlog": "ဘလော့ဂ် အသစ် ရေးရန်",
200+
"formTitle": "ခေါင်းစဉ်",
201+
"formTitlePlaceholder": "ဆောင်းပါး ခေါင်းစဉ်",
202+
"formDescription": "ဖော်ပြချက်",
203+
"formDescriptionPlaceholder": "ဆောင်းပါး၏ အကျဉ်းချုပ်",
204+
"formCoverImage": "မျက်နှာဖုံး ပုံ URL",
205+
"formCoverImagePlaceholder": "https://example.com/image.jpg (ရွေးချယ်နိုင်)",
206+
"formTags": "တဂ်များ",
207+
"formTagsPlaceholder": "တဂ် ထည့်ရန် (Enter နှိပ်ပါ)",
208+
"formContent": "အကြောင်းအရာ",
209+
"formContentPlaceholder": "ဆောင်းပါး စတင်ရေးပါ... (/ ရိုက်၍ command များ သုံးနိုင်ပါသည်)",
210+
"saving": "သိမ်းနေသည်...",
211+
"autoSaveNote": "၅ စက္ကန့်တိုင်း အလိုအလျောက် သိမ်းပါသည်",
212+
"published": "ထုတ်ဝေပြီး",
213+
"draft": "မူကြမ်း",
214+
"maxPostsPerDay": "တစ်ရက် ၃ ပုဒ်အထိ",
215+
"loadMore": "ထပ်ကြည့်ရန်"
199216
},
200217
"auth": {
201218
"signIn": "ဝင်ရောက်ရန်",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
}
2828
},
2929
"dependencies": {
30+
"@floating-ui/react": "^0.27.19",
3031
"@formkit/auto-animate": "^0.8.0",
3132
"@lexical/clipboard": "^0.41.0",
3233
"@lexical/code": "^0.41.0",

src/app/blog/edit/BlogEditClient.tsx

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import type { SerializedEditorState } from "@/components/ContentEditor";
1212
import { useBlogEditor } from "@/hooks/blog/useBlogEditor";
1313
import { useAuth } from "@/hooks/useAuth";
1414
import { publishBlogPost, unpublishBlogPost } from "@/lib/firebase/firestore";
15+
import { useTranslations } from "next-intl";
16+
import { useLanguage } from "@/hooks/useLanguage";
17+
import { khitHaungg } from "@/fonts/fonts";
1518

1619
const INPUT_CLASS = cn(
1720
"w-full px-4 py-3 rounded-xl text-sm",
@@ -25,10 +28,12 @@ function TagInput({
2528
tags,
2629
onAdd,
2730
onRemove,
31+
placeholder = "Add tags (press Enter)",
2832
}: {
2933
tags: string[];
3034
onAdd: (tag: string) => void;
3135
onRemove: (tag: string) => void;
36+
placeholder?: string;
3237
}) {
3338
const [value, setValue] = useState("");
3439

@@ -67,7 +72,7 @@ function TagInput({
6772
value={value}
6873
onChange={(e) => setValue(e.target.value)}
6974
onKeyDown={handleKeyDown}
70-
placeholder="Add tags (press Enter)"
75+
placeholder={placeholder}
7176
className={INPUT_CLASS}
7277
/>
7378
</div>
@@ -76,6 +81,9 @@ function TagInput({
7681

7782
function BlogEditForm({ postId }: { postId: string }) {
7883
const router = useRouter();
84+
const t = useTranslations("blog");
85+
const { isMyanmar } = useLanguage();
86+
const mmFont = isMyanmar ? khitHaungg.className : "";
7987
const { user, isAdmin } = useAuth();
8088
const {
8189
title,
@@ -168,19 +176,19 @@ function BlogEditForm({ postId }: { postId: string }) {
168176
<PenLine className="w-4.5 h-4.5 text-prism-cyan" />
169177
</div>
170178
<div>
171-
<h1 className="text-xl font-semibold font-display text-white tracking-tight">
172-
Edit Blog
179+
<h1 className={cn("text-xl font-semibold font-display text-white tracking-tight", mmFont)}>
180+
{t("editBlog")}
173181
</h1>
174-
<p className="text-[11px] text-zinc-600 font-mono">
175-
{post?.status === "published" ? "Published" : "Draft"} · Auto-saves every 5s
182+
<p className={cn("text-[11px] text-zinc-600 font-mono", mmFont)}>
183+
{post?.status === "published" ? t("published") : t("draft")} · {t("autoSaveNote")}
176184
</p>
177185
</div>
178186
</div>
179187

180188
{post?.status === "published" && (
181-
<span className="flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-mono uppercase tracking-wider bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
189+
<span className={cn("flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-mono uppercase tracking-wider bg-emerald-500/10 text-emerald-400 border border-emerald-500/20", mmFont)}>
182190
<CheckCircle className="w-3 h-3" />
183-
Published
191+
{t("published")}
184192
</span>
185193
)}
186194
</div>
@@ -194,65 +202,66 @@ function BlogEditForm({ postId }: { postId: string }) {
194202
className="space-y-5"
195203
>
196204
<div>
197-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
198-
Title
205+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
206+
{t("formTitle")}
199207
</label>
200208
<input
201209
type="text"
202210
value={title}
203211
onChange={(e) => setTitle(e.target.value)}
204-
placeholder="Post title"
212+
placeholder={t("formTitlePlaceholder")}
205213
className={cn(INPUT_CLASS, "text-lg font-display")}
206214
/>
207215
</div>
208216

209217
<div>
210-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
211-
Description
218+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
219+
{t("formDescription")}
212220
</label>
213221
<textarea
214222
value={description}
215223
onChange={(e) => setDescription(e.target.value)}
216-
placeholder="Brief summary"
224+
placeholder={t("formDescriptionPlaceholder")}
217225
rows={2}
218226
className={cn(INPUT_CLASS, "resize-none")}
219227
/>
220228
</div>
221229

222230
<div>
223-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
231+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
224232
<ImageIcon className="w-3 h-3 inline mr-1" />
225-
Cover Image URL
233+
{t("formCoverImage")}
226234
</label>
227235
<input
228236
type="url"
229237
value={coverImageURL ?? ""}
230238
onChange={(e) => setCoverImageURL(e.target.value || null)}
231-
placeholder="https://example.com/image.jpg (optional)"
239+
placeholder={t("formCoverImagePlaceholder")}
232240
className={INPUT_CLASS}
233241
/>
234242
</div>
235243

236244
<div>
237-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
245+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
238246
<Tag className="w-3 h-3 inline mr-1" />
239-
Tags
247+
{t("formTags")}
240248
</label>
241249
<TagInput
242250
tags={tags}
243251
onAdd={(tag) => setTags([...tags, tag])}
244252
onRemove={(tag) => setTags(tags.filter((t) => t !== tag))}
253+
placeholder={t("formTagsPlaceholder")}
245254
/>
246255
</div>
247256

248257
<div>
249-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
250-
Content
258+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
259+
{t("formContent")}
251260
</label>
252261
<ContentEditor
253262
value={content}
254263
onChange={handleContentChange}
255-
placeholder="Start writing..."
264+
placeholder={t("formContentPlaceholder")}
256265
/>
257266
</div>
258267

@@ -272,7 +281,7 @@ function BlogEditForm({ postId }: { postId: string }) {
272281
)}
273282
>
274283
<Save className="w-4 h-4" />
275-
{saving ? "Saving..." : "Save"}
284+
<span className={mmFont}>{saving ? t("saving") : t("saveDraft")}</span>
276285
</button>
277286

278287
<button
@@ -288,16 +297,18 @@ function BlogEditForm({ postId }: { postId: string }) {
288297
)}
289298
>
290299
<CheckCircle className="w-4 h-4" />
291-
{publishing
292-
? "..."
293-
: post?.status === "published"
294-
? "Unpublish"
295-
: "Publish"}
300+
<span className={mmFont}>
301+
{publishing
302+
? "..."
303+
: post?.status === "published"
304+
? t("unpublish")
305+
: t("publish")}
306+
</span>
296307
</button>
297308

298309
{!isAdmin && post?.status !== "published" && (
299-
<span className="text-[11px] text-zinc-600 font-mono">
300-
Max 3 posts/day
310+
<span className={cn("text-[11px] text-zinc-600 font-mono", mmFont)}>
311+
{t("maxPostsPerDay")}
301312
</span>
302313
)}
303314
</div>

src/app/blog/write/BlogWriteClient.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import AuthGuard from "@/components/Auth/AuthGuard";
99
import { ContentEditor } from "@/components/ContentEditor";
1010
import type { SerializedEditorState } from "@/components/ContentEditor";
1111
import { useBlogEditor } from "@/hooks/blog/useBlogEditor";
12+
import { useTranslations } from "next-intl";
13+
import { useLanguage } from "@/hooks/useLanguage";
14+
import { khitHaungg } from "@/fonts/fonts";
1215

1316
const INPUT_CLASS = cn(
1417
"w-full px-4 py-3 rounded-xl text-sm",
@@ -22,10 +25,12 @@ function TagInput({
2225
tags,
2326
onAdd,
2427
onRemove,
28+
placeholder = "Add tags (press Enter)",
2529
}: {
2630
tags: string[];
2731
onAdd: (tag: string) => void;
2832
onRemove: (tag: string) => void;
33+
placeholder?: string;
2934
}) {
3035
const [value, setValue] = useState("");
3136

@@ -64,7 +69,7 @@ function TagInput({
6469
value={value}
6570
onChange={(e) => setValue(e.target.value)}
6671
onKeyDown={handleKeyDown}
67-
placeholder="Add tags (press Enter)"
72+
placeholder={placeholder}
6873
className={INPUT_CLASS}
6974
/>
7075
</div>
@@ -73,6 +78,9 @@ function TagInput({
7378

7479
function BlogWriteForm() {
7580
const router = useRouter();
81+
const t = useTranslations("blog");
82+
const { isMyanmar } = useLanguage();
83+
const mmFont = isMyanmar ? khitHaungg.className : "";
7684
const {
7785
title,
7886
setTitle,
@@ -127,8 +135,8 @@ function BlogWriteForm() {
127135
<div className="w-9 h-9 rounded-lg flex items-center justify-center bg-gradient-to-br from-prism-violet/20 to-prism-cyan/20 border border-white/[0.08]">
128136
<PenLine className="w-4.5 h-4.5 text-prism-cyan" />
129137
</div>
130-
<h1 className="text-xl font-semibold font-display text-white tracking-tight">
131-
Write New Blog
138+
<h1 className={cn("text-xl font-semibold font-display text-white tracking-tight", mmFont)}>
139+
{t("writeNewBlog")}
132140
</h1>
133141
</div>
134142
</motion.div>
@@ -142,71 +150,72 @@ function BlogWriteForm() {
142150
>
143151
{/* Title */}
144152
<div>
145-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
146-
Title
153+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
154+
{t("formTitle")}
147155
</label>
148156
<input
149157
type="text"
150158
value={title}
151159
onChange={(e) => setTitle(e.target.value)}
152-
placeholder="Post title"
160+
placeholder={t("formTitlePlaceholder")}
153161
className={cn(INPUT_CLASS, "text-lg font-display")}
154162
/>
155163
</div>
156164

157165
{/* Description */}
158166
<div>
159-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
160-
Description
167+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
168+
{t("formDescription")}
161169
</label>
162170
<textarea
163171
value={description}
164172
onChange={(e) => setDescription(e.target.value)}
165-
placeholder="Brief summary of your post"
173+
placeholder={t("formDescriptionPlaceholder")}
166174
rows={2}
167175
className={cn(INPUT_CLASS, "resize-none")}
168176
/>
169177
</div>
170178

171179
{/* Cover Image URL */}
172180
<div>
173-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
181+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
174182
<ImageIcon className="w-3 h-3 inline mr-1" />
175-
Cover Image URL
183+
{t("formCoverImage")}
176184
</label>
177185
<input
178186
type="url"
179187
value={coverImageURL ?? ""}
180188
onChange={(e) =>
181189
setCoverImageURL(e.target.value || null)
182190
}
183-
placeholder="https://example.com/image.jpg (optional)"
191+
placeholder={t("formCoverImagePlaceholder")}
184192
className={INPUT_CLASS}
185193
/>
186194
</div>
187195

188196
{/* Tags */}
189197
<div>
190-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
198+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
191199
<Tag className="w-3 h-3 inline mr-1" />
192-
Tags
200+
{t("formTags")}
193201
</label>
194202
<TagInput
195203
tags={tags}
196204
onAdd={(tag) => setTags([...tags, tag])}
197205
onRemove={(tag) => setTags(tags.filter((t) => t !== tag))}
206+
placeholder={t("formTagsPlaceholder")}
198207
/>
199208
</div>
200209

201210
{/* Content Editor */}
202211
<div>
203-
<label className="block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2">
204-
Content
212+
<label className={cn("block text-xs font-mono text-zinc-500 uppercase tracking-wider mb-2", mmFont)}>
213+
{t("formContent")}
205214
</label>
206215
<ContentEditor
207216
value={null}
208217
onChange={handleContentChange}
209-
placeholder="Start writing your post... (type / for commands)"
218+
placeholder={t("formContentPlaceholder")}
210219
/>
211220
</div>
212221

@@ -229,7 +238,7 @@ function BlogWriteForm() {
229238
)}
230239
>
231240
<Save className="w-4 h-4" />
232-
{saving ? "Saving..." : "Save Draft"}
241+
<span className={mmFont}>{saving ? t("saving") : t("saveDraft")}</span>
233242
</button>
234243
</div>
235244
</motion.div>

0 commit comments

Comments
 (0)