forked from Qortal/q-shop
chore(release): v1.1.1\n\n- ProductForm: price fields for all supported coins\n- ProductCard: icon-based prices + QORT equivalent (2 decimals)\n- Exchange rate card: icon display, compact formatting, no background\n- Release notes and announcement for 1.1.1
This commit is contained in:
27
docs/RELEASE_NOTES_v1.1.1.md
Normal file
27
docs/RELEASE_NOTES_v1.1.1.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Q‑Shop v1.1.1 — Release Notes
|
||||
|
||||
Release date: 2025‑08‑23
|
||||
|
||||
## Summary
|
||||
Minor UI/UX improvements and a fix to price entry for all supported coins.
|
||||
|
||||
## Changes
|
||||
- Product pricing form
|
||||
- Shows price fields for every coin your shop supports (hides others).
|
||||
- Pre-fills existing prices when editing a product.
|
||||
- Product card price display
|
||||
- Uses PNG icons for all coins.
|
||||
- For non‑QORT selections, shows: `[COIN icon] amount [QORT icon] qortEquivalent`.
|
||||
- QORT amounts rounded to 2 decimals; no letter tickers.
|
||||
- Exchange rate card (sidebar)
|
||||
- Removed background panel to prevent text overflow.
|
||||
- Icon-based display with both directions:
|
||||
- `[QORT icon] 1 = X [COIN icon]`
|
||||
- `[COIN icon] 1 = Y [QORT icon]`
|
||||
- Compact formatting (>=1 → 4 decimals; <1 → 4 significant digits).
|
||||
- Removed the arrow/icons row below the subtitle.
|
||||
|
||||
## Notes
|
||||
- This is a patch over v1.1.0 (which introduced BTC/LTC/DOGE/DGB/RVN support, Edit Shop improvements, and initial pricing UX updates).
|
||||
- Build: `npm ci && npm run build`. Artifacts in `dist/`.
|
||||
|
||||
@@ -71,6 +71,17 @@ Goal: Ship new coin support and UI fixes with a clean build and verified UX.
|
||||
- Create tag `v1.1.0` and push to trigger workflow.
|
||||
- Draft Gitea release and attach `dist.zip` (then publish to Qortal).
|
||||
|
||||
## v1.1.1 Patch Plan
|
||||
- Show price inputs for all supported coins in ProductForm.
|
||||
- Unify product card pricing with PNG icons + QORT equivalency (2‑decimals).
|
||||
- Simplify Exchange Rate card visuals and format values compactly.
|
||||
- Bump to 1.1.1, add release/announcement docs, tag and release.
|
||||
|
||||
## v1.1.1 Progress
|
||||
- Implemented ProductForm price fields for all supported coins.
|
||||
- Updated product cards and Exchange Rate card per spec.
|
||||
- Added docs/RELEASE_NOTES_v1.1.1.md and docs/USER_ANNOUNCEMENT_v1.1.1.md.
|
||||
|
||||
## Open Questions
|
||||
- Recreate Shop Data: ok to publish immediately from Edit modal, or should we gate behind an extra confirmation modal?
|
||||
- Should `onPublishParamEdit` include `storeIdentifier` for edits, or remain excluded as now?
|
||||
@@ -89,3 +100,6 @@ Goal: Ship new coin support and UI fixes with a clean build and verified UX.
|
||||
- Bumped version to 1.1.0 in `package.json`.
|
||||
- Added release notes and user announcement under `docs/`.
|
||||
- Added Gitea workflow to build and archive `dist.zip` on tag.
|
||||
- Product pricing form now shows price fields for all supported coins in the shop (hides non‑supported coins) and pre-fills values when editing.
|
||||
- Product card pricing now uses PNG icons for all coins; for non‑QORT selections the card shows both the selected coin amount and the QORT equivalent (QORT rounded to 2 decimals) using icons only.
|
||||
- Exchange rate card updated: removed background, switched to icons, shows both directions with compact formatting (>=1 → 4 decimals; <1 → 4 significant digits), and removed the arrows row.
|
||||
|
||||
9
docs/USER_ANNOUNCEMENT_v1.1.1.md
Normal file
9
docs/USER_ANNOUNCEMENT_v1.1.1.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Q‑Shop v1.1.1 — What’s New
|
||||
|
||||
Quick polish update:
|
||||
|
||||
- Enter prices for every coin your shop supports (not just QORT/ARRR).
|
||||
- Product prices now show coin icons everywhere, with a clear QORT equivalent.
|
||||
- Exchange rate box is cleaner, uses icons, and shows both directions.
|
||||
|
||||
That’s it — smoother pricing and clearer info. Update to 1.1.1 and enjoy!
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "q-shop",
|
||||
"private": true,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -84,9 +84,6 @@ export const ProductForm: React.FC<ProductFormProps> = ({
|
||||
const editProductQortPrice =
|
||||
editProduct?.price?.find((item: Price) => item?.currency === "qort")
|
||||
?.value || product.price;
|
||||
const editProductARRRPrice =
|
||||
editProduct?.price?.find((item: Price) => item?.currency === CoinFilter.arrr)
|
||||
?.value || "";
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setProduct({
|
||||
@@ -276,20 +273,26 @@ export const ProductForm: React.FC<ProductFormProps> = ({
|
||||
onChangeFunc={handleProductPriceChange}
|
||||
required={true}
|
||||
/>
|
||||
{currentStore?.supportedCoins?.includes(CoinFilter.arrr) && (
|
||||
<CustomNumberField
|
||||
name="arrr-price"
|
||||
label="Price in ARRR"
|
||||
variant={Variant.filled}
|
||||
initialValue={editProductARRRPrice.toString()}
|
||||
addIconButtons={false}
|
||||
minValue={0}
|
||||
maxValue={Number.MAX_SAFE_INTEGER}
|
||||
allowDecimals={true}
|
||||
onChangeFunc={(val)=>handleProductPriceChangeForeign(val, CoinFilter.arrr)}
|
||||
required={false}
|
||||
/>
|
||||
)}
|
||||
{(currentStore?.supportedCoins || [])
|
||||
.filter((c) => c && c !== CoinFilter.qort)
|
||||
.map((coin) => {
|
||||
const initial = editProduct?.price?.find((p: Price) => p?.currency === coin)?.value;
|
||||
return (
|
||||
<CustomNumberField
|
||||
key={coin}
|
||||
name={`price-${coin}`}
|
||||
label={`Price in ${coin}`}
|
||||
variant={Variant.filled}
|
||||
initialValue={(initial ?? "").toString()}
|
||||
addIconButtons={false}
|
||||
minValue={0}
|
||||
maxValue={Number.MAX_SAFE_INTEGER}
|
||||
allowDecimals={true}
|
||||
onChangeFunc={(val) => handleProductPriceChangeForeign(val, coin)}
|
||||
required={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<Box>
|
||||
<FormControl fullWidth>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { RootState } from "../../../state/store";
|
||||
import { Product } from "../../../state/features/storeSlice";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setProductToCart } from "../../../state/features/cartSlice";
|
||||
import { QortalSVG } from "../../../assets/svgs/QortalSVG";
|
||||
import { coinPng } from "../../../constants/coin-icons";
|
||||
import {
|
||||
AddToCartButton,
|
||||
ProductDescription,
|
||||
@@ -16,7 +16,6 @@ import { CartSVG } from "../../../assets/svgs/CartSVG";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice";
|
||||
import { AcceptedCoinRow } from "../Store/Store-styles";
|
||||
import { ARRRSVG } from "../../../assets/svgs/ARRRSVG";
|
||||
import { CoinFilter } from "../Store/Store";
|
||||
|
||||
function addEllipsis(str: string, limit: number) {
|
||||
@@ -79,6 +78,11 @@ export const ProductCard: React.FC<ProductCardProps> = ({ product, exchangeRate,
|
||||
}
|
||||
}
|
||||
|
||||
const fmtQort = (n?: number) => {
|
||||
if (n === null || n === undefined || isNaN(n as any)) return "";
|
||||
return Number(n).toFixed(2);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledCard>
|
||||
<CardMedia
|
||||
@@ -109,27 +113,18 @@ export const ProductCard: React.FC<ProductCardProps> = ({ product, exchangeRate,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{filterCoin === CoinFilter.qort && (
|
||||
{filterCoin === CoinFilter.qort ? (
|
||||
<AcceptedCoinRow>
|
||||
<QortalSVG
|
||||
color={theme.palette.text.primary}
|
||||
height={"23"}
|
||||
width={"23"}
|
||||
/>{" "}
|
||||
{price}
|
||||
<img src={coinPng('QORT') || ''} alt="QORT" width={23} height={23} />
|
||||
{" "}{fmtQort(price as number)}
|
||||
</AcceptedCoinRow>
|
||||
)}
|
||||
{filterCoin !== CoinFilter.qort && (
|
||||
) : (
|
||||
<AcceptedCoinRow>
|
||||
{filterCoin === CoinFilter.arrr ? (
|
||||
<ARRRSVG color={theme.palette.text.primary} height={"23"} width={"23"} />
|
||||
) : (
|
||||
<span style={{ fontWeight: 600 }}>{filterCoin}</span>
|
||||
)}
|
||||
<img src={coinPng(filterCoin) || ''} alt={filterCoin} width={23} height={23} />
|
||||
{" "}{price}
|
||||
{qortApprox ? (
|
||||
<div style={{ fontSize: 12, opacity: 0.7, marginTop: 2 }}>≈ {qortApprox} QORT</div>
|
||||
) : null}
|
||||
<span style={{ width: 12 }} />
|
||||
<img src={coinPng('QORT') || ''} alt="QORT" width={23} height={23} />
|
||||
{" "}{fmtQort(qortApprox ?? (priceQort as number))}
|
||||
</AcceptedCoinRow>
|
||||
)}
|
||||
</ProductDescription>
|
||||
|
||||
@@ -192,6 +192,13 @@ export const Store = () => {
|
||||
null
|
||||
);
|
||||
|
||||
const formatRate = (n: number): string => {
|
||||
if (!isFinite(n)) return "";
|
||||
const abs = Math.abs(n);
|
||||
if (abs >= 1) return Number(n).toFixed(4);
|
||||
return Number(n).toPrecision(4);
|
||||
};
|
||||
|
||||
const storeToUse = useMemo(()=> {
|
||||
return username === user?.name
|
||||
? currentStore
|
||||
@@ -793,30 +800,31 @@ const switchCoin = async ()=> {
|
||||
</FormControl>
|
||||
|
||||
{coinToUse !== CoinFilter.qort && exchangeRate && (
|
||||
<ExchangeRateCard>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateTitle>1 QORT = {exchangeRate} {coinToUse}</ExchangeRateTitle>
|
||||
</ExchangeRateRow>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateTitle>
|
||||
1 {String(coinToUse)} = {Number((1 / (exchangeRate || 1)).toFixed(8))} QORT
|
||||
</ExchangeRateTitle>
|
||||
</ExchangeRateRow>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateSubTitle>
|
||||
{`Rate calculated by recent trade portal trades`}
|
||||
</ExchangeRateSubTitle>
|
||||
</ExchangeRateRow>
|
||||
<ExchangeRateRow style={{ gap: "10px" }}>
|
||||
<AcceptedCoin src={coinPng('QORT') || ''} alt="QORT-logo" />
|
||||
<CompareArrowsSVG
|
||||
color={theme.palette.text.primary}
|
||||
height={"32"}
|
||||
width={"32"}
|
||||
/>
|
||||
<AcceptedCoin src={coinPng(String(coinToUse)) || ''} alt={`${coinToUse}-logo`} />
|
||||
</ExchangeRateRow>
|
||||
</ExchangeRateCard>
|
||||
<ExchangeRateCard>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateTitle>
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
|
||||
<AcceptedCoin src={coinPng('QORT') || ''} alt="QORT" />
|
||||
1 = {formatRate(exchangeRate)}
|
||||
<AcceptedCoin src={coinPng(String(coinToUse)) || ''} alt={`${coinToUse}`} />
|
||||
</span>
|
||||
</ExchangeRateTitle>
|
||||
</ExchangeRateRow>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateTitle>
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
|
||||
<AcceptedCoin src={coinPng(String(coinToUse)) || ''} alt={`${coinToUse}`} />
|
||||
1 = {formatRate(1 / (exchangeRate || 1))}
|
||||
<AcceptedCoin src={coinPng('QORT') || ''} alt="QORT" />
|
||||
</span>
|
||||
</ExchangeRateTitle>
|
||||
</ExchangeRateRow>
|
||||
<ExchangeRateRow>
|
||||
<ExchangeRateSubTitle>
|
||||
{`Rate calculated by recent trade portal trades`}
|
||||
</ExchangeRateSubTitle>
|
||||
</ExchangeRateRow>
|
||||
</ExchangeRateCard>
|
||||
)}
|
||||
</FiltersSubContainer>
|
||||
<FiltersTitle>
|
||||
|
||||
@@ -306,9 +306,6 @@ export const ExchangeRateCard = styled(Box)(({ theme }) => ({
|
||||
gap: "10px",
|
||||
padding: "10px 15px",
|
||||
width: "100%",
|
||||
height: "200px",
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRadius: "10px",
|
||||
marginTop: "10px",
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user