Update website image slot placeholders and leather banner preview.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
e518713f1f
commit
7ea3d619d8
@@ -31,7 +31,7 @@ import {
|
|||||||
updateCustomerRole,
|
updateCustomerRole,
|
||||||
} from "@/lib/customers-api";
|
} from "@/lib/customers-api";
|
||||||
|
|
||||||
const PAGE_SIZE = 5;
|
const PAGE_SIZE = 24;
|
||||||
|
|
||||||
const ROLES: UserRole[] = ["CUSTOMER", "STORE_ADMIN", "SUPER_ADMIN"];
|
const ROLES: UserRole[] = ["CUSTOMER", "STORE_ADMIN", "SUPER_ADMIN"];
|
||||||
const MEMBERSHIPS: SubscriptionStatus[] = ["regular", "premium"];
|
const MEMBERSHIPS: SubscriptionStatus[] = ["regular", "premium"];
|
||||||
@@ -275,7 +275,7 @@ export default function CustomersPage() {
|
|||||||
style={{ animationDelay: `${70 + i * 45}ms` }}
|
style={{ animationDelay: `${70 + i * 45}ms` }}
|
||||||
>
|
>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h2 className="truncate text-base text-ink/65 sm:text-lg">
|
<h2 className="truncate text-sm text-ink/65 sm:text-base">
|
||||||
{customer.nameFa}
|
{customer.nameFa}
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p
|
||||||
@@ -284,9 +284,6 @@ export default function CustomersPage() {
|
|||||||
>
|
>
|
||||||
{customer.phone}
|
{customer.phone}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-[0.7rem] text-muted">
|
|
||||||
{roleLabel(customer.role)}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="customer-stats shrink-0">
|
<div className="customer-stats shrink-0">
|
||||||
|
|||||||
+2
-2
@@ -753,11 +753,11 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 0.75rem;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: var(--radius-card);
|
border-radius: var(--radius-card);
|
||||||
padding: 0.9rem 1rem;
|
padding: 0.5rem 0.85rem;
|
||||||
transition: border-color 0.25s ease;
|
transition: border-color 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
groupSlotSections,
|
groupSlotSections,
|
||||||
heroPreviewObjectPosition,
|
heroPreviewObjectPosition,
|
||||||
normalizeSlotDef,
|
normalizeSlotDef,
|
||||||
|
resolveSlotImageUrl,
|
||||||
type WebsiteSlotDef,
|
type WebsiteSlotDef,
|
||||||
type WebsiteSlotItem,
|
type WebsiteSlotItem,
|
||||||
} from "@/data/website-slots";
|
} from "@/data/website-slots";
|
||||||
@@ -71,9 +72,16 @@ export default function WebsiteImagesPage() {
|
|||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const data = await fetchWebsiteSlots();
|
const data = await fetchWebsiteSlots();
|
||||||
const nextCatalog = (data.catalog?.length ? data.catalog : WEBSITE_SLOT_DEFS)
|
const remote = (data.catalog?.length ? data.catalog : WEBSITE_SLOT_DEFS)
|
||||||
.map((row) => normalizeSlotDef(row as WebsiteSlotDef & { key?: string }))
|
.map((row) => normalizeSlotDef(row as WebsiteSlotDef & { key?: string }))
|
||||||
.filter((row) => row.slug && row.slug !== "payment-badges");
|
.filter((row) => row.slug && row.slug !== "payment-badges");
|
||||||
|
const remoteSlugs = new Set(remote.map((row) => row.slug));
|
||||||
|
const nextCatalog = [
|
||||||
|
...remote,
|
||||||
|
...WEBSITE_SLOT_DEFS.filter((row) => !remoteSlugs.has(row.slug)).map(
|
||||||
|
(row) => normalizeSlotDef(row),
|
||||||
|
),
|
||||||
|
];
|
||||||
setCatalog(nextCatalog.length ? nextCatalog : WEBSITE_SLOT_DEFS);
|
setCatalog(nextCatalog.length ? nextCatalog : WEBSITE_SLOT_DEFS);
|
||||||
setItems(data.items ?? []);
|
setItems(data.items ?? []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -147,10 +155,14 @@ export default function WebsiteImagesPage() {
|
|||||||
setTargetSortOrder(sortOrder);
|
setTargetSortOrder(sortOrder);
|
||||||
const cell =
|
const cell =
|
||||||
sortOrder !== undefined ? def.cellLabels?.[sortOrder] : undefined;
|
sortOrder !== undefined ? def.cellLabels?.[sortOrder] : undefined;
|
||||||
|
const defaultImage = cell?.defaultImage ?? def.defaultImage ?? "";
|
||||||
setForm({
|
setForm({
|
||||||
...emptyForm(),
|
...emptyForm(),
|
||||||
nameFa: cell?.labelFa ?? "",
|
nameFa: cell?.defaultNameFa ?? cell?.labelFa ?? def.defaultNameFa ?? "",
|
||||||
nameEn: cell?.labelEn ?? "",
|
nameEn: cell?.defaultNameEn ?? cell?.labelEn ?? def.defaultNameEn ?? "",
|
||||||
|
subtext: cell?.defaultSubtext ?? def.defaultSubtext ?? "",
|
||||||
|
link: cell?.defaultLink ?? def.defaultLink ?? "",
|
||||||
|
imagePreview: resolveSlotImageUrl(defaultImage) ?? "",
|
||||||
});
|
});
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}
|
}
|
||||||
@@ -159,13 +171,16 @@ export default function WebsiteImagesPage() {
|
|||||||
setEditingId(item.id);
|
setEditingId(item.id);
|
||||||
setSlotSlug(def.slug);
|
setSlotSlug(def.slug);
|
||||||
setTargetSortOrder(item.sortOrder);
|
setTargetSortOrder(item.sortOrder);
|
||||||
|
const cell = def.cellLabels?.[item.sortOrder];
|
||||||
|
const image =
|
||||||
|
item.imageUrl || cell?.defaultImage || def.defaultImage || "";
|
||||||
setForm({
|
setForm({
|
||||||
nameFa: item.nameFa,
|
nameFa: item.nameFa,
|
||||||
nameEn: item.nameEn,
|
nameEn: item.nameEn,
|
||||||
subtext: item.subtext,
|
subtext: item.subtext,
|
||||||
linkText: item.linkText,
|
linkText: item.linkText,
|
||||||
link: item.link,
|
link: item.link,
|
||||||
imagePreview: item.imageUrl ?? "",
|
imagePreview: resolveSlotImageUrl(image) ?? "",
|
||||||
imageFile: null,
|
imageFile: null,
|
||||||
});
|
});
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
@@ -193,6 +208,17 @@ export default function WebsiteImagesPage() {
|
|||||||
const uploaded = await uploadMedia(form.imageFile, "website");
|
const uploaded = await uploadMedia(form.imageFile, "website");
|
||||||
imageUrl = uploaded.url;
|
imageUrl = uploaded.url;
|
||||||
imageKey = uploaded.storageKey;
|
imageKey = uploaded.storageKey;
|
||||||
|
} else {
|
||||||
|
const cell =
|
||||||
|
targetSortOrder !== undefined
|
||||||
|
? activeDef?.cellLabels?.[targetSortOrder]
|
||||||
|
: undefined;
|
||||||
|
const fallback =
|
||||||
|
cell?.defaultImage ?? activeDef?.defaultImage ?? undefined;
|
||||||
|
// Keep storefront-relative defaults when saving without a new upload.
|
||||||
|
if (fallback && !editingId) {
|
||||||
|
imageUrl = fallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -299,7 +325,7 @@ export default function WebsiteImagesPage() {
|
|||||||
<SlotThumb
|
<SlotThumb
|
||||||
key={item.id}
|
key={item.id}
|
||||||
aspectRatio={def.aspectRatio}
|
aspectRatio={def.aspectRatio}
|
||||||
imageUrl={item.imageUrl}
|
imageUrl={resolveSlotImageUrl(item.imageUrl)}
|
||||||
objectPosition={
|
objectPosition={
|
||||||
def.slug === "hero-slider"
|
def.slug === "hero-slider"
|
||||||
? heroPreviewObjectPosition(index)
|
? heroPreviewObjectPosition(index)
|
||||||
@@ -336,25 +362,37 @@ export default function WebsiteImagesPage() {
|
|||||||
cell?.aspectLabel ?? def.aspectLabel,
|
cell?.aspectLabel ?? def.aspectLabel,
|
||||||
);
|
);
|
||||||
const widthRem = cell?.previewWidthRem;
|
const widthRem = cell?.previewWidthRem;
|
||||||
if (item) {
|
const previewImage = resolveSlotImageUrl(
|
||||||
|
item?.imageUrl || cell?.defaultImage,
|
||||||
|
);
|
||||||
|
if (item || previewImage) {
|
||||||
return (
|
return (
|
||||||
<SlotThumb
|
<SlotThumb
|
||||||
key={item.id}
|
key={item?.id ?? `${def.slug}-${index}`}
|
||||||
aspectRatio={cellAr}
|
aspectRatio={cellAr}
|
||||||
widthRem={widthRem}
|
widthRem={widthRem}
|
||||||
imageUrl={item.imageUrl}
|
imageUrl={previewImage}
|
||||||
objectPosition={
|
objectPosition={
|
||||||
cell?.previewObjectPosition ??
|
cell?.previewObjectPosition ??
|
||||||
def.previewObjectPosition
|
def.previewObjectPosition
|
||||||
}
|
}
|
||||||
title={
|
title={
|
||||||
item.nameFa ||
|
item?.nameFa ||
|
||||||
cell?.labelEn ||
|
cell?.defaultNameFa ||
|
||||||
|
cell?.labelFa ||
|
||||||
`${def.slug}-${index + 1}`
|
`${def.slug}-${index + 1}`
|
||||||
}
|
}
|
||||||
subtitle={sizeLabel}
|
subtitle={sizeLabel}
|
||||||
onEdit={() => openEdit(def, item)}
|
onEdit={() =>
|
||||||
onDelete={() => void handleDelete(item.id)}
|
item
|
||||||
|
? openEdit(def, item)
|
||||||
|
: openCreate(def, index)
|
||||||
|
}
|
||||||
|
onDelete={
|
||||||
|
item
|
||||||
|
? () => void handleDelete(item.id)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -375,17 +413,31 @@ export default function WebsiteImagesPage() {
|
|||||||
// fixed
|
// fixed
|
||||||
const item = slotItems[0];
|
const item = slotItems[0];
|
||||||
const sizeLabel = formatImageSize(def.aspectLabel);
|
const sizeLabel = formatImageSize(def.aspectLabel);
|
||||||
if (item) {
|
const previewImage = resolveSlotImageUrl(
|
||||||
|
item?.imageUrl || def.defaultImage,
|
||||||
|
);
|
||||||
|
if (item || previewImage) {
|
||||||
return (
|
return (
|
||||||
<SlotThumb
|
<SlotThumb
|
||||||
key={item.id}
|
key={item?.id ?? def.slug}
|
||||||
aspectRatio={def.aspectRatio}
|
aspectRatio={def.aspectRatio}
|
||||||
imageUrl={item.imageUrl}
|
imageUrl={previewImage}
|
||||||
objectPosition={def.previewObjectPosition}
|
objectPosition={def.previewObjectPosition}
|
||||||
title={item.nameFa || def.slug}
|
title={
|
||||||
|
item?.nameFa ||
|
||||||
|
def.defaultNameFa ||
|
||||||
|
def.labelFa ||
|
||||||
|
def.slug
|
||||||
|
}
|
||||||
subtitle={sizeLabel}
|
subtitle={sizeLabel}
|
||||||
onEdit={() => openEdit(def, item)}
|
onEdit={() =>
|
||||||
onDelete={() => void handleDelete(item.id)}
|
item ? openEdit(def, item) : openCreate(def)
|
||||||
|
}
|
||||||
|
onDelete={
|
||||||
|
item
|
||||||
|
? () => void handleDelete(item.id)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -641,8 +693,13 @@ function SlotThumb({
|
|||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
onDelete: () => void;
|
onDelete?: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
const coverClass =
|
||||||
|
imageUrl?.includes("/images/transparent/")
|
||||||
|
? "object-contain bg-[#ececec] p-2"
|
||||||
|
: `object-cover ${objectPosition ?? "object-center"}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex flex-col gap-2 ${thumbWidthClass(aspectRatio, widthRem) ?? ""}`}
|
className={`flex flex-col gap-2 ${thumbWidthClass(aspectRatio, widthRem) ?? ""}`}
|
||||||
@@ -658,7 +715,7 @@ function SlotThumb({
|
|||||||
alt=""
|
alt=""
|
||||||
fill
|
fill
|
||||||
unoptimized
|
unoptimized
|
||||||
className={`object-cover ${objectPosition ?? "object-center"}`}
|
className={coverClass}
|
||||||
sizes="360px"
|
sizes="360px"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -682,9 +739,11 @@ function SlotThumb({
|
|||||||
<IconButton label="ویرایش" variant="line" onClick={onEdit}>
|
<IconButton label="ویرایش" variant="line" onClick={onEdit}>
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton label="حذف" variant="line" onClick={onDelete}>
|
{onDelete ? (
|
||||||
<TrashIcon />
|
<IconButton label="حذف" variant="line" onClick={onDelete}>
|
||||||
</IconButton>
|
<TrashIcon />
|
||||||
|
</IconButton>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+183
-42
@@ -17,6 +17,12 @@ export type WebsiteSlotDef = {
|
|||||||
* (preview only — uploaded file is full-bleed cover).
|
* (preview only — uploaded file is full-bleed cover).
|
||||||
*/
|
*/
|
||||||
previewObjectPosition?: string;
|
previewObjectPosition?: string;
|
||||||
|
/** Storefront-relative default image when no upload exists yet */
|
||||||
|
defaultImage?: string;
|
||||||
|
defaultNameFa?: string;
|
||||||
|
defaultNameEn?: string;
|
||||||
|
defaultSubtext?: string;
|
||||||
|
defaultLink?: string;
|
||||||
cellLabels?: Array<{
|
cellLabels?: Array<{
|
||||||
labelFa: string;
|
labelFa: string;
|
||||||
labelEn: string;
|
labelEn: string;
|
||||||
@@ -25,6 +31,11 @@ export type WebsiteSlotDef = {
|
|||||||
/** Relative preview width in rem (matches on-site scale) */
|
/** Relative preview width in rem (matches on-site scale) */
|
||||||
previewWidthRem?: number;
|
previewWidthRem?: number;
|
||||||
previewObjectPosition?: string;
|
previewObjectPosition?: string;
|
||||||
|
defaultImage?: string;
|
||||||
|
defaultNameFa?: string;
|
||||||
|
defaultNameEn?: string;
|
||||||
|
defaultSubtext?: string;
|
||||||
|
defaultLink?: string;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -65,52 +76,153 @@ export const WEBSITE_SLOT_DEFS: WebsiteSlotDef[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Homepage CategoryBento (lg): 3×3 grid, height min(62vw, 560px).
|
* Homepage CategoryBento (lg): 6×3 grid inside a 2:1 frame.
|
||||||
* Cell sizes at 1520×560 content (max-w 1600 − lg padding, 8px gaps):
|
* Order: mantel, shirts, pants, bag, scarf, shoe.
|
||||||
* 0: 1×3 tall · 1: 2×1 wide · 2: 1×1 · 3: 1×2 · 4: 1×1
|
|
||||||
*/
|
*/
|
||||||
slug: "category-strip",
|
slug: "category-strip",
|
||||||
labelFa: "دستهبندیها",
|
labelFa: "دستهبندیها",
|
||||||
labelEn: "Categories",
|
labelEn: "Categories",
|
||||||
kind: "fixed-set",
|
kind: "fixed-set",
|
||||||
itemCount: 5,
|
itemCount: 6,
|
||||||
aspectRatio: 501 / 560,
|
aspectRatio: 1,
|
||||||
aspectLabel: "بنتو · ۵ قاب",
|
aspectLabel: "بنتو · ۶ قاب",
|
||||||
cellLabels: [
|
cellLabels: [
|
||||||
{
|
{
|
||||||
labelFa: "قاب بلند",
|
labelFa: "مانتو",
|
||||||
labelEn: "Tall",
|
labelEn: "Mantel",
|
||||||
aspectRatio: 501 / 560,
|
aspectRatio: 1,
|
||||||
aspectLabel: "501×560",
|
aspectLabel: "۱×۱",
|
||||||
previewWidthRem: 7,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
labelFa: "قاب عریض",
|
|
||||||
labelEn: "Wide",
|
|
||||||
aspectRatio: 1011 / 181,
|
|
||||||
aspectLabel: "1011×181",
|
|
||||||
previewWidthRem: 16,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
labelFa: "قاب میانی",
|
|
||||||
labelEn: "Middle",
|
|
||||||
aspectRatio: 501 / 181,
|
|
||||||
aspectLabel: "501×181",
|
|
||||||
previewWidthRem: 9,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
labelFa: "قاب نیمهبلند",
|
|
||||||
labelEn: "Half-tall",
|
|
||||||
aspectRatio: 501 / 371,
|
|
||||||
aspectLabel: "501×371",
|
|
||||||
previewWidthRem: 8,
|
previewWidthRem: 8,
|
||||||
|
defaultImage: "/images/transparent/mantel.png",
|
||||||
|
defaultNameFa: "مانتو",
|
||||||
|
defaultNameEn: "Mantel",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
labelFa: "قاب پایین",
|
labelFa: "پیراهن",
|
||||||
labelEn: "Bottom",
|
labelEn: "Shirts",
|
||||||
aspectRatio: 501 / 181,
|
aspectRatio: 4,
|
||||||
aspectLabel: "501×181",
|
aspectLabel: "۴×۱",
|
||||||
|
previewWidthRem: 14,
|
||||||
|
defaultImage: "/images/transparent/shirt.png",
|
||||||
|
defaultNameFa: "پیراهن",
|
||||||
|
defaultNameEn: "Shirts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "شلوار",
|
||||||
|
labelEn: "Pants",
|
||||||
|
aspectRatio: 2,
|
||||||
|
aspectLabel: "۲×۱",
|
||||||
previewWidthRem: 9,
|
previewWidthRem: 9,
|
||||||
|
defaultImage: "/images/transparent/pants.png",
|
||||||
|
defaultNameFa: "شلوار",
|
||||||
|
defaultNameEn: "Pants",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "کیف زنانه",
|
||||||
|
labelEn: "Bag",
|
||||||
|
aspectRatio: 2,
|
||||||
|
aspectLabel: "۲×۱",
|
||||||
|
previewWidthRem: 9,
|
||||||
|
defaultImage: "/images/transparent/bag.png",
|
||||||
|
defaultNameFa: "کیف زنانه",
|
||||||
|
defaultNameEn: "Wemon Bag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "شال",
|
||||||
|
labelEn: "Scarf",
|
||||||
|
aspectRatio: 3,
|
||||||
|
aspectLabel: "۳×۱",
|
||||||
|
previewWidthRem: 11,
|
||||||
|
defaultImage: "/images/transparent/scarf.png",
|
||||||
|
defaultNameFa: "شال",
|
||||||
|
defaultNameEn: "Scarf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "کفش",
|
||||||
|
labelEn: "Shoe",
|
||||||
|
aspectRatio: 3,
|
||||||
|
aspectLabel: "۳×۱",
|
||||||
|
previewWidthRem: 11,
|
||||||
|
defaultImage: "/images/transparent/shoe.png",
|
||||||
|
defaultNameFa: "کفش",
|
||||||
|
defaultNameEn: "Shoe",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "opening-frame",
|
||||||
|
labelFa: "قاب بازشو",
|
||||||
|
labelEn: "Opening frame",
|
||||||
|
kind: "fixed",
|
||||||
|
sectionKey: "sticky-frames",
|
||||||
|
sectionLabelFa: "قابهای اسکرولی",
|
||||||
|
sectionLabelEn: "Sticky frames",
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
aspectLabel: "۱۶×۹",
|
||||||
|
previewObjectPosition: "object-[center_22%]",
|
||||||
|
defaultImage: "/images/landscapes/frame1.jpg",
|
||||||
|
defaultNameFa: "به سبک مانا",
|
||||||
|
defaultNameEn: "Opening frame",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: "sticky-story",
|
||||||
|
labelFa: "داستان اسکرولی",
|
||||||
|
labelEn: "Sticky story",
|
||||||
|
kind: "fixed-set",
|
||||||
|
sectionKey: "sticky-frames",
|
||||||
|
sectionLabelFa: "قابهای اسکرولی",
|
||||||
|
sectionLabelEn: "Sticky frames",
|
||||||
|
itemCount: 4,
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
aspectLabel: "۱۶×۹",
|
||||||
|
cellLabels: [
|
||||||
|
{
|
||||||
|
labelFa: "مانتو",
|
||||||
|
labelEn: "Mantel",
|
||||||
|
previewObjectPosition: "object-[center_28%]",
|
||||||
|
defaultImage:
|
||||||
|
"/images/landscapes/ian-dooley-C6BrEIucDDU-unsplash.jpg",
|
||||||
|
defaultNameFa: "مانتو",
|
||||||
|
defaultNameEn: "Mantel",
|
||||||
|
defaultSubtext:
|
||||||
|
"مانتوهایی با برش معاصر؛ ساده، شهری و مناسب هر روز.",
|
||||||
|
defaultLink: "/products?category=coat",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "پیراهن",
|
||||||
|
labelEn: "Shirts",
|
||||||
|
previewObjectPosition: "object-[center_35%]",
|
||||||
|
defaultImage:
|
||||||
|
"/images/landscapes/tamara-bellis-nbC4EyCxhb8-unsplash.jpg",
|
||||||
|
defaultNameFa: "پیراهن",
|
||||||
|
defaultNameEn: "Shirts",
|
||||||
|
defaultSubtext:
|
||||||
|
"پیراهنهایی با پارچه لطیف و ایستایی تمیز برای استایل روزمره.",
|
||||||
|
defaultLink: "/products?category=shirts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "شلوار",
|
||||||
|
labelEn: "Pants",
|
||||||
|
previewObjectPosition: "object-[center_30%]",
|
||||||
|
defaultImage:
|
||||||
|
"/images/landscapes/vinicius-amnx-amano-k2yixQS_YH0-unsplash.jpg",
|
||||||
|
defaultNameFa: "شلوار",
|
||||||
|
defaultNameEn: "Pants",
|
||||||
|
defaultSubtext:
|
||||||
|
"شلوارهایی با فرم خوشایست که شکل خود را حفظ میکنند.",
|
||||||
|
defaultLink: "/products?category=pants",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFa: "کیف زنانه",
|
||||||
|
labelEn: "Bags",
|
||||||
|
previewObjectPosition: "object-[center_40%]",
|
||||||
|
defaultImage:
|
||||||
|
"/images/landscapes/mojtaba-mosayebzadeh-1nLTyduE1cY-unsplash.jpg",
|
||||||
|
defaultNameFa: "کیف زنانه",
|
||||||
|
defaultNameEn: "Bags",
|
||||||
|
defaultSubtext:
|
||||||
|
"کیفهایی با جزئیات دقیق؛ تکمیلکننده استایل مانا.",
|
||||||
|
defaultLink: "/products?category=bags",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -154,7 +266,12 @@ export const WEBSITE_SLOT_DEFS: WebsiteSlotDef[] = [
|
|||||||
kind: "fixed",
|
kind: "fixed",
|
||||||
aspectRatio: 1920 / 585,
|
aspectRatio: 1920 / 585,
|
||||||
aspectLabel: "1920×585",
|
aspectLabel: "1920×585",
|
||||||
previewObjectPosition: "object-left-bottom",
|
// Storefront crops mobile to the subject on the left
|
||||||
|
previewObjectPosition: "object-[22%_center]",
|
||||||
|
defaultImage: "/images/home/banners/leather.png",
|
||||||
|
defaultNameFa: "هنر پوشیدن",
|
||||||
|
defaultNameEn: "THE ART OF DRESSING",
|
||||||
|
defaultLink: "/products?collection=leather",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
slug: "four-portrait-banner",
|
slug: "four-portrait-banner",
|
||||||
@@ -239,15 +356,39 @@ export function normalizeSlotDef(raw: WebsiteSlotDefApi): WebsiteSlotDef {
|
|||||||
aspectLabel: local?.aspectLabel || raw.aspectLabel || "",
|
aspectLabel: local?.aspectLabel || raw.aspectLabel || "",
|
||||||
previewObjectPosition:
|
previewObjectPosition:
|
||||||
raw.previewObjectPosition ?? local?.previewObjectPosition,
|
raw.previewObjectPosition ?? local?.previewObjectPosition,
|
||||||
cellLabels: (local?.cellLabels ?? raw.cellLabels)?.map((cell, index) => ({
|
defaultImage: raw.defaultImage ?? local?.defaultImage,
|
||||||
...cell,
|
defaultNameFa: raw.defaultNameFa ?? local?.defaultNameFa,
|
||||||
previewObjectPosition:
|
defaultNameEn: raw.defaultNameEn ?? local?.defaultNameEn,
|
||||||
cell.previewObjectPosition ??
|
defaultSubtext: raw.defaultSubtext ?? local?.defaultSubtext,
|
||||||
local?.cellLabels?.[index]?.previewObjectPosition,
|
defaultLink: raw.defaultLink ?? local?.defaultLink,
|
||||||
})),
|
cellLabels: (local?.cellLabels ?? raw.cellLabels)?.map((cell, index) => {
|
||||||
|
const localCell = local?.cellLabels?.[index];
|
||||||
|
return {
|
||||||
|
...cell,
|
||||||
|
previewObjectPosition:
|
||||||
|
cell.previewObjectPosition ?? localCell?.previewObjectPosition,
|
||||||
|
defaultImage: cell.defaultImage ?? localCell?.defaultImage,
|
||||||
|
defaultNameFa: cell.defaultNameFa ?? localCell?.defaultNameFa,
|
||||||
|
defaultNameEn: cell.defaultNameEn ?? localCell?.defaultNameEn,
|
||||||
|
defaultSubtext: cell.defaultSubtext ?? localCell?.defaultSubtext,
|
||||||
|
defaultLink: cell.defaultLink ?? localCell?.defaultLink,
|
||||||
|
};
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolve storefront-relative paths for dashboard previews. */
|
||||||
|
export function resolveSlotImageUrl(imageUrl?: string | null) {
|
||||||
|
if (!imageUrl) return null;
|
||||||
|
if (/^https?:\/\//i.test(imageUrl) || imageUrl.startsWith("blob:")) {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
const site =
|
||||||
|
process.env.NEXT_PUBLIC_SITE_URL?.replace(/\/$/, "") ||
|
||||||
|
"http://localhost:3000";
|
||||||
|
return `${site}${imageUrl.startsWith("/") ? "" : "/"}${imageUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
/** Same focal points as website `map-home-slots` hero slides */
|
/** Same focal points as website `map-home-slots` hero slides */
|
||||||
export function heroPreviewObjectPosition(index: number) {
|
export function heroPreviewObjectPosition(index: number) {
|
||||||
if (index === 0) return "object-[center_20%]";
|
if (index === 0) return "object-[center_20%]";
|
||||||
|
|||||||
Reference in New Issue
Block a user