mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Improve business edit: names/about, and git URL on domain edit.
Replace color/locale in Edit business with EN/FA name and about; show git repo on Edit domain to provision storefront. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
7d6c1cd4e3
commit
f4a087089e
@@ -43,17 +43,13 @@ import {
|
||||
import { listBusinessCategories } from '../services/categoryService'
|
||||
import { Modal } from '../components/Modal'
|
||||
import { MultiSelectDropdown } from '../components/MultiSelectDropdown'
|
||||
import { PrimaryColorPicker } from '../components/PrimaryColorPicker'
|
||||
import { PrimaryColorSwatchControl } from '../components/PrimaryColorSwatchControl'
|
||||
import { useToast } from '../context/ToastContext'
|
||||
import {
|
||||
DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
|
||||
normalizeBusinessPrimaryColorId,
|
||||
type BusinessPrimaryColorId,
|
||||
} from '../utils/businessPrimaryColors'
|
||||
import {
|
||||
getBusinessSettings,
|
||||
updateBusinessBranding,
|
||||
updateBusinessDefaultLocale,
|
||||
updateBusinessModules,
|
||||
updateBusinessPrimaryColor,
|
||||
@@ -215,11 +211,8 @@ export function BusinessesPage() {
|
||||
const [editOpen, setEditOpen] = useState(false)
|
||||
const [editBusiness, setEditBusiness] = useState<BusinessListItem | null>(null)
|
||||
const [editName, setEditName] = useState('')
|
||||
const [editPrimaryColor, setEditPrimaryColor] = useState<BusinessPrimaryColorId>(
|
||||
DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
|
||||
)
|
||||
const [editDefaultLocale, setEditDefaultLocale] = useState<DashboardLocale>(DEFAULT_LOCALE)
|
||||
const [editLoadingSettings, setEditLoadingSettings] = useState(false)
|
||||
const [editNameFa, setEditNameFa] = useState('')
|
||||
const [editAbout, setEditAbout] = useState('')
|
||||
const [editSubmitting, setEditSubmitting] = useState(false)
|
||||
const [editError, setEditError] = useState('')
|
||||
|
||||
@@ -370,25 +363,10 @@ export function BusinessesPage() {
|
||||
function openEdit(b: BusinessListItem) {
|
||||
setEditBusiness(b)
|
||||
setEditName(b.name)
|
||||
setEditPrimaryColor(normalizeBusinessPrimaryColorId(b.primaryColor))
|
||||
setEditDefaultLocale(normalizeDefaultLocale(b.defaultLocale))
|
||||
setEditNameFa(b.nameFa ?? '')
|
||||
setEditAbout(b.about ?? '')
|
||||
setEditError('')
|
||||
setEditOpen(true)
|
||||
setEditLoadingSettings(true)
|
||||
|
||||
const controller = new AbortController()
|
||||
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
|
||||
setEditError(err instanceof ApiError ? err.message : 'Unable to load business theme.')
|
||||
})
|
||||
.finally(() => {
|
||||
if (!controller.signal.aborted) setEditLoadingSettings(false)
|
||||
})
|
||||
}
|
||||
|
||||
function goToBusinessUsers(b: BusinessListItem, membership: 'staff' | 'all') {
|
||||
@@ -557,13 +535,17 @@ export function BusinessesPage() {
|
||||
|
||||
async function submitEdit() {
|
||||
if (!editBusiness) return
|
||||
const name = editName.trim()
|
||||
const nameFa = editNameFa.trim()
|
||||
if (!name || !nameFa) return
|
||||
|
||||
setEditSubmitting(true)
|
||||
setEditError('')
|
||||
try {
|
||||
await updateBusiness(editBusiness.id, { name: editName })
|
||||
await updateBusinessBranding(editBusiness.id, {
|
||||
primaryColor: editPrimaryColor,
|
||||
defaultLocale: editDefaultLocale,
|
||||
await updateBusiness(editBusiness.id, {
|
||||
name,
|
||||
nameFa,
|
||||
about: editAbout.trim(),
|
||||
})
|
||||
setData((prev) => {
|
||||
if (!prev) return prev
|
||||
@@ -573,9 +555,9 @@ export function BusinessesPage() {
|
||||
item.id === editBusiness.id
|
||||
? {
|
||||
...item,
|
||||
name: editName.trim(),
|
||||
primaryColor: editPrimaryColor,
|
||||
defaultLocale: editDefaultLocale,
|
||||
name,
|
||||
nameFa,
|
||||
about: editAbout.trim() || null,
|
||||
}
|
||||
: item,
|
||||
),
|
||||
@@ -596,7 +578,7 @@ export function BusinessesPage() {
|
||||
setDomainBusiness(b)
|
||||
setDomainId(b.domainId)
|
||||
setDomainHost(b.domain ?? '')
|
||||
setDomainGitRepoUrl('')
|
||||
setDomainGitRepoUrl(b.gitRepoUrl ?? '')
|
||||
setDomainError('')
|
||||
setDomainOpen(true)
|
||||
}
|
||||
@@ -736,7 +718,15 @@ export function BusinessesPage() {
|
||||
setDomainError('')
|
||||
try {
|
||||
if (domainId) {
|
||||
await updateBusinessDomain(domainBusiness.id, domainId, { host })
|
||||
const updated = (await updateBusinessDomain(domainBusiness.id, domainId, {
|
||||
host,
|
||||
...(gitRepoUrl ? { gitRepoUrl } : {}),
|
||||
})) as {
|
||||
id: number
|
||||
host: string
|
||||
deploySlug?: string | null
|
||||
provisionError?: string | null
|
||||
}
|
||||
setData((prev) => {
|
||||
if (!prev) return prev
|
||||
return {
|
||||
@@ -746,7 +736,17 @@ export function BusinessesPage() {
|
||||
),
|
||||
}
|
||||
})
|
||||
if (updated.provisionError) {
|
||||
showToast(`Domain updated to "${host}", but storefront setup failed.`, 'error')
|
||||
showToast(updated.provisionError, 'error')
|
||||
} else if (gitRepoUrl && updated.deploySlug) {
|
||||
showToast(
|
||||
`Domain updated. Storefront deploy is ready on Websites.`,
|
||||
'success',
|
||||
)
|
||||
} else {
|
||||
showToast(`Domain updated to "${host}".`, 'success')
|
||||
}
|
||||
} else {
|
||||
const created = (await addBusinessDomain(domainBusiness.id, {
|
||||
host,
|
||||
@@ -1231,45 +1231,37 @@ export function BusinessesPage() {
|
||||
</p>
|
||||
) : null}
|
||||
<div className={styles.field}>
|
||||
<label htmlFor="edit-name">Business name</label>
|
||||
<label htmlFor="edit-name-en">Name (EN)</label>
|
||||
<input
|
||||
id="edit-name"
|
||||
id="edit-name-en"
|
||||
value={editName}
|
||||
onChange={(e) => setEditName(e.target.value)}
|
||||
placeholder="Business name"
|
||||
disabled={editSubmitting}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.field}>
|
||||
<label>Dashboard primary color</label>
|
||||
{editLoadingSettings ? (
|
||||
<p className={styles.helperText}>Loading theme...</p>
|
||||
) : (
|
||||
<PrimaryColorPicker
|
||||
value={editPrimaryColor}
|
||||
onChange={setEditPrimaryColor}
|
||||
<label htmlFor="edit-name-fa">Name (FA)</label>
|
||||
<input
|
||||
id="edit-name-fa"
|
||||
className="faText"
|
||||
value={editNameFa}
|
||||
onChange={(e) => setEditNameFa(e.target.value)}
|
||||
placeholder="نام فارسی"
|
||||
dir="rtl"
|
||||
lang="fa"
|
||||
disabled={editSubmitting}
|
||||
/>
|
||||
)}
|
||||
</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}
|
||||
<label htmlFor="edit-about">About business</label>
|
||||
<textarea
|
||||
id="edit-about"
|
||||
value={editAbout}
|
||||
onChange={(e) => setEditAbout(e.target.value)}
|
||||
placeholder="Short description about the business"
|
||||
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}>
|
||||
@@ -1285,7 +1277,7 @@ export function BusinessesPage() {
|
||||
type="button"
|
||||
className={`${styles.btn} ${styles.btnPrimary}`}
|
||||
onClick={() => void submitEdit()}
|
||||
disabled={editSubmitting || !editName.trim() || editLoadingSettings}
|
||||
disabled={editSubmitting || !editName.trim() || !editNameFa.trim()}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
@@ -1318,8 +1310,6 @@ export function BusinessesPage() {
|
||||
disabled={domainSubmitting}
|
||||
/>
|
||||
</div>
|
||||
{!domainId ? (
|
||||
<>
|
||||
<div style={{ height: 12 }} />
|
||||
<div className={styles.field}>
|
||||
<label htmlFor="domain-git-repo">Git repo URL (optional)</label>
|
||||
@@ -1337,8 +1327,6 @@ export function BusinessesPage() {
|
||||
(dashboards).
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div style={{ height: 12 }} />
|
||||
<div className={styles.actionsRow}>
|
||||
<button
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface ListBusinessesParams {
|
||||
|
||||
export interface UpdateBusinessPayload {
|
||||
name?: string
|
||||
nameFa?: string
|
||||
about?: string
|
||||
slug?: string
|
||||
oldBusinessId?: number | null
|
||||
}
|
||||
@@ -36,6 +38,8 @@ export interface DisableBusinessPayload {
|
||||
|
||||
export interface UpdateDomainPayload {
|
||||
host: string
|
||||
/** When set, provisions storefront deploy on the websites VM. */
|
||||
gitRepoUrl?: string
|
||||
}
|
||||
|
||||
export async function listBusinesses(params: ListBusinessesParams, signal?: AbortSignal) {
|
||||
|
||||
@@ -15,10 +15,14 @@ export interface BusinessDomainInfo {
|
||||
export interface BusinessListItem {
|
||||
id: string
|
||||
name: string
|
||||
nameFa: string | null
|
||||
about: string | null
|
||||
createdAt: string
|
||||
domainId: number | null
|
||||
domain: string | null
|
||||
sslEnabled: boolean | null
|
||||
gitRepoUrl: string | null
|
||||
deploySlug: string | null
|
||||
ownerName: string | null
|
||||
ownerNameEn: string | null
|
||||
ownerCellNumber: string | null
|
||||
|
||||
Reference in New Issue
Block a user