feat(catalog): improve visual manager search and offer discovery
This commit is contained in:
1 parent
08321df2aa
commit
93a7e9a7c2
9 files changed
+505
-72
No files matched your search
@@ -102,6 +102,7 @@ export interface PageUpdateData {
|
||||
}
|
||||
|
||||
interface InlineEditorProps {
|
||||
initialOfferId?: number | null;
|
||||
pageId: number | null;
|
||||
onSaved: (updated: PageUpdateData) => void;
|
||||
}
|
||||
@@ -115,7 +116,11 @@ export function InlineEditor(props: InlineEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
function InlineEditorSession({
|
||||
pageId,
|
||||
onSaved,
|
||||
initialOfferId,
|
||||
}: InlineEditorProps) {
|
||||
const pageRequests = useLatestRequest();
|
||||
const itemRequests = useLatestRequest();
|
||||
const saveRequests = useLatestRequest();
|
||||
@@ -135,7 +140,9 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
const [form, setForm] = useState<FormState>({});
|
||||
const [original, setOriginal] = useState<FormState>({});
|
||||
const [showTexts, setShowTexts] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("settings");
|
||||
const [activeTab, setActiveTab] = useState(
|
||||
initialOfferId ? "items" : "settings",
|
||||
);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
|
||||
// Items/translate data (lazy loaded)
|
||||
@@ -247,7 +254,7 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
loadPage(pageId);
|
||||
setItemsData(null);
|
||||
setItemsLoaded(false);
|
||||
setActiveTab("settings");
|
||||
setActiveTab(initialOfferId ? "items" : "settings");
|
||||
setShowPreview(false);
|
||||
} else {
|
||||
setPage(null);
|
||||
@@ -256,7 +263,7 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
setItemsData(null);
|
||||
setItemsLoaded(false);
|
||||
}
|
||||
}, [pageId, loadPage]);
|
||||
}, [pageId, loadPage, initialOfferId]);
|
||||
|
||||
// Lazy-load items (shop catalog only — BC items live on the detail page)
|
||||
useEffect(() => {
|
||||
@@ -360,14 +367,14 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
return (
|
||||
<div className="flex-1 flex flex-col min-h-0">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b shrink-0">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 px-4 py-2 border-b shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<CatalogIcon
|
||||
iconImage={(form.iconImage as number) ?? page.iconImage}
|
||||
size={24}
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{(form.caption as string) || page.caption}
|
||||
</h3>
|
||||
@@ -379,13 +386,13 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
>
|
||||
{form.pageLayout as string}
|
||||
</Badge>
|
||||
{isDirty && (
|
||||
{(isDirty || offersDirty) && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="h-4 px-1.5 text-[9px] text-[var(--admin-warning)] border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]"
|
||||
>
|
||||
<AlertCircle className="h-2.5 w-2.5 mr-0.5" />
|
||||
unsaved
|
||||
{t("unsaved")}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
@@ -397,9 +404,13 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
</div>
|
||||
</div>
|
||||
<Link href={pageHref} target="_blank">
|
||||
<Button variant="ghost" size="sm" className="h-7 text-xs gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs gap-1 text-[var(--admin-text)]"
|
||||
>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
Full editor
|
||||
{t("fullEditor")}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -408,13 +419,7 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
{isDirty && (
|
||||
<div className="flex items-center gap-2 px-4 py-1.5 bg-[var(--admin-warning-subtle)] border-b border-[var(--admin-warning-border)] text-xs text-[var(--admin-warning)] shrink-0">
|
||||
<AlertCircle className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="flex-1">
|
||||
Unsaved changes — press{" "}
|
||||
<kbd className="rounded border bg-background px-1 font-mono text-[10px]">
|
||||
Ctrl+S
|
||||
</kbd>{" "}
|
||||
to save
|
||||
</span>
|
||||
<span className="flex-1">{t("saveHint")}</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@@ -768,6 +773,9 @@ function InlineEditorSession({ pageId, onSaved }: InlineEditorProps) {
|
||||
|
||||
{/* Items table */}
|
||||
<CatalogItemsTable
|
||||
initialSearch={
|
||||
initialOfferId ? `#${initialOfferId}` : undefined
|
||||
}
|
||||
onDirtyChange={setOffersDirty}
|
||||
onRefresh={reloadItems}
|
||||
items={itemsData.items}
|
||||
|
||||
Reference in new issue
Block a user