mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Add FA/EN locale, home activity charts, and theme-aware polish.
Ship shared LocaleProvider, business/customer i18n, branding defaultLocale, curated home tiles with dual 30-day charts, and chart/page aura tokens; refresh PROJECT_CONTEXT. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
f5b2193ba1
commit
66004a0fba
@@ -55,7 +55,10 @@ import {
|
||||
} from '../utils/businessPrimaryColors'
|
||||
import {
|
||||
getBusinessSettings,
|
||||
updateBusinessBranding,
|
||||
updateBusinessDefaultLocale,
|
||||
updateBusinessPrimaryColor,
|
||||
type DashboardLocale,
|
||||
} from '../services/businessSettingsService'
|
||||
import { flattenBusinessCategories } from '../utils/categories'
|
||||
import { Pagination } from '@meshkee/dashboard-ui'
|
||||
@@ -63,6 +66,11 @@ import pageStyles from '../components/PageContent.module.css'
|
||||
import styles from './BusinessesPage.module.css'
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 10
|
||||
const DEFAULT_LOCALE: DashboardLocale = 'fa'
|
||||
|
||||
function normalizeDefaultLocale(value: unknown): DashboardLocale {
|
||||
return value === 'en' || value === 'fa' ? value : DEFAULT_LOCALE
|
||||
}
|
||||
|
||||
function formatDate(value: string) {
|
||||
const d = new Date(value)
|
||||
@@ -190,6 +198,7 @@ export function BusinessesPage() {
|
||||
const [editPrimaryColor, setEditPrimaryColor] = useState<BusinessPrimaryColorId>(
|
||||
DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
|
||||
)
|
||||
const [editDefaultLocale, setEditDefaultLocale] = useState<DashboardLocale>(DEFAULT_LOCALE)
|
||||
const [editLoadingSettings, setEditLoadingSettings] = useState(false)
|
||||
const [editSubmitting, setEditSubmitting] = useState(false)
|
||||
const [editError, setEditError] = useState('')
|
||||
@@ -216,6 +225,7 @@ export function BusinessesPage() {
|
||||
const [removeTarget, setRemoveTarget] = useState<BusinessListItem | null>(null)
|
||||
const [togglingId, setTogglingId] = useState<string | null>(null)
|
||||
const [savingColorId, setSavingColorId] = useState<string | null>(null)
|
||||
const [savingLocaleId, setSavingLocaleId] = useState<string | null>(null)
|
||||
|
||||
const [migrateOpen, setMigrateOpen] = useState(false)
|
||||
const [migrateBusiness, setMigrateBusiness] = useState<BusinessListItem | null>(null)
|
||||
@@ -327,6 +337,7 @@ export function BusinessesPage() {
|
||||
setEditBusiness(b)
|
||||
setEditName(b.name)
|
||||
setEditPrimaryColor(normalizeBusinessPrimaryColorId(b.primaryColor))
|
||||
setEditDefaultLocale(normalizeDefaultLocale(b.defaultLocale))
|
||||
setEditError('')
|
||||
setEditOpen(true)
|
||||
setEditLoadingSettings(true)
|
||||
@@ -335,6 +346,7 @@ export function BusinessesPage() {
|
||||
void getBusinessSettings(b.id, controller.signal)
|
||||
.then((data) => {
|
||||
setEditPrimaryColor(data.settings.branding.primaryColor)
|
||||
setEditDefaultLocale(normalizeDefaultLocale(data.settings.branding.defaultLocale))
|
||||
})
|
||||
.catch((err) => {
|
||||
if (isAbortError(err)) return
|
||||
@@ -405,20 +417,76 @@ export function BusinessesPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDefaultLocaleChange(b: BusinessListItem, defaultLocale: DashboardLocale) {
|
||||
const currentLocale = normalizeDefaultLocale(b.defaultLocale)
|
||||
if (currentLocale === defaultLocale) return
|
||||
|
||||
setSavingLocaleId(b.id)
|
||||
setError('')
|
||||
|
||||
setData((prev) => {
|
||||
if (!prev) return prev
|
||||
return {
|
||||
...prev,
|
||||
items: prev.items.map((item) =>
|
||||
item.id === b.id ? { ...item, defaultLocale } : item,
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
if (editBusiness?.id === b.id) {
|
||||
setEditDefaultLocale(defaultLocale)
|
||||
}
|
||||
|
||||
try {
|
||||
await updateBusinessDefaultLocale(b.id, defaultLocale)
|
||||
showToast(`Default language updated for "${b.name}".`, 'success')
|
||||
} catch (err) {
|
||||
setData((prev) => {
|
||||
if (!prev) return prev
|
||||
return {
|
||||
...prev,
|
||||
items: prev.items.map((item) =>
|
||||
item.id === b.id ? { ...item, defaultLocale: currentLocale } : item,
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
if (editBusiness?.id === b.id) {
|
||||
setEditDefaultLocale(currentLocale)
|
||||
}
|
||||
|
||||
showToast(
|
||||
err instanceof ApiError ? err.message : 'Unable to update default language.',
|
||||
'error',
|
||||
)
|
||||
} finally {
|
||||
setSavingLocaleId(null)
|
||||
}
|
||||
}
|
||||
|
||||
async function submitEdit() {
|
||||
if (!editBusiness) return
|
||||
setEditSubmitting(true)
|
||||
setEditError('')
|
||||
try {
|
||||
await updateBusiness(editBusiness.id, { name: editName })
|
||||
await updateBusinessPrimaryColor(editBusiness.id, editPrimaryColor)
|
||||
await updateBusinessBranding(editBusiness.id, {
|
||||
primaryColor: editPrimaryColor,
|
||||
defaultLocale: editDefaultLocale,
|
||||
})
|
||||
setData((prev) => {
|
||||
if (!prev) return prev
|
||||
return {
|
||||
...prev,
|
||||
items: prev.items.map((item) =>
|
||||
item.id === editBusiness.id
|
||||
? { ...item, name: editName.trim(), primaryColor: editPrimaryColor }
|
||||
? {
|
||||
...item,
|
||||
name: editName.trim(),
|
||||
primaryColor: editPrimaryColor,
|
||||
defaultLocale: editDefaultLocale,
|
||||
}
|
||||
: item,
|
||||
),
|
||||
}
|
||||
@@ -828,13 +896,14 @@ export function BusinessesPage() {
|
||||
<th className={styles.th}>Domain</th>
|
||||
<th className={styles.th}>Owner</th>
|
||||
<th className={`${styles.th} ${styles.thTheme}`}>Theme</th>
|
||||
<th className={`${styles.th} ${styles.thLocale}`}>Lang</th>
|
||||
<th className={`${styles.th} ${styles.thActions}`}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading && (
|
||||
<tr>
|
||||
<td className={styles.td} colSpan={6}>
|
||||
<td className={styles.td} colSpan={7}>
|
||||
Loading...
|
||||
</td>
|
||||
</tr>
|
||||
@@ -842,7 +911,7 @@ export function BusinessesPage() {
|
||||
|
||||
{!loading && data?.items?.length === 0 && (
|
||||
<tr>
|
||||
<td className={styles.td} colSpan={6}>
|
||||
<td className={styles.td} colSpan={7}>
|
||||
No results found.
|
||||
</td>
|
||||
</tr>
|
||||
@@ -900,6 +969,23 @@ export function BusinessesPage() {
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td className={`${styles.td} ${styles.tdLocale}`}>
|
||||
<select
|
||||
className={styles.localeSelect}
|
||||
value={normalizeDefaultLocale(b.defaultLocale)}
|
||||
disabled={savingLocaleId === b.id}
|
||||
aria-label={`Default language for ${b.name}`}
|
||||
onChange={(e) =>
|
||||
void handleDefaultLocaleChange(
|
||||
b,
|
||||
normalizeDefaultLocale(e.target.value),
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="fa">FA</option>
|
||||
<option value="en">EN</option>
|
||||
</select>
|
||||
</td>
|
||||
<td className={`${styles.td} ${styles.tdActions}`}>
|
||||
<div className={styles.rowActions}>
|
||||
<span className={styles.toggleInActions}>
|
||||
@@ -1058,6 +1144,26 @@ export function BusinessesPage() {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.field}>
|
||||
<label htmlFor="edit-default-locale">Default dashboard language</label>
|
||||
{editLoadingSettings ? (
|
||||
<p className={styles.helperText}>Loading language...</p>
|
||||
) : (
|
||||
<select
|
||||
id="edit-default-locale"
|
||||
className={styles.localeSelect}
|
||||
value={editDefaultLocale}
|
||||
disabled={editSubmitting}
|
||||
onChange={(e) => setEditDefaultLocale(normalizeDefaultLocale(e.target.value))}
|
||||
>
|
||||
<option value="fa">Farsi (FA)</option>
|
||||
<option value="en">English (EN)</option>
|
||||
</select>
|
||||
)}
|
||||
<p className={styles.helperText}>
|
||||
Business and customer dashboards open in this language.
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ height: 12 }} />
|
||||
<div className={styles.actionsRow}>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user