fix(admin): زبان ویرایش از زبان صفحه پیروی کند — ویرایش روی /en دیگر متن فارسی را بازنویسی نمیکند
قبلاً زبان ویرایش (editLang) کلید جداگانهای در نوار ادمین بود که با زبان صفحهٔ در حال نمایش هیچ ارتباطی نداشت و پیشفرضش فارسی میماند؛ نتیجهاش این بود که ویرایش درجا روی صفحههای /en به ستونهای فارسی میرفت و تغییرها «سمت دیگر» یعنی سایت فارسی ظاهر میشدند. - AdminBar: در حالت ویرایش، editLang با زبان URL همگام میشود (syncEditLang)؛ کلید دستی فقط برای ویرایش ستون زبان دیگر روی همین صفحه میماند. - entities: تعریف status-labels اضافه شد — نبودش یعنی ویرایش برچسب وضعیت روی صفحهٔ انگلیسی همیشه ستون فارسی را بازنویسی میکرد.
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import { useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { KeyRoundIcon, XIcon } from 'lucide-react'
|
import { KeyRoundIcon, XIcon } from 'lucide-react'
|
||||||
import { useAdmin } from './AdminContext'
|
import { useAdmin } from './AdminContext'
|
||||||
import { adminApi } from './adminClient'
|
import { adminApi } from './adminClient'
|
||||||
|
import { useLang } from '../i18n/lang'
|
||||||
|
|
||||||
const STATUS_TEXT: Record<string, string> = {
|
const STATUS_TEXT: Record<string, string> = {
|
||||||
idle: 'همگامسازی شد',
|
idle: 'همگامسازی شد',
|
||||||
@@ -127,9 +128,18 @@ function ChangePasswordModal({ onClose }: { onClose: () => void }) {
|
|||||||
|
|
||||||
/** نوار شناور ادمین — فقط برای کاربر واردشده رندر میشود */
|
/** نوار شناور ادمین — فقط برای کاربر واردشده رندر میشود */
|
||||||
export function AdminBar() {
|
export function AdminBar() {
|
||||||
const { isEditMode, toggleEditMode, saveStatus, logout, editLang, toggleEditLang, role, canManageSite } = useAdmin()
|
const { isEditMode, toggleEditMode, saveStatus, logout, editLang, toggleEditLang, syncEditLang, role, canManageSite } = useAdmin()
|
||||||
|
const { lang } = useLang()
|
||||||
const [pwOpen, setPwOpen] = useState(false)
|
const [pwOpen, setPwOpen] = useState(false)
|
||||||
|
|
||||||
|
// زبان ویرایش از زبان صفحهٔ در حال نمایش پیروی میکند — ویرایش روی /en به ستونهای
|
||||||
|
// انگلیسی میرود و روی صفحات فارسی به فارسی؛ وگرنه ویرایشِ دیدهشده در /en ناخواسته
|
||||||
|
// متن فارسی را بازنویسی میکرد. کلید دستی پایین فقط برای ویرایش ستون زبان دیگر
|
||||||
|
// روی همین صفحه است و با ناوبری بعدی دوباره همگام میشود.
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEditMode) syncEditLang(lang)
|
||||||
|
}, [isEditMode, lang, syncEditLang])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -167,7 +177,7 @@ export function AdminBar() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleEditLang}
|
onClick={toggleEditLang}
|
||||||
title="تعیین میکند ویرایشها روی کدام ستون زبان ذخیره شوند"
|
title="معمولاً خودکار با زبان صفحه همگام میشود — این کلید فقط برای ویرایش ستون زبان دیگر روی همین صفحه است"
|
||||||
className={`flex items-center gap-1.5 border px-3 py-1.5 text-xs font-light transition-colors duration-200 ${
|
className={`flex items-center gap-1.5 border px-3 py-1.5 text-xs font-light transition-colors duration-200 ${
|
||||||
editLang === 'en'
|
editLang === 'en'
|
||||||
? 'border-copper bg-copper/10 text-copper'
|
? 'border-copper bg-copper/10 text-copper'
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ interface AdminContextValue {
|
|||||||
/** زبان محتوایی که در حالت ویرایش ذخیره میشود (فارسی یا انگلیسی) */
|
/** زبان محتوایی که در حالت ویرایش ذخیره میشود (فارسی یا انگلیسی) */
|
||||||
editLang: EditLang
|
editLang: EditLang
|
||||||
toggleEditLang: () => void
|
toggleEditLang: () => void
|
||||||
|
/** همگامسازی زبان ویرایش با زبان صفحهٔ در حال نمایش — کلید دستی فقط برای
|
||||||
|
* ویرایش ستون زبان دیگر روی همان صفحه است؛ در ناوبری دوباره از صفحه پیروی میکند */
|
||||||
|
syncEditLang: (lang: EditLang) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AdminContext = createContext<AdminContextValue | null>(null)
|
const AdminContext = createContext<AdminContextValue | null>(null)
|
||||||
@@ -80,7 +83,7 @@ export function AdminProvider({ children }: { children: ReactNode }) {
|
|||||||
const [isEditMode, setIsEditMode] = useState<boolean>(
|
const [isEditMode, setIsEditMode] = useState<boolean>(
|
||||||
() => sessionStorage.getItem('aramesh_edit_mode') === '1',
|
() => sessionStorage.getItem('aramesh_edit_mode') === '1',
|
||||||
)
|
)
|
||||||
const [editLang, setEditLang] = useState<EditLang>(
|
const [editLang, setEditLangState] = useState<EditLang>(
|
||||||
() => (sessionStorage.getItem('aramesh_edit_lang') === 'en' ? 'en' : 'fa'),
|
() => (sessionStorage.getItem('aramesh_edit_lang') === 'en' ? 'en' : 'fa'),
|
||||||
)
|
)
|
||||||
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
|
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
|
||||||
@@ -94,13 +97,22 @@ export function AdminProvider({ children }: { children: ReactNode }) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const toggleEditLang = useCallback(() => {
|
const toggleEditLang = useCallback(() => {
|
||||||
setEditLang((v) => {
|
setEditLangState((v) => {
|
||||||
const next: EditLang = v === 'fa' ? 'en' : 'fa'
|
const next: EditLang = v === 'fa' ? 'en' : 'fa'
|
||||||
sessionStorage.setItem('aramesh_edit_lang', next)
|
sessionStorage.setItem('aramesh_edit_lang', next)
|
||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// بدون بازنویسی انتخاب دستی وقتی مقدار همان است — تا اثر همگامسازی بیدلیل رندر نکند
|
||||||
|
const syncEditLang = useCallback((next: EditLang) => {
|
||||||
|
setEditLangState((v) => {
|
||||||
|
if (v === next) return v
|
||||||
|
sessionStorage.setItem('aramesh_edit_lang', next)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const login = useCallback(async (username: string, password: string): Promise<AdminRole> => {
|
const login = useCallback(async (username: string, password: string): Promise<AdminRole> => {
|
||||||
await adminApi.login(username, password)
|
await adminApi.login(username, password)
|
||||||
const nextRole = roleFromToken(getToken()) ?? 'admin'
|
const nextRole = roleFromToken(getToken()) ?? 'admin'
|
||||||
@@ -194,8 +206,9 @@ export function AdminProvider({ children }: { children: ReactNode }) {
|
|||||||
runSave,
|
runSave,
|
||||||
editLang,
|
editLang,
|
||||||
toggleEditLang,
|
toggleEditLang,
|
||||||
|
syncEditLang,
|
||||||
}),
|
}),
|
||||||
[isAuthenticated, role, canManageSite, isEditMode, toggleEditMode, login, logout, saveStatus, runSave, editLang, toggleEditLang],
|
[isAuthenticated, role, canManageSite, isEditMode, toggleEditMode, login, logout, saveStatus, runSave, editLang, toggleEditLang, syncEditLang],
|
||||||
)
|
)
|
||||||
|
|
||||||
return <AdminContext.Provider value={value}>{children}</AdminContext.Provider>
|
return <AdminContext.Provider value={value}>{children}</AdminContext.Provider>
|
||||||
|
|||||||
@@ -176,6 +176,14 @@ export const ENTITIES: Record<string, EntityConfig> = {
|
|||||||
fields: [F('label', 'عنوان'), F('href', 'مسیر (مثل /about)', 'text', undefined, true)],
|
fields: [F('label', 'عنوان'), F('href', 'مسیر (مثل /about)', 'text', undefined, true)],
|
||||||
defaults: { label: 'آیتم جدید', href: '/' },
|
defaults: { label: 'آیتم جدید', href: '/' },
|
||||||
},
|
},
|
||||||
|
'status-labels': {
|
||||||
|
resource: 'status-labels', dataKey: 'status_labels', title: 'برچسبهای وضعیت پروژه', kind: 'collection',
|
||||||
|
titleKey: 'label',
|
||||||
|
// نبودِ این تعریف یعنی isTranslatable همیشه false برمیگشت و ویرایشِ برچسب روی
|
||||||
|
// صفحهٔ انگلیسی بهجای ستون label_en ستون فارسی را بازنویسی میکرد
|
||||||
|
fields: [F('label', 'برچسب'), F('key', 'کلید وضعیت', 'text', 'done یا progress', true)],
|
||||||
|
defaults: { key: 'done', label: 'برچسب جدید' },
|
||||||
|
},
|
||||||
seo_meta: {
|
seo_meta: {
|
||||||
resource: 'seo-meta', dataKey: 'seo_meta', title: 'سئوی صفحه', kind: 'collection',
|
resource: 'seo-meta', dataKey: 'seo_meta', title: 'سئوی صفحه', kind: 'collection',
|
||||||
titleKey: 'page_key',
|
titleKey: 'page_key',
|
||||||
|
|||||||
Reference in New Issue
Block a user