shop: group cards

Closes #30
Co-authored-by: Egor Savkin <es@m-labs.hk>
Co-committed-by: Egor Savkin <es@m-labs.hk>
pull/66/head
Egor Savkin 2023-06-30 10:10:15 +08:00 committed by sb10q
parent d9ba17ad3a
commit 53a6d3f378
4 changed files with 337 additions and 476 deletions

View File

@ -12,6 +12,42 @@
display: none; display: none;
} }
#accordion_categories,
#accordion_categories .card {
background-color: inherit;
}
#accordion_categories .card:not(:first-child) {
border-top: thin rgba(255,255, 255, 20%) solid ;
border-radius: 0;
}
#accordion_categories .card-header {
padding: 0;
}
#accordion_categories button {
color: white;
font-weight: bold;
font-size: 1.75rem;
padding: .75rem 2rem;
}
#accordion_categories .card .card-body {
padding: 0;
}
#accordion_categories .card-header:hover {
background-color: #1f4f68;
transition: 0.3s;
text-decoration: none;
}
#accordion_categories button:focus,
#accordion_categories button:hover {
text-decoration: none;
}
/* /*
##Device = Tablets, Ipads (portrait) ##Device = Tablets, Ipads (portrait)
##Screen = B/w 768px to 1024px ##Screen = B/w 768px to 1024px
@ -110,6 +146,11 @@
#root-shop .panel .summary>.summary-form { #root-shop .panel .summary>.summary-form {
width: 100%; width: 100%;
} }
#accordion_categories button {
font-size: 1.5rem;
padding: .75rem 1rem;
}
} }
/* /*
##Device = Tablets, Ipads (landscape) ##Device = Tablets, Ipads (landscape)
@ -178,6 +219,11 @@
#root-shop .panel .summary>.summary-form { #root-shop .panel .summary>.summary-form {
width: 100%; width: 100%;
} }
#accordion_categories button {
font-size: 1.5rem;
padding: .75rem 1rem;
}
} }
/* /*
##Device = Low Resolution Tablets, Mobiles (Landscape) ##Device = Low Resolution Tablets, Mobiles (Landscape)
@ -379,6 +425,11 @@
#root-shop .panel .summary>.summary-form { #root-shop .panel .summary>.summary-form {
width: 100%; width: 100%;
} }
#accordion_categories button {
font-size: 1rem;
padding: .5rem 0.5rem;
}
} }
/* /*
##Device = Most of the Smartphones Mobiles (Portrait) ##Device = Most of the Smartphones Mobiles (Portrait)
@ -576,4 +627,9 @@
#root-shop .panel .summary>.summary-form { #root-shop .panel .summary>.summary-form {
width: 100%; width: 100%;
} }
#accordion_categories button {
font-size: 1rem;
padding: .5rem 0.5rem;
}
} }

View File

@ -74,15 +74,13 @@ const copy = (
model, model,
source, source,
destination, destination,
droppableSource, draggableSource,
droppableDestination droppableDestination
) => { ) => {
const sourceClone = Array.from(source.itemIds);
const destClone = Array.from(destination.items); const destClone = Array.from(destination.items);
const item = sourceClone[droppableSource.index];
destClone.splice(droppableDestination.index, 0, { destClone.splice(droppableDestination.index, 0, {
...model[item], ...model[draggableSource],
id: uuidv4(), id: uuidv4(),
}); });
@ -408,7 +406,6 @@ class ProductItem extends React.PureComponent {
static get propTypes() { static get propTypes() {
return { return {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
name_codename: PropTypes.string, name_codename: PropTypes.string,
price: PropTypes.number.isRequired, price: PropTypes.number.isRequired,
@ -426,9 +423,9 @@ class ProductItem extends React.PureComponent {
this.handleOnClickAddItem = this.handleOnClickAddItem.bind(this); this.handleOnClickAddItem = this.handleOnClickAddItem.bind(this);
} }
handleOnClickAddItem(index, tap, e) { handleOnClickAddItem(id, tap, e) {
if (this.props.onClickAddItem) { if (this.props.onClickAddItem) {
this.props.onClickAddItem(index, tap); this.props.onClickAddItem(id, tap);
} }
e.preventDefault(); e.preventDefault();
} }
@ -436,7 +433,6 @@ class ProductItem extends React.PureComponent {
render() { render() {
const { const {
id, id,
index,
name, name,
name_codename, name_codename,
price, price,
@ -482,11 +478,11 @@ class ProductItem extends React.PureComponent {
<div className="content"> <div className="content">
<button onClick={this.handleOnClickAddItem.bind(this, index, true)}> <button onClick={this.handleOnClickAddItem.bind(this, id, true)}>
<img src="/images/shop/icon-add.svg" alt="add" /> <img src="/images/shop/icon-add.svg" alt="add" />
</button> </button>
<Draggable draggableId={id} index={index}> <Draggable draggableId={id}>
{(provided, snapshot) => ( {(provided, snapshot) => (
<React.Fragment> <React.Fragment>
<img <img
@ -1515,25 +1511,43 @@ class Backlog extends React.PureComponent {
isMobile, isMobile,
} = this.props; } = this.props;
const ordered_items = data.itemIds.map(itemId => items[itemId]);
const products = ordered_items.map((item, index) => { const ordered_groups = data.categories.map(groupItem => ({ name: groupItem.name,
return ( items: groupItem.itemIds.map(itemId => items[itemId])
<ProductItem }));
key={item.id} const groups = ordered_groups.map((group, g_index) => {
id={item.id} return (
index={index} <div className="card">
name={`${item.name_number} ${item.name}`} <h2 className="card-header">
name_codename={item.name_codename} <button className="btn btn-link btn-block text-left " type="button" data-toggle="collapse"
price={item.price} data-target={`#collapse${g_index}`} aria-expanded="true" aria-controls={`collapse${g_index}`}>
currency={currency} {group.name}
image={`/images/${item.image}`} </button>
specs={item.specs} </h2>
datasheet_file={item.datasheet_file} <div id={`collapse${g_index}`} className="collapse" aria-labelledby="headingOne"
datasheet_name={item.datasheet_name} data-parent="#accordion_categories">
onClickAddItem={onClickAddItem} <div className="card-body">
></ProductItem> {group.items.map(item => (
); <ProductItem
}); key={item.id}
id={item.id}
name={`${item.name_number} ${item.name}`}
name_codename={item.name_codename}
price={item.price}
currency={currency}
image={`/images/${item.image}`}
specs={item.specs}
datasheet_file={item.datasheet_file}
datasheet_name={item.datasheet_name}
onClickAddItem={onClickAddItem}
></ProductItem>
))}
</div>
</div>
</div>
);
}
);
return ( return (
<Droppable <Droppable
@ -1554,7 +1568,9 @@ class Backlog extends React.PureComponent {
</div> </div>
) : null} ) : null}
{products} <div className="accordion" id="accordion_categories">
{groups}
</div>
{provided.placeholder && ( {provided.placeholder && (
<div style={{ display: 'none' }}> <div style={{ display: 'none' }}>
@ -1608,9 +1624,10 @@ class Shop extends React.PureComponent {
componentDidMount() { componentDidMount() {
// index 0 is a Kasli, we place it as a default conf on the crate. // index 0 is a Kasli, we place it as a default conf on the crate.
const sourceIds = Array.from(this.state.columns.backlog.categories.map(groupId => groupId.itemIds).flat())
const source = { const source = {
droppableId: 'backlog', droppableId: 'backlog',
index: 0, index: null,
}; };
const destination = { const destination = {
droppableId: 'cart', droppableId: 'cart',
@ -1620,7 +1637,7 @@ class Shop extends React.PureComponent {
this.handleOnDragEnd({ this.handleOnDragEnd({
source, source,
destination, destination,
draggableId: null, draggableId: sourceIds[0],
}); });
} }
@ -1703,10 +1720,10 @@ class Shop extends React.PureComponent {
}); });
} }
handleClickAddItem(index, tap) { handleClickAddItem(id, tap) {
const source = { const source = {
droppableId: 'backlog', droppableId: 'backlog',
index: index, index: null,
}; };
const destination = { const destination = {
droppableId: 'cart', droppableId: 'cart',
@ -1716,7 +1733,7 @@ class Shop extends React.PureComponent {
this.handleOnDragEnd({ this.handleOnDragEnd({
source, source,
destination, destination,
draggableId: null draggableId: id
}, tap); }, tap);
} }
@ -1958,7 +1975,7 @@ class Shop extends React.PureComponent {
this.state.items, this.state.items,
this.state.columns[source.droppableId], this.state.columns[source.droppableId],
this.state.columns[destination.droppableId], this.state.columns[destination.droppableId],
source, draggableId,
destination, destination,
), ),
}, },

612
static/js/shop.min.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -855,28 +855,40 @@ const shop_data = {
id: 'backlog', id: 'backlog',
title: 'Backlog', title: 'Backlog',
/* itemIds define items order - change order to suit your need */ /* itemIds define items order - change order to suit your need */
itemIds: [ categories: [
'kasli', { name: 'Core',
'kaslisoc', itemIds: [
'bnc-dio', 'kasli',
'sma-dio', 'kaslisoc']},
'mcx-dio', { name: 'TTL',
'rj45-dio', itemIds: [
'urukul', 'bnc-dio',
'phaser', 'sma-dio',
'mirny', 'mcx-dio',
'almazny', 'rj45-dio']},
'zotino', { name: 'RF',
'fastino', itemIds: [
'idc-bnc-adapter', 'clocker',
'idc-sma-adapter', 'urukul',
'idc-mcx-adapter', 'phaser',
'hd68-idc-adapter', 'mirny',
'novo', 'almazny']},
'koster', { name: 'DAC/ADC',
'clocker', itemIds: [
'stabilizer', 'zotino',
'vhdcicarrier', 'fastino',
'novo']},
{ name: 'Adapters',
itemIds: [
'idc-bnc-adapter',
'idc-sma-adapter',
'idc-mcx-adapter',
'hd68-idc-adapter',
'vhdcicarrier']},
{ name: 'Misc',
itemIds: [
'koster',
'stabilizer']}
], ],
}, },