Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0345fe042e | ||
|
|
a9ffc57608 |
@@ -4,9 +4,13 @@ export const storefrontImgUrlPrefix = '/shop/assets/img';
|
|||||||
|
|
||||||
export const storefrontCssUrlPrefix = '/shop/assets/css';
|
export const storefrontCssUrlPrefix = '/shop/assets/css';
|
||||||
|
|
||||||
|
export const storefrontJsUrlPrefix = '/shop/assets/js';
|
||||||
|
|
||||||
export const getStorefrontPublicDir = (): string =>
|
export const getStorefrontPublicDir = (): string =>
|
||||||
path.join(__dirname, '..', 'modules', 'storefrontCore', 'public');
|
path.join(__dirname, '..', 'modules', 'storefrontCore', 'public');
|
||||||
|
|
||||||
export const getStorefrontImgDir = (): string => path.join(getStorefrontPublicDir(), 'img');
|
export const getStorefrontImgDir = (): string => path.join(getStorefrontPublicDir(), 'img');
|
||||||
|
|
||||||
export const getStorefrontCssDir = (): string => path.join(getStorefrontPublicDir(), 'css');
|
export const getStorefrontCssDir = (): string => path.join(getStorefrontPublicDir(), 'css');
|
||||||
|
|
||||||
|
export const getStorefrontJsDir = (): string => path.join(getStorefrontPublicDir(), 'js');
|
||||||
|
|||||||
+7
-1
@@ -8,8 +8,10 @@ import path from 'node:path';
|
|||||||
import {
|
import {
|
||||||
getStorefrontCssDir,
|
getStorefrontCssDir,
|
||||||
getStorefrontImgDir,
|
getStorefrontImgDir,
|
||||||
|
getStorefrontJsDir,
|
||||||
storefrontCssUrlPrefix,
|
storefrontCssUrlPrefix,
|
||||||
storefrontImgUrlPrefix
|
storefrontImgUrlPrefix,
|
||||||
|
storefrontJsUrlPrefix
|
||||||
} from './config/storefrontAssetPaths';
|
} from './config/storefrontAssetPaths';
|
||||||
import { getPublicUploadsDir, publicUploadsUrlPrefix } from './config/uploadPaths';
|
import { getPublicUploadsDir, publicUploadsUrlPrefix } from './config/uploadPaths';
|
||||||
import { AppModule } from './AppModule';
|
import { AppModule } from './AppModule';
|
||||||
@@ -42,6 +44,10 @@ async function bootstrap() {
|
|||||||
prefix: storefrontCssUrlPrefix
|
prefix: storefrontCssUrlPrefix
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.useStaticAssets(getStorefrontJsDir(), {
|
||||||
|
prefix: storefrontJsUrlPrefix
|
||||||
|
});
|
||||||
|
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
|
||||||
if (nodeEnv === NodeEnv.Production) {
|
if (nodeEnv === NodeEnv.Production) {
|
||||||
|
|||||||
@@ -159,6 +159,26 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-js-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js .sf-js-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js .sf-js-only.sf-copy-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-nojs-only {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.js .sf-nojs-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-padding-top: var(--sf-sticky-header-scroll-padding);
|
scroll-padding-top: var(--sf-sticky-header-scroll-padding);
|
||||||
}
|
}
|
||||||
@@ -629,6 +649,10 @@ a:hover {
|
|||||||
border-color 0.1s;
|
border-color 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-btn.sf-js-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.sf-btn:hover {
|
.sf-btn:hover {
|
||||||
color: var(--sf-accent);
|
color: var(--sf-accent);
|
||||||
background: var(--sf-accent-subtle);
|
background: var(--sf-accent-subtle);
|
||||||
@@ -1263,7 +1287,7 @@ a:hover {
|
|||||||
|
|
||||||
.sf-qr-wrap {
|
.sf-qr-wrap {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: var(--sf-space-4);
|
margin: var(--sf-space-4) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-tx-list {
|
.sf-tx-list {
|
||||||
@@ -1302,6 +1326,64 @@ a:hover {
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-copy-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--sf-space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-row .sf-payment-amount {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-row--inline {
|
||||||
|
display: inline-flex;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-row--address {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: var(--sf-space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-row__value {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 1.375rem;
|
||||||
|
min-height: 1.375rem;
|
||||||
|
padding: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn__icon {
|
||||||
|
display: block;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn__icon svg {
|
||||||
|
width: 0.875rem;
|
||||||
|
height: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn__icon--success {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn--copied .sf-copy-btn__icon--copy {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-copy-btn--copied .sf-copy-btn__icon--success {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.sf-order-status {
|
.sf-order-status {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
(() => {
|
||||||
|
const copiedAriaLabel = 'Copied';
|
||||||
|
const resetMs = 2000;
|
||||||
|
|
||||||
|
const copyText = async text => {
|
||||||
|
if (navigator.clipboard?.writeText) {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
|
||||||
|
textarea.value = text;
|
||||||
|
textarea.setAttribute('readonly', '');
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.left = '-9999px';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopyClick = async event => {
|
||||||
|
const button = event.currentTarget;
|
||||||
|
const text = button.dataset.copyText;
|
||||||
|
|
||||||
|
if (!text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultAriaLabel = button.dataset.copyAriaLabel ?? 'Copy';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await copyText(text);
|
||||||
|
|
||||||
|
button.classList.add('sf-copy-btn--copied');
|
||||||
|
button.setAttribute('aria-label', copiedAriaLabel);
|
||||||
|
|
||||||
|
window.setTimeout(() => {
|
||||||
|
button.classList.remove('sf-copy-btn--copied');
|
||||||
|
button.setAttribute('aria-label', defaultAriaLabel);
|
||||||
|
}, resetMs);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Copy to clipboard failed', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const button of document.querySelectorAll('[data-action="copy"]')) {
|
||||||
|
button.addEventListener('click', handleCopyClick);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
document.documentElement.classList.replace('no-js', 'js');
|
||||||
@@ -12,6 +12,7 @@ const STOREFRONT_PARTIALS = [
|
|||||||
{ name: 'cart-line-item', file: 'cart-line-item.hbs' },
|
{ name: 'cart-line-item', file: 'cart-line-item.hbs' },
|
||||||
{ name: 'checkout-line-item', file: 'checkout-line-item.hbs' },
|
{ name: 'checkout-line-item', file: 'checkout-line-item.hbs' },
|
||||||
{ name: 'confirm-action-details', file: 'confirm-action-details.hbs' },
|
{ name: 'confirm-action-details', file: 'confirm-action-details.hbs' },
|
||||||
|
{ name: 'copy-button', file: 'copy-button.hbs' },
|
||||||
{ name: 'dismiss-button', file: 'dismiss-button.hbs' },
|
{ name: 'dismiss-button', file: 'dismiss-button.hbs' },
|
||||||
{ name: 'order-line-item', file: 'order-line-item.hbs' },
|
{ name: 'order-line-item', file: 'order-line-item.hbs' },
|
||||||
{ name: 'order-digital-delivery', file: 'order-digital-delivery.hbs' },
|
{ name: 'order-digital-delivery', file: 'order-digital-delivery.hbs' },
|
||||||
|
|||||||
@@ -45,3 +45,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src='/shop/assets/js/copy.js' defer></script>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html
|
<html
|
||||||
lang='en'
|
lang='en'
|
||||||
|
class='no-js'
|
||||||
{{> html-theme-attributes}}
|
{{> html-theme-attributes}}
|
||||||
>
|
>
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html
|
<html
|
||||||
lang='en'
|
lang='en'
|
||||||
|
class='no-js'
|
||||||
{{> html-theme-attributes}}
|
{{> html-theme-attributes}}
|
||||||
>
|
>
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
@@ -69,3 +69,5 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src='/shop/assets/js/copy.js' defer></script>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
class='sf-btn sf-copy-btn sf-js-only'
|
||||||
|
data-action='copy'
|
||||||
|
data-copy-text='{{copyText}}'
|
||||||
|
data-copy-aria-label='{{ariaLabel}}'
|
||||||
|
aria-label='{{ariaLabel}}'
|
||||||
|
>
|
||||||
|
<span class='sf-copy-btn__icon sf-copy-btn__icon--copy' aria-hidden='true'>
|
||||||
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>
|
||||||
|
<rect width='14' height='14' x='8' y='8' rx='2' ry='2' />
|
||||||
|
<path d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class='sf-copy-btn__icon sf-copy-btn__icon--success' aria-hidden='true'>
|
||||||
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'>
|
||||||
|
<path d='M20 6 9 17l-5-5' />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showProminentAmount}}
|
{{#if showProminentAmount}}
|
||||||
<p class='sf-payment-amount'>{{expectedTotalCrypto}} {{paymentLabel}}</p>
|
<p class='sf-payment-amount'>{{instructionAmountCrypto}} {{paymentLabel}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showReceivedTotal}}
|
{{#if showReceivedTotal}}
|
||||||
@@ -23,7 +23,13 @@
|
|||||||
{{#if showInstruction}}
|
{{#if showInstruction}}
|
||||||
<p class='sf-payment-detail'>
|
<p class='sf-payment-detail'>
|
||||||
{{instructionPrefix}}
|
{{instructionPrefix}}
|
||||||
<strong>{{instructionAmountCrypto}} {{paymentLabel}}</strong>
|
<span class='sf-copy-row sf-copy-row--inline'>
|
||||||
|
<strong>{{instructionAmountCrypto}} {{paymentLabel}}</strong>
|
||||||
|
{{> copy-button
|
||||||
|
copyText=instructionAmountCrypto
|
||||||
|
ariaLabel='Copy payment amount'
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
{{instructionSuffix}}
|
{{instructionSuffix}}
|
||||||
{{#if showExpiry}}
|
{{#if showExpiry}}
|
||||||
Payment expires in
|
Payment expires in
|
||||||
@@ -40,7 +46,13 @@
|
|||||||
|
|
||||||
{{#if showAddress}}
|
{{#if showAddress}}
|
||||||
<p class='sf-field__label'>{{#if showQr}}Address{{else}}Payment address{{/if}}</p>
|
<p class='sf-field__label'>{{#if showQr}}Address{{else}}Payment address{{/if}}</p>
|
||||||
<p class='sf-mono sf-payment-detail'>{{paymentAddress}}</p>
|
<div class='sf-copy-row sf-copy-row--address'>
|
||||||
|
<p class='sf-mono sf-payment-detail sf-copy-row__value'>{{paymentAddress}}</p>
|
||||||
|
{{> copy-button
|
||||||
|
copyText=paymentAddress
|
||||||
|
ariaLabel='Copy payment address'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showPayments}}
|
{{#if showPayments}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<section id='order-chat' class='sf-chat-panel'>
|
<section id='order-chat' class='sf-chat-panel'>
|
||||||
<div class='sf-chat-header'>
|
<div class='sf-chat-header'>
|
||||||
<h2 class='sf-section-title'>Order chat (encrypted)</h2>
|
<h2 class='sf-section-title'>Order chat</h2>
|
||||||
{{> refresh-link href=order.chatRefreshHref}}
|
{{> refresh-link href=order.chatRefreshHref}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<meta http-equiv='refresh' content='{{refreshSec}}' />
|
<meta http-equiv='refresh' content='{{refreshSec}}' />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<link rel='stylesheet' href='/shop/assets/css/storefront.css' />
|
<link rel='stylesheet' href='/shop/assets/css/storefront.css' />
|
||||||
|
<script src='/shop/assets/js/js-enabled.js'></script>
|
||||||
{{> shop-favicon}}
|
{{> shop-favicon}}
|
||||||
{{#if pageMeta.productJsonLd}}
|
{{#if pageMeta.productJsonLd}}
|
||||||
<script type='application/ld+json'>{{{pageMeta.productJsonLd}}}</script>
|
<script type='application/ld+json'>{{{pageMeta.productJsonLd}}}</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user