Use compact unlabeled filter rows across Super Admin lists.
Also ship pending add-customer name flexibility, store-gated e-payment settings, and logo-only customer login branding. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
29a190f02a
commit
15aad9f9d8
@@ -70,14 +70,20 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
|
|||||||
if (!mounted) return null
|
if (!mounted) return null
|
||||||
|
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
firstName.trim().length >= 2 &&
|
(firstName.trim().length >= 1 || lastName.trim().length >= 1) &&
|
||||||
lastName.trim().length >= 2 &&
|
|
||||||
toE164CellNumber(cellNumber.trim()).length > 0
|
toE164CellNumber(cellNumber.trim()).length > 0
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
const normalizedCell = toE164CellNumber(cellNumber.trim())
|
const normalizedCell = toE164CellNumber(cellNumber.trim())
|
||||||
if (!canSubmit || !normalizedCell) return
|
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()
|
const trimmedPassword = password.trim()
|
||||||
if (trimmedPassword || passwordConfirm.trim()) {
|
if (trimmedPassword || passwordConfirm.trim()) {
|
||||||
if (trimmedPassword.length < 8) {
|
if (trimmedPassword.length < 8) {
|
||||||
@@ -105,8 +111,8 @@ export function AddCustomerModal({ open, onClose, onCreated }: AddCustomerModalP
|
|||||||
try {
|
try {
|
||||||
const created = await createCustomer({
|
const created = await createCustomer({
|
||||||
cellNumber: normalizedCell,
|
cellNumber: normalizedCell,
|
||||||
firstName: firstName.trim(),
|
...(trimmedFirst ? { firstName: trimmedFirst } : {}),
|
||||||
lastName: lastName.trim(),
|
...(trimmedLast ? { lastName: trimmedLast } : {}),
|
||||||
...(trimmedFirstEn ? { firstNameEn: trimmedFirstEn } : {}),
|
...(trimmedFirstEn ? { firstNameEn: trimmedFirstEn } : {}),
|
||||||
...(trimmedLastEn ? { lastNameEn: trimmedLastEn } : {}),
|
...(trimmedLastEn ? { lastNameEn: trimmedLastEn } : {}),
|
||||||
...(trimmedPassword ? { password: trimmedPassword } : {}),
|
...(trimmedPassword ? { password: trimmedPassword } : {}),
|
||||||
|
|||||||
@@ -1162,11 +1162,11 @@ const en = {
|
|||||||
'customers.addModal.subtitle':
|
'customers.addModal.subtitle':
|
||||||
'Creates a verified customer account or links an existing user to your business.',
|
'Creates a verified customer account or links an existing user to your business.',
|
||||||
'customers.addModal.hint':
|
'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':
|
'customers.addModal.pointCreate':
|
||||||
'Creates a verified customer account or links an existing user to your business.',
|
'Creates a verified customer account or links an existing user to your business.',
|
||||||
'customers.addModal.pointPassword':
|
'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.firstName': 'First name',
|
||||||
'customers.addModal.lastName': 'Last name',
|
'customers.addModal.lastName': 'Last name',
|
||||||
'customers.addModal.firstNameFa': 'First name (FA)',
|
'customers.addModal.firstNameFa': 'First name (FA)',
|
||||||
@@ -1182,6 +1182,7 @@ const en = {
|
|||||||
'customers.addModal.cancel': 'Cancel',
|
'customers.addModal.cancel': 'Cancel',
|
||||||
'customers.addModal.submit': 'Add customer',
|
'customers.addModal.submit': 'Add customer',
|
||||||
'customers.addModal.error': 'Unable to 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.errorPasswordMatch': 'Passwords do not match.',
|
||||||
'customers.addModal.errorPasswordLength': 'Password must be at least 8 characters.',
|
'customers.addModal.errorPasswordLength': 'Password must be at least 8 characters.',
|
||||||
'customers.addModal.errorNameEnLength': 'English names must be at least 2 characters.',
|
'customers.addModal.errorNameEnLength': 'English names must be at least 2 characters.',
|
||||||
@@ -3631,11 +3632,11 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'customers.addModal.subtitle':
|
'customers.addModal.subtitle':
|
||||||
'یک حساب تأییدشده مشتری میسازد یا کاربر موجود را به کسبوکار شما وصل میکند.',
|
'یک حساب تأییدشده مشتری میسازد یا کاربر موجود را به کسبوکار شما وصل میکند.',
|
||||||
'customers.addModal.hint':
|
'customers.addModal.hint':
|
||||||
'رمز عبور فقط برای حسابهای جدید لازم است. کاربران موجود بهعنوان مشتری تأییدشده اضافه میشوند.',
|
'رمز عبور اختیاری است. حسابهای جدید بدون رمز میتوانند با پیامک (OTP) وارد شوند.',
|
||||||
'customers.addModal.pointCreate':
|
'customers.addModal.pointCreate':
|
||||||
'یک حساب تأییدشده مشتری میسازد یا کاربر موجود را به کسبوکار شما وصل میکند.',
|
'یک حساب تأییدشده مشتری میسازد یا کاربر موجود را به کسبوکار شما وصل میکند.',
|
||||||
'customers.addModal.pointPassword':
|
'customers.addModal.pointPassword':
|
||||||
'رمز عبور فقط برای حسابهای جدید لازم است. کاربران موجود بهعنوان مشتری تأییدشده اضافه میشوند.',
|
'رمز عبور اختیاری است. حسابهای جدید بدون رمز میتوانند با پیامک (OTP) وارد شوند.',
|
||||||
'customers.addModal.firstName': 'نام',
|
'customers.addModal.firstName': 'نام',
|
||||||
'customers.addModal.lastName': 'نام خانوادگی',
|
'customers.addModal.lastName': 'نام خانوادگی',
|
||||||
'customers.addModal.firstNameFa': 'نام (فارسی)',
|
'customers.addModal.firstNameFa': 'نام (فارسی)',
|
||||||
@@ -3651,6 +3652,7 @@ const fa: Record<MessageKey, string> = {
|
|||||||
'customers.addModal.cancel': 'انصراف',
|
'customers.addModal.cancel': 'انصراف',
|
||||||
'customers.addModal.submit': 'افزودن مشتری',
|
'customers.addModal.submit': 'افزودن مشتری',
|
||||||
'customers.addModal.error': 'افزودن مشتری ممکن نشد.',
|
'customers.addModal.error': 'افزودن مشتری ممکن نشد.',
|
||||||
|
'customers.addModal.errorNameRequired': 'نام یا نام خانوادگی را وارد کنید.',
|
||||||
'customers.addModal.errorPasswordMatch': 'رمزهای عبور یکسان نیستند.',
|
'customers.addModal.errorPasswordMatch': 'رمزهای عبور یکسان نیستند.',
|
||||||
'customers.addModal.errorPasswordLength': 'رمز عبور باید حداقل ۸ کاراکتر باشد.',
|
'customers.addModal.errorPasswordLength': 'رمز عبور باید حداقل ۸ کاراکتر باشد.',
|
||||||
'customers.addModal.errorNameEnLength': 'نام انگلیسی باید حداقل ۲ کاراکتر باشد.',
|
'customers.addModal.errorNameEnLength': 'نام انگلیسی باید حداقل ۲ کاراکتر باشد.',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { RefreshCw } from 'lucide-react'
|
import { RefreshCw } from 'lucide-react'
|
||||||
import { Breadcrumbs } from '../components/Breadcrumbs'
|
import { Breadcrumbs } from '../components/Breadcrumbs'
|
||||||
import { PageTitle } from '../components/PageTitle'
|
import { PageTitle } from '../components/PageTitle'
|
||||||
|
import { useTenantBranding } from '../context/TenantBrandingContext'
|
||||||
import { useToast } from '../context/ToastContext'
|
import { useToast } from '../context/ToastContext'
|
||||||
import { useT } from '../i18n/useT'
|
import { useT } from '../i18n/useT'
|
||||||
import { ApiError } from '../lib/api'
|
import { ApiError } from '../lib/api'
|
||||||
@@ -42,6 +43,8 @@ function resolveSitemapUrl(config: WebsiteSitemapConfig | null): string {
|
|||||||
export function WebsiteSettingsPage() {
|
export function WebsiteSettingsPage() {
|
||||||
const t = useT()
|
const t = useT()
|
||||||
const { showToast } = useToast()
|
const { showToast } = useToast()
|
||||||
|
const { enabledModules } = useTenantBranding()
|
||||||
|
const storeModuleEnabled = hasBusinessModule(enabledModules, 'store')
|
||||||
const [source, setSource] = useState<SpecialProductsSource>('product')
|
const [source, setSource] = useState<SpecialProductsSource>('product')
|
||||||
const [sitemapConfig, setSitemapConfig] = useState<WebsiteSitemapConfig | null>(null)
|
const [sitemapConfig, setSitemapConfig] = useState<WebsiteSitemapConfig | null>(null)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
@@ -219,9 +222,11 @@ export function WebsiteSettingsPage() {
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div className={`${styles.ePaymentBlock} ${settingsStyles.stack}`}>
|
{storeModuleEnabled ? (
|
||||||
<EPaymentSettingsSection />
|
<div className={`${styles.ePaymentBlock} ${settingsStyles.stack}`}>
|
||||||
</div>
|
<EPaymentSettingsSection />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ export interface UpdateCustomerPayload {
|
|||||||
|
|
||||||
export interface CreateCustomerPayload {
|
export interface CreateCustomerPayload {
|
||||||
cellNumber: string
|
cellNumber: string
|
||||||
firstName: string
|
firstName?: string
|
||||||
lastName: string
|
lastName?: string
|
||||||
firstNameEn?: string
|
firstNameEn?: string
|
||||||
lastNameEn?: string
|
lastNameEn?: string
|
||||||
password?: string
|
password?: string
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { LanguageSelect, PasswordStrengthMeter, isPasswordStrong, useLocale, use
|
|||||||
import { useT } from '../i18n/useT'
|
import { useT } from '../i18n/useT'
|
||||||
import { safeRedirectPath } from '../lib/redirect'
|
import { safeRedirectPath } from '../lib/redirect'
|
||||||
import { BackgroundOrbs } from '../components/BackgroundOrbs'
|
import { BackgroundOrbs } from '../components/BackgroundOrbs'
|
||||||
import meshkeeLogo from '../assets/meshkee-logo.png'
|
|
||||||
import styles from './LoginPage.module.css'
|
import styles from './LoginPage.module.css'
|
||||||
|
|
||||||
type AuthView = 'login' | 'signup' | 'forgot' | 'otp'
|
type AuthView = 'login' | 'signup' | 'forgot' | 'otp'
|
||||||
@@ -395,7 +394,7 @@ export function LoginPage() {
|
|||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<BackgroundOrbs />
|
<BackgroundOrbs />
|
||||||
<div className={styles.brand}>
|
<div className={styles.brand}>
|
||||||
<img src={logoUrl || meshkeeLogo} alt="" className={styles.logo} />
|
{logoUrl ? <img src={logoUrl} alt="" className={styles.logo} /> : null}
|
||||||
<div className={styles.brandText}>
|
<div className={styles.brandText}>
|
||||||
<span className={styles.domain} lang={locale === 'fa' ? 'fa' : undefined}>
|
<span className={styles.domain} lang={locale === 'fa' ? 'fa' : undefined}>
|
||||||
{businessName || tenantDomain}
|
{businessName || tenantDomain}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.filtersPanel {
|
.filtersPanel {
|
||||||
padding: 14px 16px;
|
padding: 10px 14px;
|
||||||
background: var(--glass-bg);
|
background: var(--glass-bg);
|
||||||
backdrop-filter: blur(var(--blur-glass));
|
backdrop-filter: blur(var(--blur-glass));
|
||||||
-webkit-backdrop-filter: blur(var(--blur-glass));
|
-webkit-backdrop-filter: blur(var(--blur-glass));
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
.filtersGrid {
|
.filtersGrid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px 12px;
|
gap: 8px 12px;
|
||||||
align-items: end;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -26,8 +26,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px 12px;
|
gap: 8px 12px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Equal width fields; same 12px gap as field→action buttons.
|
/* Equal width fields; same 12px gap as field→action buttons.
|
||||||
@@ -36,6 +37,16 @@
|
|||||||
flex: 1 1 160px;
|
flex: 1 1 160px;
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
gap: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filtersInputs > .field + .field {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filtersPanel .field label {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filterActions {
|
.filterActions {
|
||||||
@@ -369,8 +380,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.iconActionBtn {
|
.iconActionBtn {
|
||||||
width: 40px;
|
width: var(--field-height);
|
||||||
height: 40px;
|
height: var(--field-height);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1472,37 +1472,35 @@ export function BusinessesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.filtersPanel}>
|
<div className={styles.filtersPanel}>
|
||||||
<div className={styles.filtersTitle}>Filters</div>
|
|
||||||
|
|
||||||
<form className={styles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
<form className={styles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
||||||
<div className={styles.filtersInputs}>
|
<div className={styles.filtersInputs}>
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label htmlFor="filter-name">Name</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-name"
|
id="filter-name"
|
||||||
value={draftName}
|
value={draftName}
|
||||||
onChange={(e) => setDraftName(e.target.value)}
|
onChange={(e) => setDraftName(e.target.value)}
|
||||||
placeholder="Search by business name"
|
placeholder="Name"
|
||||||
|
aria-label="Name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label htmlFor="filter-domain">Domain</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-domain"
|
id="filter-domain"
|
||||||
value={draftDomain}
|
value={draftDomain}
|
||||||
onChange={(e) => setDraftDomain(e.target.value)}
|
onChange={(e) => setDraftDomain(e.target.value)}
|
||||||
placeholder="shop-a.local"
|
placeholder="Domain"
|
||||||
|
aria-label="Domain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label htmlFor="filter-category">Category</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-category"
|
id="filter-category"
|
||||||
value={draftCategory}
|
value={draftCategory}
|
||||||
onChange={(e) => setDraftCategory(e.target.value)}
|
onChange={(e) => setDraftCategory(e.target.value)}
|
||||||
placeholder="e.g. electronics / phones"
|
placeholder="Category"
|
||||||
|
aria-label="Category"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ export function SmsTemplatesPage() {
|
|||||||
{error ? <p className={styles.alertError}>{error}</p> : null}
|
{error ? <p className={styles.alertError}>{error}</p> : null}
|
||||||
|
|
||||||
<div className={tableStyles.filtersPanel}>
|
<div className={tableStyles.filtersPanel}>
|
||||||
<div className={tableStyles.filtersTitle}>Filters</div>
|
|
||||||
<form
|
<form
|
||||||
className={tableStyles.filtersGrid}
|
className={tableStyles.filtersGrid}
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
@@ -212,31 +211,31 @@ export function SmsTemplatesPage() {
|
|||||||
>
|
>
|
||||||
<div className={tableStyles.filtersInputs}>
|
<div className={tableStyles.filtersInputs}>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="sms-template-name">Name</label>
|
|
||||||
<input
|
<input
|
||||||
id="sms-template-name"
|
id="sms-template-name"
|
||||||
value={draftName}
|
value={draftName}
|
||||||
onChange={(e) => setDraftName(e.target.value)}
|
onChange={(e) => setDraftName(e.target.value)}
|
||||||
placeholder="Search by name"
|
placeholder="Name"
|
||||||
|
aria-label="Name"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="sms-template-business">Business</label>
|
|
||||||
<input
|
<input
|
||||||
id="sms-template-business"
|
id="sms-template-business"
|
||||||
value={draftBusiness}
|
value={draftBusiness}
|
||||||
onChange={(e) => setDraftBusiness(e.target.value)}
|
onChange={(e) => setDraftBusiness(e.target.value)}
|
||||||
placeholder="Search by business"
|
placeholder="Business"
|
||||||
|
aria-label="Business"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="sms-template-status">Status</label>
|
|
||||||
<select
|
<select
|
||||||
id="sms-template-status"
|
id="sms-template-status"
|
||||||
value={draftStatus}
|
value={draftStatus}
|
||||||
onChange={(e) => setDraftStatus(e.target.value as StatusFilter)}
|
onChange={(e) => setDraftStatus(e.target.value as StatusFilter)}
|
||||||
|
aria-label="Status"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<option value="all">All (pending + verified)</option>
|
<option value="all">All (pending + verified)</option>
|
||||||
|
|||||||
@@ -576,34 +576,33 @@ export function UsersPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={tableStyles.filtersPanel}>
|
<div className={tableStyles.filtersPanel}>
|
||||||
<div className={tableStyles.filtersTitle}>Filters</div>
|
|
||||||
<form className={tableStyles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
<form className={tableStyles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
||||||
<div className={tableStyles.filtersInputs}>
|
<div className={tableStyles.filtersInputs}>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="filter-user-name">Name</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-user-name"
|
id="filter-user-name"
|
||||||
value={draftName}
|
value={draftName}
|
||||||
onChange={(e) => setDraftName(e.target.value)}
|
onChange={(e) => setDraftName(e.target.value)}
|
||||||
placeholder="Search by name"
|
placeholder="Name"
|
||||||
|
aria-label="Name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="filter-user-cell">Cell number</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-user-cell"
|
id="filter-user-cell"
|
||||||
value={draftCell}
|
value={draftCell}
|
||||||
onChange={(e) => setDraftCell(e.target.value)}
|
onChange={(e) => setDraftCell(e.target.value)}
|
||||||
placeholder="0912..."
|
placeholder="Cell number"
|
||||||
|
aria-label="Cell number"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="filter-user-role">Role</label>
|
|
||||||
<select
|
<select
|
||||||
id="filter-user-role"
|
id="filter-user-role"
|
||||||
value={draftRole}
|
value={draftRole}
|
||||||
onChange={(e) => setDraftRole(e.target.value)}
|
onChange={(e) => setDraftRole(e.target.value)}
|
||||||
|
aria-label="Role"
|
||||||
>
|
>
|
||||||
<option value="">All roles</option>
|
<option value="">All roles</option>
|
||||||
{roles.map((role) => (
|
{roles.map((role) => (
|
||||||
|
|||||||
@@ -515,16 +515,15 @@ export function WebsitesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={tableStyles.filtersPanel}>
|
<div className={tableStyles.filtersPanel}>
|
||||||
<div className={tableStyles.filtersTitle}>Filters</div>
|
|
||||||
<form className={tableStyles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
<form className={tableStyles.filtersGrid} onSubmit={(e) => { e.preventDefault(); applyFilters() }}>
|
||||||
<div className={tableStyles.filtersInputs}>
|
<div className={tableStyles.filtersInputs}>
|
||||||
<div className={tableStyles.field}>
|
<div className={tableStyles.field}>
|
||||||
<label htmlFor="filter-domain-name">Name</label>
|
|
||||||
<input
|
<input
|
||||||
id="filter-domain-name"
|
id="filter-domain-name"
|
||||||
value={draftName}
|
value={draftName}
|
||||||
onChange={(e) => setDraftName(e.target.value)}
|
onChange={(e) => setDraftName(e.target.value)}
|
||||||
placeholder="shop-a.local"
|
placeholder="Name / domain"
|
||||||
|
aria-label="Name / domain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user