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 <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
c289de5d2e
commit
dc570c1e7f
@@ -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 {
|
||||
|
||||
@@ -124,8 +124,8 @@ export function VariationsModal({
|
||||
{categoryName && (
|
||||
<p
|
||||
className={[styles.subtitle, categoryLocale.className].filter(Boolean).join(' ')}
|
||||
lang={categoryLocale.lang}
|
||||
dir={categoryLocale.dir}
|
||||
lang={isFa ? 'fa' : categoryLocale.lang}
|
||||
dir={isFa ? 'rtl' : categoryLocale.dir}
|
||||
>
|
||||
{categoryName}
|
||||
</p>
|
||||
@@ -149,15 +149,15 @@ export function VariationsModal({
|
||||
<div className={styles.listItemInfo}>
|
||||
<div
|
||||
className={[styles.listItemName, nameLocale.className].filter(Boolean).join(' ')}
|
||||
lang={nameLocale.lang}
|
||||
dir={nameLocale.dir}
|
||||
lang={isFa ? 'fa' : nameLocale.lang}
|
||||
dir={isFa ? 'rtl' : nameLocale.dir}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={[styles.listItemValues, valuesLocale.className].filter(Boolean).join(' ')}
|
||||
lang={valuesLocale.lang}
|
||||
dir={valuesLocale.dir}
|
||||
lang={isFa ? 'fa' : valuesLocale.lang}
|
||||
dir={isFa ? 'rtl' : valuesLocale.dir}
|
||||
>
|
||||
{valuesText}
|
||||
</div>
|
||||
|
||||
@@ -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<MessageKey, string> = {
|
||||
'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': 'گالری تصاویر',
|
||||
|
||||
@@ -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() {
|
||||
<ImageUploader
|
||||
images={gallery}
|
||||
onChange={(next) => {
|
||||
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) ?? '',
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
mapPortfolioCategoryToUi,
|
||||
} from '../services/portfolioCategoryService'
|
||||
import {
|
||||
galleryMediaIdsAfterChange,
|
||||
resolveDataUrlToMediaId,
|
||||
resolveDataUrlsToMediaIds,
|
||||
resolveHtmlEmbeddedDataUrls,
|
||||
@@ -329,10 +330,11 @@ export function AddNewPortfolioPage() {
|
||||
<input
|
||||
id="portfolio-link"
|
||||
name="projectUrl"
|
||||
type="url"
|
||||
type="text"
|
||||
dir="ltr"
|
||||
lang="en"
|
||||
inputMode="url"
|
||||
autoComplete="url"
|
||||
placeholder={t('portfolio.form.linkPlaceholder')}
|
||||
value={projectUrl}
|
||||
onChange={(e) => setProjectUrl(e.target.value)}
|
||||
@@ -354,7 +356,15 @@ export function AddNewPortfolioPage() {
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>{t('portfolio.form.gallery')}</label>
|
||||
<ImageUploader images={images} onChange={setImages} />
|
||||
<ImageUploader
|
||||
images={images}
|
||||
onChange={(next) => {
|
||||
setGalleryMediaIds(
|
||||
galleryMediaIdsAfterChange(images, galleryMediaIds, next),
|
||||
)
|
||||
setImages(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>{t('products.form.images')}</label>
|
||||
<ImageUploader images={images} onChange={setImages} />
|
||||
<ImageUploader
|
||||
images={images}
|
||||
onChange={(next) => {
|
||||
setGalleryMediaIds(
|
||||
galleryMediaIdsAfterChange(images, galleryMediaIds, next),
|
||||
)
|
||||
setImages(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
mapWorkshopCategoryToUi,
|
||||
} from '../services/workshopCategoryService'
|
||||
import {
|
||||
galleryMediaIdsAfterChange,
|
||||
resolveDataUrlToMediaId,
|
||||
resolveDataUrlsToMediaIds,
|
||||
resolveHtmlEmbeddedDataUrls,
|
||||
@@ -353,7 +354,15 @@ export function AddNewWorkshopPage() {
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
<label>{t('workshop.form.gallery')}</label>
|
||||
<ImageUploader images={images} onChange={setImages} />
|
||||
<ImageUploader
|
||||
images={images}
|
||||
onChange={(next) => {
|
||||
setGalleryMediaIds(
|
||||
galleryMediaIdsAfterChange(images, galleryMediaIds, next),
|
||||
)
|
||||
setImages(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col12}`}>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
<Switch
|
||||
id="epayment-mellat"
|
||||
checked={ePayment.gateways.mellat.enabled}
|
||||
disabled={savingKey !== null || !ePayment.enabled}
|
||||
disabled={savingKey !== null}
|
||||
onChange={(checked) => void handleMellatToggle(checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form className={styles.credentials} onSubmit={handleSaveMellatCredentials}>
|
||||
<form
|
||||
className={`${styles.credentials} ${
|
||||
!ePayment.gateways.mellat.enabled ? styles.credentialsDisabled : ''
|
||||
}`}
|
||||
onSubmit={handleSaveMellatCredentials}
|
||||
aria-disabled={!ePayment.gateways.mellat.enabled}
|
||||
>
|
||||
<div className={`${fieldStyles.field} ${styles.field}`}>
|
||||
<label htmlFor="mellat-terminal">
|
||||
{t('website.ePayment.terminalId')}
|
||||
@@ -301,7 +304,7 @@ export function SettingsPage() {
|
||||
autoComplete="off"
|
||||
dir="ltr"
|
||||
value={ePayment.gateways.mellat.terminalId}
|
||||
disabled={savingKey !== null}
|
||||
disabled={savingKey !== null || !ePayment.gateways.mellat.enabled}
|
||||
onChange={(e) =>
|
||||
setEPayment((current) => ({
|
||||
...current,
|
||||
@@ -327,7 +330,7 @@ export function SettingsPage() {
|
||||
autoComplete="off"
|
||||
dir="ltr"
|
||||
value={ePayment.gateways.mellat.username}
|
||||
disabled={savingKey !== null}
|
||||
disabled={savingKey !== null || !ePayment.gateways.mellat.enabled}
|
||||
onChange={(e) =>
|
||||
setEPayment((current) => ({
|
||||
...current,
|
||||
@@ -346,12 +349,6 @@ export function SettingsPage() {
|
||||
<div className={`${fieldStyles.field} ${styles.field}`}>
|
||||
<label htmlFor="mellat-password">
|
||||
{t('website.ePayment.password')}
|
||||
{ePayment.gateways.mellat.passwordSet ? (
|
||||
<span className={styles.hint}>
|
||||
{' '}
|
||||
({t('website.ePayment.passwordSet')})
|
||||
</span>
|
||||
) : null}
|
||||
</label>
|
||||
<div className={styles.passwordWrap}>
|
||||
<input
|
||||
@@ -359,13 +356,9 @@ export function SettingsPage() {
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
autoComplete="new-password"
|
||||
dir="ltr"
|
||||
placeholder={
|
||||
ePayment.gateways.mellat.passwordSet
|
||||
? t('website.ePayment.passwordPlaceholderSet')
|
||||
: t('website.ePayment.passwordPlaceholder')
|
||||
}
|
||||
placeholder={t('website.ePayment.passwordPlaceholderSet')}
|
||||
value={passwordDraft}
|
||||
disabled={savingKey !== null}
|
||||
disabled={savingKey !== null || !ePayment.gateways.mellat.enabled}
|
||||
onChange={(e) => setPasswordDraft(e.target.value)}
|
||||
/>
|
||||
<Tooltip
|
||||
@@ -379,6 +372,7 @@ export function SettingsPage() {
|
||||
type="button"
|
||||
className={styles.passwordToggle}
|
||||
onClick={() => setShowPassword((v) => !v)}
|
||||
disabled={savingKey !== null || !ePayment.gateways.mellat.enabled}
|
||||
aria-label={
|
||||
showPassword
|
||||
? t('website.ePayment.hidePassword')
|
||||
@@ -391,11 +385,11 @@ export function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<div className={styles.actionsInline}>
|
||||
<button
|
||||
type="submit"
|
||||
className={styles.saveBtn}
|
||||
disabled={savingKey !== null}
|
||||
disabled={savingKey !== null || !ePayment.gateways.mellat.enabled}
|
||||
>
|
||||
{savingKey === 'mellat-credentials'
|
||||
? t('storeSettings.saving')
|
||||
@@ -418,79 +412,53 @@ export function SettingsPage() {
|
||||
<Switch
|
||||
id="epayment-zarinpal"
|
||||
checked={ePayment.gateways.zarinpal.enabled}
|
||||
disabled={savingKey !== null || !ePayment.enabled}
|
||||
disabled={savingKey !== null}
|
||||
onChange={(checked) => void handleZarinpalToggle(checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form className={styles.credentials} onSubmit={handleSaveZarinpalCredentials}>
|
||||
<div className={`${fieldStyles.field} ${styles.field}`}>
|
||||
<label htmlFor="zarinpal-merchant">
|
||||
{t('website.ePayment.merchantId')}
|
||||
</label>
|
||||
<input
|
||||
id="zarinpal-merchant"
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
dir="ltr"
|
||||
spellCheck={false}
|
||||
placeholder={t('website.ePayment.merchantIdPlaceholder')}
|
||||
value={ePayment.gateways.zarinpal.merchantId}
|
||||
disabled={savingKey !== null}
|
||||
onChange={(e) =>
|
||||
setEPayment((current) => ({
|
||||
...current,
|
||||
gateways: {
|
||||
...current.gateways,
|
||||
zarinpal: {
|
||||
...current.gateways.zarinpal,
|
||||
merchantId: e.target.value,
|
||||
},
|
||||
<form
|
||||
className={`${styles.credentialsInline} ${
|
||||
!ePayment.gateways.zarinpal.enabled ? styles.credentialsDisabled : ''
|
||||
}`}
|
||||
onSubmit={handleSaveZarinpalCredentials}
|
||||
aria-disabled={!ePayment.gateways.zarinpal.enabled}
|
||||
>
|
||||
<label className={styles.inlineLabel} htmlFor="zarinpal-merchant">
|
||||
{t('website.ePayment.merchantId')}
|
||||
</label>
|
||||
<input
|
||||
id="zarinpal-merchant"
|
||||
className={styles.inlineInput}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
dir="ltr"
|
||||
spellCheck={false}
|
||||
placeholder={t('website.ePayment.merchantIdPlaceholder')}
|
||||
value={ePayment.gateways.zarinpal.merchantId}
|
||||
disabled={savingKey !== null || !ePayment.gateways.zarinpal.enabled}
|
||||
onChange={(e) =>
|
||||
setEPayment((current) => ({
|
||||
...current,
|
||||
gateways: {
|
||||
...current.gateways,
|
||||
zarinpal: {
|
||||
...current.gateways.zarinpal,
|
||||
merchantId: e.target.value,
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={`${sharedStyles.row} ${styles.fullRow}`}>
|
||||
<div className={sharedStyles.rowText}>
|
||||
<label className={sharedStyles.rowLabel} htmlFor="zarinpal-sandbox">
|
||||
{t('website.ePayment.sandbox')}
|
||||
</label>
|
||||
<p className={sharedStyles.rowDescription}>
|
||||
{t('website.ePayment.sandboxDesc')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="zarinpal-sandbox"
|
||||
checked={ePayment.gateways.zarinpal.sandbox}
|
||||
disabled={savingKey !== null}
|
||||
onChange={(checked) =>
|
||||
setEPayment((current) => ({
|
||||
...current,
|
||||
gateways: {
|
||||
...current.gateways,
|
||||
zarinpal: {
|
||||
...current.gateways.zarinpal,
|
||||
sandbox: checked,
|
||||
},
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<button
|
||||
type="submit"
|
||||
className={styles.saveBtn}
|
||||
disabled={savingKey !== null}
|
||||
>
|
||||
{savingKey === 'zarinpal-credentials'
|
||||
? t('storeSettings.saving')
|
||||
: t('website.ePayment.saveCredentials')}
|
||||
</button>
|
||||
</div>
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className={styles.saveBtn}
|
||||
disabled={savingKey !== null || !ePayment.gateways.zarinpal.enabled}
|
||||
>
|
||||
{savingKey === 'zarinpal-credentials'
|
||||
? t('storeSettings.saving')
|
||||
: t('website.ePayment.saveCredentials')}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ const DEFAULT_STORE_SETTINGS: StoreSettings = {
|
||||
terminalId: '',
|
||||
username: '',
|
||||
password: '',
|
||||
passwordSet: false,
|
||||
},
|
||||
sep: { enabled: false },
|
||||
snappay: { enabled: false },
|
||||
|
||||
@@ -146,6 +146,19 @@ export async function resolveHtmlEmbeddedDataUrls(html: string): Promise<string>
|
||||
return doc.body.innerHTML
|
||||
}
|
||||
|
||||
export function galleryMediaIdsAfterChange(
|
||||
previousImages: string[],
|
||||
previousIds: string[],
|
||||
nextImages: string[],
|
||||
): string[] {
|
||||
const prevUrlToId = new Map(
|
||||
previousImages.map((url, index) => [url, previousIds[index] ?? '']),
|
||||
)
|
||||
return nextImages.map((url) =>
|
||||
isDataUrl(url) ? '' : prevUrlToId.get(url) ?? '',
|
||||
)
|
||||
}
|
||||
|
||||
export async function resolveDataUrlsToMediaIds(
|
||||
values: string[],
|
||||
existingMediaIds: string[],
|
||||
|
||||
@@ -31,9 +31,8 @@ export interface MellatGatewaySettings {
|
||||
enabled: boolean
|
||||
terminalId: string
|
||||
username: string
|
||||
/** Always empty in API responses; use passwordSet. */
|
||||
/** Always empty in API responses; send a new value to replace the stored secret. */
|
||||
password: string
|
||||
passwordSet?: boolean
|
||||
}
|
||||
|
||||
export interface ZarinpalGatewaySettings {
|
||||
@@ -73,7 +72,6 @@ export const DEFAULT_EPAYMENT_SETTINGS: EPaymentSettings = {
|
||||
terminalId: '',
|
||||
username: '',
|
||||
password: '',
|
||||
passwordSet: false,
|
||||
},
|
||||
sep: { enabled: false },
|
||||
snappay: { enabled: false },
|
||||
|
||||
Reference in New Issue
Block a user