Add business workshop management UI with Jalali scheduling and shared dashboard polish.
Includes workshop list/edit/details, registrations modal, category tree, shared EmptyState/RichTextEditor/toasts, and fixes for the Jalali date picker and thumbnail field layout. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
89269dff04
commit
d5aca8bea7
@@ -22,6 +22,11 @@ import {
|
||||
listStoreSpecials,
|
||||
updateStoreSpecial,
|
||||
} from '../services/storeSpecialService'
|
||||
import {
|
||||
getSettings,
|
||||
type SpecialProductsSource,
|
||||
} from '../services/settingsService'
|
||||
import { hasBusinessModule, type BusinessModuleId } from '../utils/businessModules'
|
||||
import type { StoreItem } from '../services/storeItemService'
|
||||
import type { StoreSpecial } from '../types/storeSpecial'
|
||||
import type { StoreProductListing } from '../utils/storeProductGroups'
|
||||
@@ -29,11 +34,23 @@ import pageStyles from '../components/PageContent.module.css'
|
||||
import fabStyles from './StoreItemsPage.module.css'
|
||||
import styles from './StoreSpecialsPage.module.css'
|
||||
|
||||
function defaultSpecialProductsSource(
|
||||
enabledModules: string[] | undefined,
|
||||
): SpecialProductsSource {
|
||||
return hasBusinessModule(
|
||||
enabledModules as BusinessModuleId[] | undefined,
|
||||
'store',
|
||||
)
|
||||
? 'store_item'
|
||||
: 'product'
|
||||
}
|
||||
|
||||
export function StoreSpecialsPage() {
|
||||
const t = useT()
|
||||
const { itemCount, hasItems, addVariant } = useDraftCart()
|
||||
const { showToast } = useToast()
|
||||
const [specials, setSpecials] = useState<StoreSpecial[]>([])
|
||||
const [specialSource, setSpecialSource] = useState<SpecialProductsSource>('store_item')
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
@@ -61,10 +78,36 @@ export function StoreSpecialsPage() {
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
void loadSpecials(controller.signal)
|
||||
void loadPage(controller.signal)
|
||||
return () => controller.abort()
|
||||
}, [])
|
||||
|
||||
async function loadPage(signal?: AbortSignal) {
|
||||
setIsLoading(true)
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const [specialsData, settingsData] = await Promise.all([
|
||||
listStoreSpecials(1, 50, signal),
|
||||
getSettings(signal),
|
||||
])
|
||||
setSpecials(specialsData.items)
|
||||
setSpecialSource(
|
||||
settingsData.settings.website?.specialProductsSource ??
|
||||
defaultSpecialProductsSource(settingsData.settings.modules?.enabled),
|
||||
)
|
||||
} catch (err) {
|
||||
if (err instanceof DOMException && err.name === 'AbortError') return
|
||||
if (err instanceof ApiError) {
|
||||
setError(err.message)
|
||||
} else {
|
||||
setError(t('website.specialItems.errorLoad'))
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSpecials(signal?: AbortSignal) {
|
||||
setIsLoading(true)
|
||||
setError('')
|
||||
@@ -212,19 +255,23 @@ export function StoreSpecialsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddItems(storeItemIds: string[]) {
|
||||
async function handleAddItems(ids: string[]) {
|
||||
if (!pickItemsTarget) return
|
||||
|
||||
const existingIds = pickItemsTarget.items.map((item) => item.id)
|
||||
const nextIds = [...existingIds, ...storeItemIds]
|
||||
const fromProducts = specialSource === 'product'
|
||||
const existingIds = pickItemsTarget.items.map((item) =>
|
||||
fromProducts ? item.productId : item.id,
|
||||
)
|
||||
const nextIds = [...existingIds, ...ids]
|
||||
|
||||
setIsSaving(true)
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const result = await updateStoreSpecial(pickItemsTarget.id, {
|
||||
storeItemIds: nextIds,
|
||||
})
|
||||
const result = await updateStoreSpecial(
|
||||
pickItemsTarget.id,
|
||||
fromProducts ? { productIds: nextIds } : { storeItemIds: nextIds },
|
||||
)
|
||||
replaceSpecial(result.special)
|
||||
setPickItemsTarget(null)
|
||||
showToast(t('website.specialItems.toastAdded'), 'success')
|
||||
@@ -402,7 +449,12 @@ export function StoreSpecialsPage() {
|
||||
<PickStoreSpecialItemsModal
|
||||
open={!!pickItemsTarget}
|
||||
specialTitle={pickItemsTarget?.title ?? ''}
|
||||
existingStoreItemIds={pickItemsTarget?.items.map((item) => item.id) ?? []}
|
||||
source={specialSource}
|
||||
existingIds={
|
||||
pickItemsTarget?.items.map((item) =>
|
||||
specialSource === 'product' ? item.productId : item.id,
|
||||
) ?? []
|
||||
}
|
||||
onClose={() => !isSaving && setPickItemsTarget(null)}
|
||||
onConfirm={handleAddItems}
|
||||
isSubmitting={isSaving}
|
||||
|
||||
Reference in New Issue
Block a user