From dc570c1e7f27dbf3e3dd1307bc201c4ba0115231 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Tue, 25 Aug 2026 16:40:52 +0330 Subject: [PATCH] Share customer session cookies with the storefront and polish business settings. Keep JWT login mirrored on the tenant cookie domain, improve e-payment settings UX, and add a storefront link from the customer sidebar. Co-authored-by: Cursor --- .../src/components/VariationsModal.module.css | 7 +- .../src/components/VariationsModal.tsx | 12 +- apps/business/src/i18n/messages.ts | 4 +- .../src/pages/AddCustomerProductPage.tsx | 16 +- .../src/pages/AddNewPortfolioPage.tsx | 14 +- apps/business/src/pages/AddNewProductPage.tsx | 11 +- .../business/src/pages/AddNewWorkshopPage.tsx | 11 +- .../src/pages/SettingsPage.module.css | 99 ++++++++++- apps/business/src/pages/SettingsPage.tsx | 166 +++++++----------- apps/business/src/pages/StoreSettingsPage.tsx | 1 - apps/business/src/services/mediaService.ts | 13 ++ apps/business/src/services/settingsService.ts | 4 +- apps/customer/.env.example | 3 + .../src/components/Sidebar.module.css | 17 ++ apps/customer/src/components/Sidebar.tsx | 28 ++- apps/customer/src/i18n/messages.ts | 2 + apps/customer/src/lib/api.ts | 62 ++++++- apps/customer/src/lib/guestCart.ts | 13 +- apps/customer/src/lib/sharedAuthCookie.ts | 124 +++++++++++++ apps/customer/src/services/websiteService.ts | 15 +- 20 files changed, 463 insertions(+), 159 deletions(-) create mode 100644 apps/customer/src/lib/sharedAuthCookie.ts diff --git a/apps/business/src/components/VariationsModal.module.css b/apps/business/src/components/VariationsModal.module.css index 2c7a22f..6e30b5c 100644 --- a/apps/business/src/components/VariationsModal.module.css +++ b/apps/business/src/components/VariationsModal.module.css @@ -57,6 +57,7 @@ font-size: 13px; color: var(--text-secondary); margin-top: 4px; + text-align: start; } .modalHint { @@ -122,14 +123,13 @@ font-size: 14px; font-weight: 600; color: var(--text-primary); + text-align: start; } .listItemName:global(.faText), .listItemValues:global(.faText), .subtitle:global(.faText) { font-weight: 500; - text-align: left; - unicode-bidi: plaintext; } .listItemValues { @@ -139,6 +139,7 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + text-align: start; } .listItemEnd { @@ -146,7 +147,7 @@ align-items: center; gap: 8px; flex-shrink: 0; - margin-left: auto; + margin-inline-start: auto; } .listItemType { diff --git a/apps/business/src/components/VariationsModal.tsx b/apps/business/src/components/VariationsModal.tsx index c1d79e9..73ae9bf 100644 --- a/apps/business/src/components/VariationsModal.tsx +++ b/apps/business/src/components/VariationsModal.tsx @@ -124,8 +124,8 @@ export function VariationsModal({ {categoryName && (

{categoryName}

@@ -149,15 +149,15 @@ export function VariationsModal({
{title}
{valuesText}
diff --git a/apps/business/src/i18n/messages.ts b/apps/business/src/i18n/messages.ts index bbf261b..98f66fe 100644 --- a/apps/business/src/i18n/messages.ts +++ b/apps/business/src/i18n/messages.ts @@ -1441,7 +1441,7 @@ const en = { 'portfolio.form.abstract': 'Abstract', 'portfolio.form.abstractPlaceholder': 'Short summary shown in portfolio listings', 'portfolio.form.link': 'Website link', - 'portfolio.form.linkPlaceholder': 'https://example.com', + 'portfolio.form.linkPlaceholder': 'example.com', 'portfolio.form.mainText': 'Main Text', 'portfolio.form.mainTextPlaceholder': 'Full portfolio content with formatting and images...', 'portfolio.form.gallery': 'Image Gallery', @@ -3624,7 +3624,7 @@ const fa: Record = { 'portfolio.form.abstract': 'خلاصه', 'portfolio.form.abstractPlaceholder': 'خلاصه کوتاه برای نمایش در فهرست نمونه کارها', 'portfolio.form.link': 'لینک وبسایت', - 'portfolio.form.linkPlaceholder': 'https://example.com', + 'portfolio.form.linkPlaceholder': 'example.com', 'portfolio.form.mainText': 'متن اصلی', 'portfolio.form.mainTextPlaceholder': 'محتوای کامل نمونه کار با قالب‌بندی و تصاویر...', 'portfolio.form.gallery': 'گالری تصاویر', diff --git a/apps/business/src/pages/AddCustomerProductPage.tsx b/apps/business/src/pages/AddCustomerProductPage.tsx index 26a5832..9a9dc2a 100644 --- a/apps/business/src/pages/AddCustomerProductPage.tsx +++ b/apps/business/src/pages/AddCustomerProductPage.tsx @@ -32,7 +32,7 @@ import { type UserProductPriceCurrency, type UserProductTechnicalValueInput, } from '../services/userProductsService' -import { resolveDataUrlToMediaId, resolveDataUrlsToMediaIds } from '../services/mediaService' +import { galleryMediaIdsAfterChange, resolveDataUrlToMediaId, resolveDataUrlsToMediaIds } from '../services/mediaService' import { formatIrtInput, parseIrtInput } from '../utils/irtPrice' import styles from './AddCustomerProductPage.module.css' @@ -744,20 +744,10 @@ export function AddCustomerProductPage() { { - const prevUrlToId = new Map( - gallery.map((url, index) => [ - url, - galleryMediaIds[index] ?? '', - ]), + setGalleryMediaIds( + galleryMediaIdsAfterChange(gallery, galleryMediaIds, next), ) setGallery(next) - setGalleryMediaIds( - next.map((url) => - url.startsWith('data:') - ? '' - : prevUrlToId.get(url) ?? '', - ), - ) }} />
diff --git a/apps/business/src/pages/AddNewPortfolioPage.tsx b/apps/business/src/pages/AddNewPortfolioPage.tsx index 14663a6..efa145c 100644 --- a/apps/business/src/pages/AddNewPortfolioPage.tsx +++ b/apps/business/src/pages/AddNewPortfolioPage.tsx @@ -14,6 +14,7 @@ import { mapPortfolioCategoryToUi, } from '../services/portfolioCategoryService' import { + galleryMediaIdsAfterChange, resolveDataUrlToMediaId, resolveDataUrlsToMediaIds, resolveHtmlEmbeddedDataUrls, @@ -329,10 +330,11 @@ export function AddNewPortfolioPage() { setProjectUrl(e.target.value)} @@ -354,7 +356,15 @@ export function AddNewPortfolioPage() {
- + { + setGalleryMediaIds( + galleryMediaIdsAfterChange(images, galleryMediaIds, next), + ) + setImages(next) + }} + />
diff --git a/apps/business/src/pages/AddNewProductPage.tsx b/apps/business/src/pages/AddNewProductPage.tsx index 4a952a0..e6dde08 100644 --- a/apps/business/src/pages/AddNewProductPage.tsx +++ b/apps/business/src/pages/AddNewProductPage.tsx @@ -14,6 +14,7 @@ import { ApiError } from '../lib/api' import { listProductCategories, mapProductCategoryToUi } from '../services/productCategoryService' import { listAllBrands, mapBrandToSelectOption } from '../services/brandService' import { + galleryMediaIdsAfterChange, resolveDataUrlToMediaId, resolveDataUrlsToMediaIds, } from '../services/mediaService' @@ -372,7 +373,15 @@ export function AddNewProductPage() {
- + { + setGalleryMediaIds( + galleryMediaIdsAfterChange(images, galleryMediaIds, next), + ) + setImages(next) + }} + />
diff --git a/apps/business/src/pages/AddNewWorkshopPage.tsx b/apps/business/src/pages/AddNewWorkshopPage.tsx index 025d22f..4057916 100644 --- a/apps/business/src/pages/AddNewWorkshopPage.tsx +++ b/apps/business/src/pages/AddNewWorkshopPage.tsx @@ -15,6 +15,7 @@ import { mapWorkshopCategoryToUi, } from '../services/workshopCategoryService' import { + galleryMediaIdsAfterChange, resolveDataUrlToMediaId, resolveDataUrlsToMediaIds, resolveHtmlEmbeddedDataUrls, @@ -353,7 +354,15 @@ export function AddNewWorkshopPage() {
- + { + setGalleryMediaIds( + galleryMediaIdsAfterChange(images, galleryMediaIds, next), + ) + setImages(next) + }} + />
diff --git a/apps/business/src/pages/SettingsPage.module.css b/apps/business/src/pages/SettingsPage.module.css index 9ae9279..86f1885 100644 --- a/apps/business/src/pages/SettingsPage.module.css +++ b/apps/business/src/pages/SettingsPage.module.css @@ -43,10 +43,27 @@ .credentials { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); - gap: 12px 16px; - margin-top: 18px; - padding-top: 18px; + gap: 10px 12px; + margin-top: 14px; + padding-top: 14px; border-top: 1px solid rgba(148, 163, 184, 0.2); + align-items: end; +} + +.credentialsInline { + display: flex; + align-items: center; + gap: 10px 12px; + margin-top: 14px; + padding-top: 14px; + border-top: 1px solid rgba(148, 163, 184, 0.2); +} + +.credentialsDisabled { + filter: blur(2px); + opacity: 0.55; + pointer-events: none; + user-select: none; } .field { @@ -54,8 +71,38 @@ min-width: 0; } -.fullRow { - grid-column: 1 / -1; +.inlineLabel { + flex-shrink: 0; + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); + white-space: nowrap; +} + +.inlineInput { + flex: 1; + min-width: 0; + min-height: var(--field-height); + padding: var(--field-padding-y) var(--field-padding-x); + font-size: var(--field-font-size); + line-height: 1.4; + font-family: var(--font-ui); + color: var(--text-primary); + background-color: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(148, 163, 184, 0.35); + border-radius: var(--radius-sm); + outline: none; + box-sizing: border-box; +} + +.inlineInput:focus { + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(var(--primary-rgb) / 0.12); +} + +.inlineInput:disabled { + opacity: 0.7; + cursor: not-allowed; } .hint { @@ -90,15 +137,22 @@ border-radius: var(--radius-sm); } -.passwordToggle:hover { +.passwordToggle:hover:not(:disabled) { color: var(--text-primary); background: rgba(148, 163, 184, 0.15); } -.actions { - grid-column: 1 / -1; +.passwordToggle:disabled { + cursor: not-allowed; + opacity: 0.6; +} + +.actionsInline { + grid-column: span 3; display: flex; + align-items: flex-end; justify-content: flex-end; + min-width: 0; } .saveBtn { @@ -112,6 +166,8 @@ font-weight: 600; font-family: var(--font-ui); cursor: pointer; + flex-shrink: 0; + white-space: nowrap; } .saveBtn:disabled { @@ -143,10 +199,35 @@ grid-template-columns: 1fr; } - .field { + .field, + .actionsInline { grid-column: 1 / -1; } + .actionsInline { + justify-content: stretch; + } + + .actionsInline .saveBtn { + width: 100%; + } + + .credentialsInline { + flex-wrap: wrap; + } + + .inlineLabel { + width: 100%; + } + + .inlineInput { + flex: 1 1 100%; + } + + .credentialsInline .saveBtn { + width: 100%; + } + .gatewayHeader { flex-direction: column; } diff --git a/apps/business/src/pages/SettingsPage.tsx b/apps/business/src/pages/SettingsPage.tsx index 656e130..05152a0 100644 --- a/apps/business/src/pages/SettingsPage.tsx +++ b/apps/business/src/pages/SettingsPage.tsx @@ -104,16 +104,22 @@ export function SettingsPage() { try { const payload: EPaymentSettings = { - ...next, + enabled: next.enabled, + defaultGateway: next.defaultGateway, gateways: { - ...next.gateways, mellat: { - ...next.gateways.mellat, + enabled: next.gateways.mellat.enabled, + terminalId: next.gateways.mellat.terminalId, + username: next.gateways.mellat.username, password: password ?? '', }, + sep: { enabled: next.gateways.sep.enabled }, + snappay: { enabled: next.gateways.snappay.enabled }, + digipay: { enabled: next.gateways.digipay.enabled }, zarinpal: { - ...next.gateways.zarinpal, + enabled: next.gateways.zarinpal.enabled, merchantId: next.gateways.zarinpal.merchantId.trim(), + sandbox: next.gateways.zarinpal.sandbox, }, }, } @@ -180,15 +186,6 @@ export function SettingsPage() { return } - if ( - ePayment.gateways.mellat.enabled && - !ePayment.gateways.mellat.passwordSet && - !passwordDraft.trim() - ) { - setError(t('website.ePayment.error.passwordRequired')) - return - } - await patchEPayment( 'mellat-credentials', { @@ -284,12 +281,18 @@ export function SettingsPage() { void handleMellatToggle(checked)} />
-
+