diff --git a/src/Meezi.API/Validators/PosValidators.cs b/src/Meezi.API/Validators/PosValidators.cs index 04948be..6f33853 100644 --- a/src/Meezi.API/Validators/PosValidators.cs +++ b/src/Meezi.API/Validators/PosValidators.cs @@ -12,7 +12,7 @@ public class CreateMenuCategoryRequestValidator : AbstractValidator x.Name).NotEmpty().MaximumLength(200); - RuleFor(x => x.NameEn).NotEmpty().MaximumLength(200); + RuleFor(x => x.NameEn).MaximumLength(200).When(x => !string.IsNullOrWhiteSpace(x.NameEn)); RuleFor(x => x.SortOrder).GreaterThanOrEqualTo(0); RuleFor(x => x.DiscountPercent).InclusiveBetween(0, 100); RuleFor(x => x.Icon).MaximumLength(32).When(x => x.Icon is not null); @@ -39,7 +39,7 @@ public class CreateMenuItemRequestValidator : AbstractValidator x.CategoryId).NotEmpty(); RuleFor(x => x.Name).NotEmpty().MaximumLength(200); - RuleFor(x => x.NameEn).NotEmpty().MaximumLength(200); + RuleFor(x => x.NameEn).MaximumLength(200).When(x => !string.IsNullOrWhiteSpace(x.NameEn)); RuleFor(x => x.Price).GreaterThanOrEqualTo(0); RuleFor(x => x.DiscountPercent).InclusiveBetween(0, 100); } diff --git a/web/admin/messages/ar.json b/web/admin/messages/ar.json index 802e109..039313f 100644 --- a/web/admin/messages/ar.json +++ b/web/admin/messages/ar.json @@ -1057,6 +1057,7 @@ "fieldCategoryEn": "الفئة بالإنجليزية", "fieldContentFa": "المحتوى بالفارسية (Markdown)", "fieldContentEn": "المحتوى بالإنجليزية (Markdown)", + "fieldPublished": "منشور", "commentsTitle": "إدارة التعليقات", "noComments": "لا توجد تعليقات", "approved": "موافق عليه", diff --git a/web/admin/messages/en.json b/web/admin/messages/en.json index eea4a46..dfe90ec 100644 --- a/web/admin/messages/en.json +++ b/web/admin/messages/en.json @@ -1058,6 +1058,7 @@ "fieldCategoryEn": "Category (English)", "fieldContentFa": "Content (Persian, Markdown)", "fieldContentEn": "Content (English, Markdown)", + "fieldPublished": "Published", "commentsTitle": "Comment management", "noComments": "No comments found", "approved": "Approved", diff --git a/web/admin/messages/fa.json b/web/admin/messages/fa.json index 0d3b472..fa598cd 100644 --- a/web/admin/messages/fa.json +++ b/web/admin/messages/fa.json @@ -1058,6 +1058,7 @@ "fieldCategoryEn": "دسته‌بندی انگلیسی", "fieldContentFa": "محتوا فارسی (Markdown)", "fieldContentEn": "محتوا انگلیسی (Markdown)", + "fieldPublished": "وضعیت انتشار", "commentsTitle": "مدیریت نظرات", "noComments": "نظری یافت نشد", "approved": "تأیید شده", diff --git a/web/admin/src/components/admin/admin-screens.tsx b/web/admin/src/components/admin/admin-screens.tsx index c25c2ad..61c41a2 100644 --- a/web/admin/src/components/admin/admin-screens.tsx +++ b/web/admin/src/components/admin/admin-screens.tsx @@ -62,6 +62,33 @@ function Toggle({ checked, onChange, disabled }: { checked: boolean; onChange: ( ); } +// Styled single-select indicator (replaces raw ). +function RadioDot({ + selected, + onSelect, + disabled, +}: { + selected: boolean; + onSelect: () => void; + disabled?: boolean; +}) { + return ( + + ); +} + export function AdminDashboardScreen() { const t = useTranslations("admin.dashboard"); const { data } = useQuery({ @@ -632,11 +659,9 @@ export function AdminIntegrationsScreen() {
- setActiveGateway(g.id)} + setActiveGateway(g.id)} /> {g.displayNameFa} {activeGateway === g.id ? ( diff --git a/web/admin/src/components/admin/admin-website-screens.tsx b/web/admin/src/components/admin/admin-website-screens.tsx index e022df0..a9101e8 100644 --- a/web/admin/src/components/admin/admin-website-screens.tsx +++ b/web/admin/src/components/admin/admin-website-screens.tsx @@ -16,6 +16,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { notify } from "@/lib/notify"; +import { cn } from "@/lib/utils"; import { Link } from "@/i18n/routing"; import { CheckCircle2, @@ -149,6 +150,74 @@ export function AdminBlogListScreen() { // ── Blog Post Editor ───────────────────────────────────────────────────────── +// iOS-style toggle (mirrors the one in admin-screens.tsx). +function BlogToggle({ + checked, + onChange, +}: { + checked: boolean; + onChange: (v: boolean) => void; +}) { + return ( + + ); +} + +// Module-level so it keeps a stable component identity across renders. +// (Previously defined inside the editor, which remounted every input on each +// keystroke and dropped focus after a single character.) +function BlogField({ + label, + value, + onChange, + multiline, + dir, +}: { + label: string; + value: string; + onChange: (v: string) => void; + multiline?: boolean; + dir: "rtl" | "ltr"; +}) { + return ( +
+ + {multiline ? ( +