Update homepage static-image slots and customer login links.

Align slot catalog with slider / our-experts / two-service-banner and send guests to customer portal /login.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-01 21:23:22 +03:30
co-authored by Cursor
parent 29ff589c5b
commit e7e5381b80
8 changed files with 50 additions and 52 deletions
+3 -3
View File
@@ -4,10 +4,10 @@ Read http://meshkee.com/docs/website (static-images). Env is already set.
Fetch slots, match by key:
- slider > duplicatable
- 4 people image > 4 slot
- our-services-icon > 4 slot
- two-banner-bg > 2 slot
- our-experts > duplicatable
- two-service-banner > 2 slot
check the original UI, use name fa, name en and subtex according to it
Use slot.images (all of them if kind=list, first if kind=single).
url → <img src>. linkUrl → wrap in <a>. Empty or fail → keep current placeholder.
+16 -9
View File
@@ -59,12 +59,13 @@ Helpers: `src/lib/meshkee.ts` → `meshkeeGet`, `tenantPath`, `getTenant`, `getB
- **Note:** Tenant currently has no published workshops in API; homepage/list fall back to static samples in `src/data/site.ts`.
### Auth / customer portal
- Login icon → `https://customer.clinicsamen.com` (`customerPortalUrl` in `src/lib/config.ts`)
- Session: `src/lib/session.tsx` + shared cookies (`src/lib/shared-auth-cookie.ts`)
- Login / user icon → `https://customer.{domain}/login` (`customerPortalUrl` in `src/lib/config.ts` → `customer.clinicsamen.com`)
- Session: `src/lib/session.tsx` + shared cookies (`src/lib/shared-auth-cookie.ts`) — reads `meshkee_customer_access_token` from parent domain
- Profile API proxy: `GET /api/auth/me` → Meshkee `/auth/me`
- Admin handoff: `POST /api/auth/handoff` → Meshkee `/auth/handoff` → `https://business.clinicsamen.com`
- Logged-in header: user name + «عزیز»; dropdown: پروفایل من، خروج، مدیریت وبسایت (admins only)
- Component: `src/components/AccountMenu.tsx`
- Logged-in header: `{firstName lastName} عزیز` via `userDisplayName`
- Dropdown (`AccountMenu`): پروفایل من → `/profile`; مدیریت وبسایت (admins/owners/team only, handoff ticket); خروج (clears shared cookies)
- Component: `src/components/AccountMenu.tsx` (wired in `Header` topbar)
## Static image slots
API: `GET /tenants/{domain}/website/static-images?pageKey=home`
@@ -72,17 +73,23 @@ Catalog for dashboard Refresh: `GET /meshkee/static-image-slots` → `STATIC_IMA
| key | kind | itemCount | aspectRatio | recommendedWidth | homepage section |
| --- | --- | --- | --- | --- | --- |
| `slider` | list | null | `1920:740` | 1180 | Hero slider |
| `4-people-image` | list | 4 | `325:356` | 280 | Home therapists row |
| `our-services-icon` | list | 4 | `1:1` | 110 | Services icons |
| `two-banner-bg` | list | 2 | `540:312` | 560 | Dual promo banners |
| `slider` | list (duplicatable) | null | `1920:740` | 1180 | Hero slider |
| `our-experts` | list (duplicatable) | null | `325:356` | 280 | Home therapists / experts row |
| `two-service-banner` | list | 2 | `540:312` | 560 | Dual promo banners under services |
Field → UI mapping (match original homepage; empty API → local placeholders):
- `slider`: `url` → slide image; `linkUrl` → wrap slide. `titleFa` / `titleEn` / `subtext` not rendered in hero (image-only).
- `our-experts`: `url` → photo; `linkUrl` → card link (fallback `/ourdr`); `titleFa` → name (`h3`); `subtext` → role (`p`). `titleEn` not shown (original therapist cards have no English line).
- `two-service-banner`: `url` → banner background; `linkUrl` → CTA href; `titleFa` → banner title (`h3`). `titleEn` / `subtext` not shown on banners.
Service icons on the homepage stay local (`serviceCards` in `src/data/site.ts`) — not a Meshkee static-image slot.
Rules:
- Use `slot.images` (all if `kind=list`, first if `kind=single`)
- `url` → `<img src>`; `linkUrl` → wrap in link
- Empty / fail → keep local placeholders from `src/data/site.ts` / `public/images/`
- Use `slot.aspectRatio` (via `parseAspectRatio`)
- **If a slot’s on-page size or aspect ratio changes in CSS, update `STATIC_IMAGE_SLOTS` in the same change**
- **If a slot’s on-page size or aspect ratio changes in CSS, update `STATIC_IMAGE_SLOTS` in the same change** (Meshkee reads the catalog as the static-image source of truth)
Helpers: `src/lib/static-images.ts`, `src/components/SlotImage.tsx`, `src/components/SlotLink.tsx`
Home wiring: `src/app/page.tsx`
+11 -12
View File
@@ -27,6 +27,7 @@ export default async function HomePage() {
: getLocalBlogPosts();
const workshops = workshopResult.items;
// slider: titleFa / titleEn / subtext unused in hero UI (image + optional linkUrl only)
const slider = overlaySlotImages(
pickSlot(slots, "slider"),
slides.map((slide, index) => {
@@ -41,8 +42,9 @@ export default async function HomePage() {
"slider",
);
// our-experts: titleFa → name, subtext → role (titleEn not shown in original therapist cards)
const people = overlaySlotImages(
pickSlot(slots, "4-people-image"),
pickSlot(slots, "our-experts"),
homeTherapists.map((person, index) => ({
id: `person-${index}`,
image: person.image,
@@ -50,23 +52,21 @@ export default async function HomePage() {
title: person.name,
text: person.role,
})),
"4-people-image",
"our-experts",
);
const serviceIcons = overlaySlotImages(
pickSlot(slots, "our-services-icon"),
serviceCards.map((service, index) => ({
// Local service icons (not a Meshkee static-image slot)
const serviceIcons = serviceCards.map((service, index) => ({
id: `service-${index}`,
image: service.image,
href: service.href,
title: service.title,
kicker: service.english,
})),
"our-services-icon",
);
}));
// two-service-banner: titleFa → banner title (titleEn / subtext not shown in original banner UI)
const banners = overlaySlotImages(
pickSlot(slots, "two-banner-bg"),
pickSlot(slots, "two-service-banner"),
[
{
id: "banner-workshops",
@@ -81,7 +81,7 @@ export default async function HomePage() {
title: "مشاوره فردی",
},
],
"two-banner-bg",
"two-service-banner",
);
return (
@@ -90,8 +90,7 @@ export default async function HomePage() {
<HomeSections
people={people.items}
peopleAspectRatio={people.aspectRatio}
serviceIcons={serviceIcons.items}
serviceIconsAspectRatio={serviceIcons.aspectRatio}
serviceIcons={serviceIcons}
banners={banners.items}
bannersAspectRatio={banners.aspectRatio}
workshops={workshops}
+5 -1
View File
@@ -106,7 +106,11 @@ export default function AccountMenu() {
if (!isAuthenticated || !user) {
return (
<a href={customerPortalUrl} className="account-menu account-menu--login" aria-label="ورود / ثبت‌نام">
<a
href={`${customerPortalUrl}/login`}
className="account-menu account-menu--login"
aria-label="ورود / ثبت‌نام"
>
<UserIcon />
<span>ورود / ثبت‌نام</span>
</a>
+1 -1
View File
@@ -74,7 +74,7 @@ export default function EntityTabs({
<div className="login-prompt">
<p className="login-prompt__title">برای ثبت نظر وارد شوید</p>
<p className="login-prompt__text">نظرات فقط برای کاربران واردشده فعال است.</p>
<a href={customerPortalUrl} className="btn btn-primary">
<a href={`${customerPortalUrl}/login`} className="btn btn-primary">
ورود / ثبت‌نام
</a>
</div>
+1 -4
View File
@@ -14,7 +14,6 @@ type Props = {
people: SlotMediaItem[];
peopleAspectRatio?: string;
serviceIcons: SlotMediaItem[];
serviceIconsAspectRatio?: string;
banners: SlotMediaItem[];
bannersAspectRatio?: string;
workshops: Workshop[];
@@ -25,14 +24,12 @@ export default function HomeSections({
people,
peopleAspectRatio,
serviceIcons,
serviceIconsAspectRatio,
banners,
bannersAspectRatio,
workshops,
articles,
}: Props) {
const peopleRatio = parseAspectRatio(peopleAspectRatio) ?? "325 / 356";
const iconRatio = parseAspectRatio(serviceIconsAspectRatio) ?? "1 / 1";
const bannerRatio = parseAspectRatio(bannersAspectRatio) ?? "540 / 312";
return (
@@ -129,7 +126,7 @@ export default function HomeSections({
<div className="service-grid">
{serviceIcons.map((item) => (
<SlotLink key={item.id} href={item.href || "/#services"} className="service-card">
<div className="service-icon" style={{ aspectRatio: iconRatio }}>
<div className="service-icon" style={{ aspectRatio: "1 / 1" }}>
<SlotImage
src={item.image}
fallbackSrc={item.fallbackSrc}
+1 -1
View File
@@ -92,7 +92,7 @@ export default function WorkshopRegistration({
) : (
<div className="login-prompt login-prompt--inline">
<p className="login-prompt__title">برای ثبت‌نام در کارگاه وارد شوید</p>
<a href={customerPortalUrl} className="btn btn-primary">
<a href={`${customerPortalUrl}/login`} className="btn btn-primary">
ورود / ثبت‌نام
</a>
</div>
+5 -14
View File
@@ -55,26 +55,17 @@ export const STATIC_IMAGE_SLOTS: StaticImageSlotCatalogItem[] = [
recommendedWidth: 1180,
},
{
key: "4-people-image",
label: "Four people images",
key: "our-experts",
label: "Our experts",
kind: "list",
pageKey: "home",
aspectRatio: "325:356",
itemCount: 4,
itemCount: null,
recommendedWidth: 280,
},
{
key: "our-services-icon",
label: "Our services icons",
kind: "list",
pageKey: "home",
aspectRatio: "1:1",
itemCount: 4,
recommendedWidth: 110,
},
{
key: "two-banner-bg",
label: "Two banner backgrounds",
key: "two-service-banner",
label: "Two service banners",
kind: "list",
pageKey: "home",
aspectRatio: "540:312",