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 { listBusinessCategories } from '../services/categoryService'
|
||||||
import { Modal } from '../components/Modal'
|
import { Modal } from '../components/Modal'
|
||||||
import { MultiSelectDropdown } from '../components/MultiSelectDropdown'
|
import { MultiSelectDropdown } from '../components/MultiSelectDropdown'
|
||||||
import { PrimaryColorPicker } from '../components/PrimaryColorPicker'
|
|
||||||
import { PrimaryColorSwatchControl } from '../components/PrimaryColorSwatchControl'
|
import { PrimaryColorSwatchControl } from '../components/PrimaryColorSwatchControl'
|
||||||
import { useToast } from '../context/ToastContext'
|
import { useToast } from '../context/ToastContext'
|
||||||
import {
|
import {
|
||||||
DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
|
|
||||||
normalizeBusinessPrimaryColorId,
|
normalizeBusinessPrimaryColorId,
|
||||||
type BusinessPrimaryColorId,
|
type BusinessPrimaryColorId,
|
||||||
} from '../utils/businessPrimaryColors'
|
} from '../utils/businessPrimaryColors'
|
||||||
import {
|
import {
|
||||||
getBusinessSettings,
|
|
||||||
updateBusinessBranding,
|
|
||||||
updateBusinessDefaultLocale,
|
updateBusinessDefaultLocale,
|
||||||
updateBusinessModules,
|
updateBusinessModules,
|
||||||
updateBusinessPrimaryColor,
|
updateBusinessPrimaryColor,
|
||||||
@@ -215,11 +211,8 @@ export function BusinessesPage() {
|
|||||||
const [editOpen, setEditOpen] = useState(false)
|
const [editOpen, setEditOpen] = useState(false)
|
||||||
const [editBusiness, setEditBusiness] = useState<BusinessListItem | null>(null)
|
const [editBusiness, setEditBusiness] = useState<BusinessListItem | null>(null)
|
||||||
const [editName, setEditName] = useState('')
|
const [editName, setEditName] = useState('')
|
||||||
const [editPrimaryColor, setEditPrimaryColor] = useState<BusinessPrimaryColorId>(
|
const [editNameFa, setEditNameFa] = useState('')
|
||||||
DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
|
const [editAbout, setEditAbout] = useState('')
|
||||||
)
|
|
||||||
const [editDefaultLocale, setEditDefaultLocale] = useState<DashboardLocale>(DEFAULT_LOCALE)
|
|
||||||
const [editLoadingSettings, setEditLoadingSettings] = useState(false)
|
|
||||||
const [editSubmitting, setEditSubmitting] = useState(false)
|
const [editSubmitting, setEditSubmitting] = useState(false)
|
||||||
const [editError, setEditError] = useState('')
|
const [editError, setEditError] = useState('')
|
||||||
|
|
||||||
@@ -370,25 +363,10 @@ export function BusinessesPage() {
|
|||||||
function openEdit(b: BusinessListItem) {
|
function openEdit(b: BusinessListItem) {
|
||||||
setEditBusiness(b)
|
setEditBusiness(b)
|
||||||
setEditName(b.name)
|
setEditName(b.name)
|
||||||
setEditPrimaryColor(normalizeBusinessPrimaryColorId(b.primaryColor))
|
setEditNameFa(b.nameFa ?? '')
|
||||||
setEditDefaultLocale(normalizeDefaultLocale(b.defaultLocale))
|
setEditAbout(b.about ?? '')
|
||||||
setEditError('')
|
setEditError('')
|
||||||
setEditOpen(true)
|
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') {
|
function goToBusinessUsers(b: BusinessListItem, membership: 'staff' | 'all') {
|
||||||
@@ -557,13 +535,17 @@ export function BusinessesPage() {
|
|||||||
|
|
||||||
async function submitEdit() {
|
async function submitEdit() {
|
||||||
if (!editBusiness) return
|
if (!editBusiness) return
|
||||||
|
const name = editName.trim()
|
||||||
|
const nameFa = editNameFa.trim()
|
||||||
|
if (!name || !nameFa) return
|
||||||
|
|
||||||
setEditSubmitting(true)
|
setEditSubmitting(true)
|
||||||
setEditError('')
|
setEditError('')
|
||||||
try {
|
try {
|
||||||
await updateBusiness(editBusiness.id, { name: editName })
|
await updateBusiness(editBusiness.id, {
|
||||||
await updateBusinessBranding(editBusiness.id, {
|
name,
|
||||||
primaryColor: editPrimaryColor,
|
nameFa,
|
||||||
defaultLocale: editDefaultLocale,
|
about: editAbout.trim(),
|
||||||
})
|
})
|
||||||
setData((prev) => {
|
setData((prev) => {
|
||||||
if (!prev) return prev
|
if (!prev) return prev
|
||||||
@@ -573,9 +555,9 @@ export function BusinessesPage() {
|
|||||||
item.id === editBusiness.id
|
item.id === editBusiness.id
|
||||||
? {
|
? {
|
||||||
...item,
|
...item,
|
||||||
name: editName.trim(),
|
name,
|
||||||
primaryColor: editPrimaryColor,
|
nameFa,
|
||||||
defaultLocale: editDefaultLocale,
|
about: editAbout.trim() || null,
|
||||||
}
|
}
|
||||||
: item,
|
: item,
|
||||||
),
|
),
|
||||||
@@ -596,7 +578,7 @@ export function BusinessesPage() {
|
|||||||
setDomainBusiness(b)
|
setDomainBusiness(b)
|
||||||
setDomainId(b.domainId)
|
setDomainId(b.domainId)
|
||||||
setDomainHost(b.domain ?? '')
|
setDomainHost(b.domain ?? '')
|
||||||
setDomainGitRepoUrl('')
|
setDomainGitRepoUrl(b.gitRepoUrl ?? '')
|
||||||
setDomainError('')
|
setDomainError('')
|
||||||
setDomainOpen(true)
|
setDomainOpen(true)
|
||||||
}
|
}
|
||||||
@@ -736,7 +718,15 @@ export function BusinessesPage() {
|
|||||||
setDomainError('')
|
setDomainError('')
|
||||||
try {
|
try {
|
||||||
if (domainId) {
|
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) => {
|
setData((prev) => {
|
||||||
if (!prev) return prev
|
if (!prev) return prev
|
||||||
return {
|
return {
|
||||||
@@ -746,7 +736,17 @@ export function BusinessesPage() {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
showToast(`Domain updated to "${host}".`, 'success')
|
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 {
|
} else {
|
||||||
const created = (await addBusinessDomain(domainBusiness.id, {
|
const created = (await addBusinessDomain(domainBusiness.id, {
|
||||||
host,
|
host,
|
||||||
@@ -1231,45 +1231,37 @@ export function BusinessesPage() {
|
|||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label htmlFor="edit-name">Business name</label>
|
<label htmlFor="edit-name-en">Name (EN)</label>
|
||||||
<input
|
<input
|
||||||
id="edit-name"
|
id="edit-name-en"
|
||||||
value={editName}
|
value={editName}
|
||||||
onChange={(e) => setEditName(e.target.value)}
|
onChange={(e) => setEditName(e.target.value)}
|
||||||
|
placeholder="Business name"
|
||||||
disabled={editSubmitting}
|
disabled={editSubmitting}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label>Dashboard primary color</label>
|
<label htmlFor="edit-name-fa">Name (FA)</label>
|
||||||
{editLoadingSettings ? (
|
<input
|
||||||
<p className={styles.helperText}>Loading theme...</p>
|
id="edit-name-fa"
|
||||||
) : (
|
className="faText"
|
||||||
<PrimaryColorPicker
|
value={editNameFa}
|
||||||
value={editPrimaryColor}
|
onChange={(e) => setEditNameFa(e.target.value)}
|
||||||
onChange={setEditPrimaryColor}
|
placeholder="نام فارسی"
|
||||||
disabled={editSubmitting}
|
dir="rtl"
|
||||||
/>
|
lang="fa"
|
||||||
)}
|
disabled={editSubmitting}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.field}>
|
<div className={styles.field}>
|
||||||
<label htmlFor="edit-default-locale">Default dashboard language</label>
|
<label htmlFor="edit-about">About business</label>
|
||||||
{editLoadingSettings ? (
|
<textarea
|
||||||
<p className={styles.helperText}>Loading language...</p>
|
id="edit-about"
|
||||||
) : (
|
value={editAbout}
|
||||||
<select
|
onChange={(e) => setEditAbout(e.target.value)}
|
||||||
id="edit-default-locale"
|
placeholder="Short description about the business"
|
||||||
className={styles.localeSelect}
|
disabled={editSubmitting}
|
||||||
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>
|
||||||
<div style={{ height: 12 }} />
|
<div style={{ height: 12 }} />
|
||||||
<div className={styles.actionsRow}>
|
<div className={styles.actionsRow}>
|
||||||
@@ -1285,7 +1277,7 @@ export function BusinessesPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
className={`${styles.btn} ${styles.btnPrimary}`}
|
className={`${styles.btn} ${styles.btnPrimary}`}
|
||||||
onClick={() => void submitEdit()}
|
onClick={() => void submitEdit()}
|
||||||
disabled={editSubmitting || !editName.trim() || editLoadingSettings}
|
disabled={editSubmitting || !editName.trim() || !editNameFa.trim()}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
@@ -1318,27 +1310,23 @@ export function BusinessesPage() {
|
|||||||
disabled={domainSubmitting}
|
disabled={domainSubmitting}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!domainId ? (
|
<div style={{ height: 12 }} />
|
||||||
<>
|
<div className={styles.field}>
|
||||||
<div style={{ height: 12 }} />
|
<label htmlFor="domain-git-repo">Git repo URL (optional)</label>
|
||||||
<div className={styles.field}>
|
<input
|
||||||
<label htmlFor="domain-git-repo">Git repo URL (optional)</label>
|
id="domain-git-repo"
|
||||||
<input
|
value={domainGitRepoUrl}
|
||||||
id="domain-git-repo"
|
onChange={(e) => setDomainGitRepoUrl(e.target.value)}
|
||||||
value={domainGitRepoUrl}
|
placeholder="git@git.meshkee.com:Meshkee-Websites/oaktasty.git"
|
||||||
onChange={(e) => setDomainGitRepoUrl(e.target.value)}
|
disabled={domainSubmitting}
|
||||||
placeholder="git@git.meshkee.com:Meshkee-Websites/oaktasty.git"
|
autoComplete="off"
|
||||||
disabled={domainSubmitting}
|
spellCheck={false}
|
||||||
autoComplete="off"
|
/>
|
||||||
spellCheck={false}
|
<p className={styles.subText} style={{ marginTop: 6 }}>
|
||||||
/>
|
Fill to enable storefront Deploy on Websites. Leave empty for domain-only
|
||||||
<p className={styles.subText} style={{ marginTop: 6 }}>
|
(dashboards).
|
||||||
Fill to enable storefront Deploy on Websites. Leave empty for domain-only
|
</p>
|
||||||
(dashboards).
|
</div>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
<div style={{ height: 12 }} />
|
<div style={{ height: 12 }} />
|
||||||
<div className={styles.actionsRow}>
|
<div className={styles.actionsRow}>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export interface ListBusinessesParams {
|
|||||||
|
|
||||||
export interface UpdateBusinessPayload {
|
export interface UpdateBusinessPayload {
|
||||||
name?: string
|
name?: string
|
||||||
|
nameFa?: string
|
||||||
|
about?: string
|
||||||
slug?: string
|
slug?: string
|
||||||
oldBusinessId?: number | null
|
oldBusinessId?: number | null
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,8 @@ export interface DisableBusinessPayload {
|
|||||||
|
|
||||||
export interface UpdateDomainPayload {
|
export interface UpdateDomainPayload {
|
||||||
host: string
|
host: string
|
||||||
|
/** When set, provisions storefront deploy on the websites VM. */
|
||||||
|
gitRepoUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listBusinesses(params: ListBusinessesParams, signal?: AbortSignal) {
|
export async function listBusinesses(params: ListBusinessesParams, signal?: AbortSignal) {
|
||||||
|
|||||||
@@ -15,10 +15,14 @@ export interface BusinessDomainInfo {
|
|||||||
export interface BusinessListItem {
|
export interface BusinessListItem {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
nameFa: string | null
|
||||||
|
about: string | null
|
||||||
createdAt: string
|
createdAt: string
|
||||||
domainId: number | null
|
domainId: number | null
|
||||||
domain: string | null
|
domain: string | null
|
||||||
sslEnabled: boolean | null
|
sslEnabled: boolean | null
|
||||||
|
gitRepoUrl: string | null
|
||||||
|
deploySlug: string | null
|
||||||
ownerName: string | null
|
ownerName: string | null
|
||||||
ownerNameEn: string | null
|
ownerNameEn: string | null
|
||||||
ownerCellNumber: string | null
|
ownerCellNumber: string | null
|
||||||
|
|||||||
Reference in New Issue
Block a user