diff --git a/apps/business/src/components/AddCustomerModal.tsx b/apps/business/src/components/AddCustomerModal.tsx index bc76d59..ab25797 100644 --- a/apps/business/src/components/AddCustomerModal.tsx +++ b/apps/business/src/components/AddCustomerModal.tsx @@ -70,14 +70,20 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP if (!mounted) return null const canSubmit = - firstName.trim().length >= 2 && - lastName.trim().length >= 2 && + (firstName.trim().length >= 1 || lastName.trim().length >= 1) && toE164CellNumber(cellNumber.trim()).length > 0 async function handleSubmit() { const normalizedCell = toE164CellNumber(cellNumber.trim()) if (!canSubmit || !normalizedCell) return + const trimmedFirst = firstName.trim() + const trimmedLast = lastName.trim() + if (!trimmedFirst && !trimmedLast) { + setError(t('customers.addModal.errorNameRequired')) + return + } + const trimmedPassword = password.trim() if (trimmedPassword || passwordConfirm.trim()) { if (trimmedPassword.length < 8) { @@ -105,8 +111,8 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP try { const created = await createCustomer({ cellNumber: normalizedCell, - firstName: firstName.trim(), - lastName: lastName.trim(), + ...(trimmedFirst ? { firstName: trimmedFirst } : {}), + ...(trimmedLast ? { lastName: trimmedLast } : {}), ...(trimmedFirstEn ? { firstNameEn: trimmedFirstEn } : {}), ...(trimmedLastEn ? { lastNameEn: trimmedLastEn } : {}), ...(trimmedPassword ? { password: trimmedPassword } : {}), diff --git a/apps/business/src/i18n/messages.ts b/apps/business/src/i18n/messages.ts index 88bc03f..6ba8a3d 100644 --- a/apps/business/src/i18n/messages.ts +++ b/apps/business/src/i18n/messages.ts @@ -1162,11 +1162,11 @@ const en = { 'customers.addModal.subtitle': 'Creates a verified customer account or links an existing user to your business.', 'customers.addModal.hint': - 'Password is required only for new accounts. Existing users are added as verified customers.', + 'Password is optional. New accounts without a password can still sign in with SMS OTP.', 'customers.addModal.pointCreate': 'Creates a verified customer account or links an existing user to your business.', 'customers.addModal.pointPassword': - 'Password is required only for new accounts. Existing users are added as verified customers.', + 'Password is optional. New accounts without a password can still sign in with SMS OTP.', 'customers.addModal.firstName': 'First name', 'customers.addModal.lastName': 'Last name', 'customers.addModal.firstNameFa': 'First name (FA)', @@ -1182,6 +1182,7 @@ const en = { 'customers.addModal.cancel': 'Cancel', 'customers.addModal.submit': 'Add customer', 'customers.addModal.error': 'Unable to add customer.', + 'customers.addModal.errorNameRequired': 'Enter a first name or last name.', 'customers.addModal.errorPasswordMatch': 'Passwords do not match.', 'customers.addModal.errorPasswordLength': 'Password must be at least 8 characters.', 'customers.addModal.errorNameEnLength': 'English names must be at least 2 characters.', @@ -3631,11 +3632,11 @@ const fa: Record = { 'customers.addModal.subtitle': 'یک حساب تأییدشده مشتری می‌سازد یا کاربر موجود را به کسب‌وکار شما وصل می‌کند.', 'customers.addModal.hint': - 'رمز عبور فقط برای حساب‌های جدید لازم است. کاربران موجود به‌عنوان مشتری تأییدشده اضافه می‌شوند.', + 'رمز عبور اختیاری است. حساب‌های جدید بدون رمز می‌توانند با پیامک (OTP) وارد شوند.', 'customers.addModal.pointCreate': 'یک حساب تأییدشده مشتری می‌سازد یا کاربر موجود را به کسب‌وکار شما وصل می‌کند.', 'customers.addModal.pointPassword': - 'رمز عبور فقط برای حساب‌های جدید لازم است. کاربران موجود به‌عنوان مشتری تأییدشده اضافه می‌شوند.', + 'رمز عبور اختیاری است. حساب‌های جدید بدون رمز می‌توانند با پیامک (OTP) وارد شوند.', 'customers.addModal.firstName': 'نام', 'customers.addModal.lastName': 'نام خانوادگی', 'customers.addModal.firstNameFa': 'نام (فارسی)', @@ -3651,6 +3652,7 @@ const fa: Record = { 'customers.addModal.cancel': 'انصراف', 'customers.addModal.submit': 'افزودن مشتری', 'customers.addModal.error': 'افزودن مشتری ممکن نشد.', + 'customers.addModal.errorNameRequired': 'نام یا نام خانوادگی را وارد کنید.', 'customers.addModal.errorPasswordMatch': 'رمزهای عبور یکسان نیستند.', 'customers.addModal.errorPasswordLength': 'رمز عبور باید حداقل ۸ کاراکتر باشد.', 'customers.addModal.errorNameEnLength': 'نام انگلیسی باید حداقل ۲ کاراکتر باشد.', diff --git a/apps/business/src/pages/WebsiteSettingsPage.tsx b/apps/business/src/pages/WebsiteSettingsPage.tsx index b00b7ef..1679778 100644 --- a/apps/business/src/pages/WebsiteSettingsPage.tsx +++ b/apps/business/src/pages/WebsiteSettingsPage.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { RefreshCw } from 'lucide-react' import { Breadcrumbs } from '../components/Breadcrumbs' import { PageTitle } from '../components/PageTitle' +import { useTenantBranding } from '../context/TenantBrandingContext' import { useToast } from '../context/ToastContext' import { useT } from '../i18n/useT' import { ApiError } from '../lib/api' @@ -42,6 +43,8 @@ function resolveSitemapUrl(config: WebsiteSitemapConfig | null): string { export function WebsiteSettingsPage() { const t = useT() const { showToast } = useToast() + const { enabledModules } = useTenantBranding() + const storeModuleEnabled = hasBusinessModule(enabledModules, 'store') const [source, setSource] = useState('product') const [sitemapConfig, setSitemapConfig] = useState(null) const [isLoading, setIsLoading] = useState(true) @@ -219,9 +222,11 @@ export function WebsiteSettingsPage() { )} -
- -
+ {storeModuleEnabled ? ( +
+ +
+ ) : null} ) } diff --git a/apps/business/src/services/customerService.ts b/apps/business/src/services/customerService.ts index 50ec393..c6d57f1 100644 --- a/apps/business/src/services/customerService.ts +++ b/apps/business/src/services/customerService.ts @@ -78,8 +78,8 @@ export interface UpdateCustomerPayload { export interface CreateCustomerPayload { cellNumber: string - firstName: string - lastName: string + firstName?: string + lastName?: string firstNameEn?: string lastNameEn?: string password?: string diff --git a/apps/customer/src/pages/LoginPage.tsx b/apps/customer/src/pages/LoginPage.tsx index 2359faa..4575392 100644 --- a/apps/customer/src/pages/LoginPage.tsx +++ b/apps/customer/src/pages/LoginPage.tsx @@ -20,7 +20,6 @@ import { LanguageSelect, PasswordStrengthMeter, isPasswordStrong, useLocale, use import { useT } from '../i18n/useT' import { safeRedirectPath } from '../lib/redirect' import { BackgroundOrbs } from '../components/BackgroundOrbs' -import meshkeeLogo from '../assets/meshkee-logo.png' import styles from './LoginPage.module.css' type AuthView = 'login' | 'signup' | 'forgot' | 'otp' @@ -395,7 +394,7 @@ export function LoginPage() {
- + {logoUrl ? : null}
{businessName || tenantDomain} diff --git a/apps/super-admin/src/pages/BusinessesPage.module.css b/apps/super-admin/src/pages/BusinessesPage.module.css index 381fffe..1af0977 100644 --- a/apps/super-admin/src/pages/BusinessesPage.module.css +++ b/apps/super-admin/src/pages/BusinessesPage.module.css @@ -1,5 +1,5 @@ .filtersPanel { - padding: 14px 16px; + padding: 10px 14px; background: var(--glass-bg); backdrop-filter: blur(var(--blur-glass)); -webkit-backdrop-filter: blur(var(--blur-glass)); @@ -15,8 +15,8 @@ .filtersGrid { display: flex; flex-wrap: wrap; - gap: 10px 12px; - align-items: end; + gap: 8px 12px; + align-items: center; margin: 0; padding: 0; border: none; @@ -26,8 +26,9 @@ display: flex; flex: 1; flex-wrap: wrap; - gap: 10px 12px; + gap: 8px 12px; min-width: 0; + align-items: center; } /* Equal width fields; same 12px gap as field→action buttons. @@ -36,6 +37,16 @@ flex: 1 1 160px; max-width: 280px; min-width: 0; + gap: 0; + margin-top: 0; +} + +.filtersInputs > .field + .field { + margin-top: 0; +} + +.filtersPanel .field label { + display: none; } .filterActions { @@ -369,8 +380,8 @@ } .iconActionBtn { - width: 40px; - height: 40px; + width: var(--field-height); + height: var(--field-height); flex-shrink: 0; display: flex; align-items: center; diff --git a/apps/super-admin/src/pages/BusinessesPage.tsx b/apps/super-admin/src/pages/BusinessesPage.tsx index 4482fdc..0f24c32 100644 --- a/apps/super-admin/src/pages/BusinessesPage.tsx +++ b/apps/super-admin/src/pages/BusinessesPage.tsx @@ -1472,37 +1472,35 @@ export function BusinessesPage() {
-
Filters
-
{ e.preventDefault(); applyFilters() }}>
- setDraftName(e.target.value)} - placeholder="Search by business name" + placeholder="Name" + aria-label="Name" />
- setDraftDomain(e.target.value)} - placeholder="shop-a.local" + placeholder="Domain" + aria-label="Domain" />
- setDraftCategory(e.target.value)} - placeholder="e.g. electronics / phones" + placeholder="Category" + aria-label="Category" />
diff --git a/apps/super-admin/src/pages/SmsTemplatesPage.tsx b/apps/super-admin/src/pages/SmsTemplatesPage.tsx index 39af42f..ef32db5 100644 --- a/apps/super-admin/src/pages/SmsTemplatesPage.tsx +++ b/apps/super-admin/src/pages/SmsTemplatesPage.tsx @@ -202,7 +202,6 @@ export function SmsTemplatesPage() { {error ?

{error}

: null}
-
Filters
{ @@ -212,31 +211,31 @@ export function SmsTemplatesPage() { >
- setDraftName(e.target.value)} - placeholder="Search by name" + placeholder="Name" + aria-label="Name" disabled={loading} />
- setDraftBusiness(e.target.value)} - placeholder="Search by business" + placeholder="Business" + aria-label="Business" disabled={loading} />
- setDraftName(e.target.value)} - placeholder="Search by name" + placeholder="Name" + aria-label="Name" />
- setDraftCell(e.target.value)} - placeholder="0912..." + placeholder="Cell number" + aria-label="Cell number" autoComplete="off" />
- setDraftName(e.target.value)} - placeholder="shop-a.local" + placeholder="Name / domain" + aria-label="Name / domain" />