Add legend to help users discover options
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
a03a151c42
commit
14c365b20f
@ -212,10 +212,22 @@ button {
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
|
|
||||||
> p {
|
> .description {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
padding-right: 30px;
|
padding-right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .legend {
|
||||||
|
//d-flex justify-content-end align-self-start
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
align-self: start;
|
||||||
|
width: 50%;
|
||||||
|
table {
|
||||||
|
width: 75%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
@ -560,6 +572,7 @@ button {
|
|||||||
|
|
||||||
.crate-bar {
|
.crate-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
.crate-mode {
|
.crate-mode {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -572,6 +585,7 @@ button {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
a.active {
|
a.active {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@ -663,17 +677,13 @@ button {
|
|||||||
color: white;
|
color: white;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: .6rem;
|
font-size: .6rem;
|
||||||
padding: .8rem 1rem;
|
padding: .5rem .8rem;
|
||||||
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.15);
|
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.15);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p + p {
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.k-popup-connectors {
|
.k-popup-connectors {
|
||||||
|
@ -108,9 +108,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#root-shop .panel .control > .description,
|
#root-shop .panel .control > .description,
|
||||||
|
#root-shop .panel .control > .legend,
|
||||||
#root-shop .crate-mode {
|
#root-shop .crate-mode {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
#root-shop .panel .control > .legend {
|
||||||
|
justify-content: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
#root-shop .panel .control > .legend tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#root-shop .crate-mode {
|
#root-shop .crate-mode {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -308,12 +316,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#root-shop .panel .control > .description,
|
#root-shop .panel .control > .description,
|
||||||
#root-shop .crate-mode {
|
#root-shop .panel .control > .legend {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
#root-shop .panel .control > .legend {
|
||||||
|
justify-content: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
#root-shop .panel .control > .legend tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#root-shop .crate-mode {
|
#root-shop .panel .crate .crate-bar .crate-mode {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
#root-shop .panel .crate .crate-bar .crate-mode a {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root-shop .panel .summary {
|
#root-shop .panel .summary {
|
||||||
@ -571,12 +590,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#root-shop .panel .control > .description,
|
#root-shop .panel .control > .description,
|
||||||
#root-shop .panel .control > .crate-mode {
|
#root-shop .panel .control > .legend {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
#root-shop .panel .control > .legend {
|
||||||
|
justify-content: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
#root-shop .panel .control > .legend tr {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#root-shop .panel .control > .crate-mode {
|
#root-shop .panel .crate .crate-bar .crate-mode {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
#root-shop .panel .crate .crate-bar .crate-mode a {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root-shop .panel .summary {
|
#root-shop .panel .summary {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -26,10 +26,10 @@ export function Crate({crate_index}) {
|
|||||||
return (
|
return (
|
||||||
<div className="crate">
|
<div className="crate">
|
||||||
|
|
||||||
<div className="crate-bar d-inline-flex">
|
<div className="crate-bar d-inline-flex justify-content-between">
|
||||||
<CrateMode crate_index={crate_index}/>
|
<CrateMode crate_index={crate_index}/>
|
||||||
|
|
||||||
<div className="delete-crate align-self-end align-content-end justify-content-end" onClick={() => onDeleteCrate(crate.id)}>
|
<div className="delete-crate align-self-start align-content-start justify-content-end" onClick={() => onDeleteCrate(crate.id)}>
|
||||||
Delete crate <img src="/images/shop/icon-remove.svg" alt="remove"/>
|
Delete crate <img src="/images/shop/icon-remove.svg" alt="remove"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@ export function CrateMode({crate_index}) {
|
|||||||
{modes_order.map((mode_name, _) => (
|
{modes_order.map((mode_name, _) => (
|
||||||
<a
|
<a
|
||||||
key={mode_name}
|
key={mode_name}
|
||||||
className={crate.crate_mode === mode_name ? 'active' : ''}
|
className={(crate.crate_mode === mode_name ? 'active' : '') }
|
||||||
onClick={() => setMode(crate.id, mode_name)}
|
onClick={() => setMode(crate.id, mode_name)}
|
||||||
href="#"
|
href="#"
|
||||||
role="button">{crate_modes[mode_name].name}</a>
|
role="button">{crate_modes[mode_name].name}</a>
|
||||||
|
21
static/js/shop/Legend.jsx
Normal file
21
static/js/shop/Legend.jsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {useShopStore} from "./shop_store";
|
||||||
|
|
||||||
|
export function LegendItem({icon, description}) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td className="p-1"><img className="" width="20px" src={icon} alt={description}/></td>
|
||||||
|
<td className="p-1"><span> {description} </span></td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Legend() {
|
||||||
|
const legend = useShopStore(state => state.legend);
|
||||||
|
|
||||||
|
return <table>
|
||||||
|
<tbody>
|
||||||
|
{legend.map((item, i) => <LegendItem key={"legend_item"+i} icon={item.icon} description={item.description}/>)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
@ -13,7 +13,7 @@ import {useRenderCount} from "@uidotdev/usehooks";
|
|||||||
* Component that renders all things for order.
|
* Component that renders all things for order.
|
||||||
* It acts like-a layout, this component do nothing more.
|
* It acts like-a layout, this component do nothing more.
|
||||||
*/
|
*/
|
||||||
export function OrderPanel({title, description}) {
|
export function OrderPanel({title, description, legend}) {
|
||||||
// #!render_count
|
// #!render_count
|
||||||
const renderCount = useRenderCount();
|
const renderCount = useRenderCount();
|
||||||
const isMobile = useShopStore((state) => state.isMobile);
|
const isMobile = useShopStore((state) => state.isMobile);
|
||||||
@ -26,8 +26,13 @@ export function OrderPanel({title, description}) {
|
|||||||
|
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
|
|
||||||
<div className="control">
|
<div className="control justify-content-between">
|
||||||
{description}
|
{description}
|
||||||
|
|
||||||
|
<div className="legend">
|
||||||
|
{legend}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -9,6 +9,7 @@ import {Layout} from "./Layout";
|
|||||||
import {Backlog} from "./Backlog";
|
import {Backlog} from "./Backlog";
|
||||||
import {OrderPanel} from "./OrderPanel";
|
import {OrderPanel} from "./OrderPanel";
|
||||||
import {useShopStore} from "./shop_store";
|
import {useShopStore} from "./shop_store";
|
||||||
|
import {Legend} from "./Legend";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders the entire shop
|
* Component that renders the entire shop
|
||||||
@ -59,6 +60,7 @@ export function Shop() {
|
|||||||
this ordering system, or if you need other configurations, email us directly anytime
|
this ordering system, or if you need other configurations, email us directly anytime
|
||||||
at <a href="mailto:sales@m-labs.hk">sales@m-labs.hk</a>. The price is estimated and must
|
at <a href="mailto:sales@m-labs.hk">sales@m-labs.hk</a>. The price is estimated and must
|
||||||
be confirmed by a quote.</p>)}
|
be confirmed by a quote.</p>)}
|
||||||
|
legend={(<Legend/>)}
|
||||||
/>
|
/>
|
||||||
)}>
|
)}>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import {create} from "zustand";
|
import {createWithEqualityFn} from "zustand/traditional";
|
||||||
import {data as shared_data, itemsUnfoldedList} from "./utils";
|
import {data as shared_data, itemsUnfoldedList} from "./utils";
|
||||||
import {true_type_of} from "./options/utils";
|
import {true_type_of} from "./options/utils";
|
||||||
import {v4 as uuidv4} from "uuid";
|
import {v4 as uuidv4} from "uuid";
|
||||||
@ -28,6 +28,10 @@ const useBacklog = ((set, get) => ({
|
|||||||
cardIndexById: card_id => get().cards_list.findIndex((element) => (card_id === element))
|
cardIndexById: card_id => get().cards_list.findIndex((element) => (card_id === element))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const useLegend = ((set, get) => ({
|
||||||
|
legend: shared_data.legend
|
||||||
|
}))
|
||||||
|
|
||||||
const useCrateModes = ((set, get) => ({
|
const useCrateModes = ((set, get) => ({
|
||||||
crate_modes: shared_data.crateModes,
|
crate_modes: shared_data.crateModes,
|
||||||
modes_order: shared_data.crateModeOrder,
|
modes_order: shared_data.crateModeOrder,
|
||||||
@ -389,7 +393,7 @@ const useCart = ((set, get) => ({
|
|||||||
|
|
||||||
setCrateMode: (id, mode) => {
|
setCrateMode: (id, mode) => {
|
||||||
get()._setCrateMode(id, mode)
|
get()._setCrateMode(id, mode)
|
||||||
get().fillExtData(crate_id);
|
get().fillExtData(id);
|
||||||
get().fillWarnings(id);
|
get().fillWarnings(id);
|
||||||
get().setActiveCrate(id);
|
get().setActiveCrate(id);
|
||||||
},
|
},
|
||||||
@ -435,7 +439,7 @@ const useCart = ((set, get) => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
export const useShopStore = create((...params) => ({
|
export const useShopStore = createWithEqualityFn((...params) => ({
|
||||||
...useBacklog(...params),
|
...useBacklog(...params),
|
||||||
...useCrateModes(...params),
|
...useCrateModes(...params),
|
||||||
...useCart(...params),
|
...useCart(...params),
|
||||||
@ -443,4 +447,5 @@ export const useShopStore = create((...params) => ({
|
|||||||
...useLayout(...params),
|
...useLayout(...params),
|
||||||
...useHighlighted(...params),
|
...useHighlighted(...params),
|
||||||
...useImportJSON(...params),
|
...useImportJSON(...params),
|
||||||
|
...useLegend(...params),
|
||||||
}))
|
}))
|
@ -3,6 +3,16 @@ const shop_data = {
|
|||||||
API_RFQ: 'https://hooks.m-labs.hk/rfq',
|
API_RFQ: 'https://hooks.m-labs.hk/rfq',
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
|
|
||||||
|
legend: [
|
||||||
|
{icon: "/images/shop/icon-customize.svg", description: "cards configuration available"},
|
||||||
|
{icon: "/images/shop/icon-add.svg", description: "add a card or crate to the order"},
|
||||||
|
{icon: "/images/shop/icon-remove.svg", description: "remove a card or crate from the order"},
|
||||||
|
{icon: "/images/shop/icon-clear.svg", description: "remove all the cards from the crate"},
|
||||||
|
{icon: "/images/shop/icon-warning.svg", description: "the card or crate contains errors"},
|
||||||
|
{icon: "/images/shop/icon-reminder.svg", description: "suggestions or hints available"},
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
crateModes: {
|
crateModes: {
|
||||||
rack: {
|
rack: {
|
||||||
id: 'rack',
|
id: 'rack',
|
||||||
|
Loading…
Reference in New Issue
Block a user