store things

This commit is contained in:
Martti Malmi 2020-11-23 16:36:48 +02:00
parent c9c5ce1dbf
commit 363a8246b9
6 changed files with 217 additions and 147 deletions

View File

@ -1253,12 +1253,19 @@ form.public {
.store-item {
display: inline-flex;
flex-direction: column;
width: calc(33.33% - 5px);
height: calc(33.33% - 5px);
background-color: rgba(255,255,255,0.1);
width: calc(33.33% - 8px);
height: calc(33.33% - 8px);
background-color: var(--chat-hover);
padding: 15px;
margin-right: 5px;
margin-bottom: 5px
margin-right: 8px;
margin-bottom: 8px;
cursor: pointer;
transition: all 125ms;
border-radius: 8px;
}
.store-item:hover {
background-color: var(--chat-active);
}
.price-cell {
@ -1290,6 +1297,22 @@ form.public {
font-weight: bold;
}
#store-steps {
display: flex;
flex-direction: row;
}
#store-steps div {
flex-basis: 25%;
padding: 30px 5px;
text-align: center;
cursor: pointer;
}
#store-steps div.active {
background-color: var(--chat-hover);
}
/* Tooltip container */
.tooltip {
position: relative;
@ -1406,6 +1429,9 @@ form.public {
padding: 5px 10px;
}
.profile-photo-container .identicon-container * { max-width: 80px; max-height: 80px; text-align: center;}
.side-padding-xs {
padding: 5px 0 0 5px;
}
}
@media (min-width: 576px) {

View File

@ -14,7 +14,6 @@ import Settings from './components/Settings.js';
import LogoutConfirmation from './components/LogoutConfirmation.js';
import ChatView from './components/ChatView.js';
import StoreView from './components/StoreView.js';
import CartView from './components/CartView.js';
import CheckoutView from './components/CheckoutView.js';
import ProductView from './components/ProductView.js';
import Login from './components/Login.js';
@ -80,7 +79,6 @@ class Main extends Component {
<${LogoutConfirmation} path="/logout"/>
<${Profile.Profile} path="/profile/:id"/>
<${StoreView} path="/store/:id"/>
<${CartView} path="/cart/:id"/>
<${CheckoutView} path="/checkout/:id"/>
<${ProductView} path="/product/:id/:store"/>
<${FollowsView} path="/follows/:id"/>

View File

@ -1,105 +0,0 @@
import { Component } from '../lib/preact.js';
import { html } from '../Helpers.js';
import {translate as t} from '../Translation.js';
import {localState, publicState} from '../Main.js';
import {chats, deleteChat} from '../Chat.js';
import Session from '../Session.js';
import Helpers from '../Helpers.js';
import MessageForm from './MessageForm.js';
import ProfilePhotoPicker from './ProfilePhotoPicker.js';
import { route } from '../lib/preact-router.es.js';
import SafeImg from './SafeImg.js';
import CopyButton from './CopyButton.js';
import FollowButton from './FollowButton.js';
import MessageFeed from './MessageFeed.js';
import Identicon from './Identicon.js';
import Name from './Name.js';
import SearchBox from './SearchBox.js';
class CartView extends Component {
constructor() {
super();
this.eventListeners = [];
this.followedUsers = new Set();
this.followers = new Set();
this.cart = {};
this.state = {
items: {
aa: {name: 'Doge T-shirt', description: 'Amazing t shirt with doge', price: '100€'},
bb: {name: 'Doge Mug', description: 'Wonderful mug with doge', price: '200€'},
cc: {name: 'Iris Sticker', description: 'Very sticky stickers', price: '10€'},
dd: {name: 'Iris virtual badge', description: 'Incredible profile badge', price: '5€'},
ee: {name: 'Gun hosting', description: 'Top gun hosting', price: '10€ / month'}
}
};
}
changeItemCount(k, v, e) {
this.cart[k] = Math.max(this.cart[k] + v, 0);
localState.get('cart').get(this.props.id).get(k).put(this.cart[k]);
}
render() {
const total = Object.keys(this.cart).reduce((sum, currentKey) => {
return sum + parseInt(this.state.items[currentKey].price) * this.cart[currentKey];
}, 0);
return html`
<div class="main-view" id="profile">
<div class="content">
<a href="/store/${this.props.id}"><iris-profile-attribute pub=${this.props.id}/></a>
<h2>Shopping cart</h2>
<div class="flex-table">
${Object.keys(this.cart).filter(k => !!this.cart[k]).map(k => {
const i = this.state.items[k];
return html`
<div class="flex-row">
<div class="flex-cell">
<${SafeImg} src=${i.thumbnail}/>
${i.name}
</div>
<div class="flex-cell no-flex price-cell">
<p>
<span class="unit-price">${parseInt(i.price)} </span>
<button onClick=${() => this.changeItemCount(k, -1)}>-</button>
<input type="text" value=${this.cart[k]} onInput=${e => this.changeItemCount(k, null, e)}/>
<button onClick=${() => this.changeItemCount(k, 1)}>+</button>
</p>
<span class="price">${parseInt(i.price) * this.cart[k]} </span>
</div>
</div>
`;
})}
<div class="flex-row">
<div class="flex-cell"><b>Total</b></div>
<div class="flex-cell no-flex"><b>${total} </b></div>
</div>
</div>
<p>
<button onClick=${() => route('/checkout/' + this.props.id)}>Checkout</button>
</p>
</div>
</div>`;
}
componentWillUnmount() {
this.eventListeners.forEach(e => e.off());
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.componentDidMount();
}
}
componentDidMount() {
const pub = this.props.id;
this.eventListeners.forEach(e => e.off());
this.cart = {};
localState.get('cart').get(pub).map().on((v, k) => {
this.cart[k] = v;
this.setState({cart: this.cart})
});
}
}
export default CartView;

View File

@ -2,7 +2,7 @@ import { Component } from '../lib/preact.js';
import { html } from '../Helpers.js';
import {translate as t} from '../Translation.js';
import {localState, publicState} from '../Main.js';
import {chats, deleteChat} from '../Chat.js';
import {chats, deleteChat, newChat} from '../Chat.js';
import Session from '../Session.js';
import Helpers from '../Helpers.js';
import MessageForm from './MessageForm.js';
@ -24,6 +24,7 @@ class CheckoutView extends Component {
this.followers = new Set();
this.cart = {};
this.state = {
paymentMethod: 'bitcoin',
items: {
aa: {name: 'Doge T-shirt', description: 'Amazing t shirt with doge', price: '100€'},
bb: {name: 'Doge Mug', description: 'Wonderful mug with doge', price: '200€'},
@ -39,41 +40,163 @@ class CheckoutView extends Component {
localState.get('cart').get(this.props.id).get(k).put(this.cart[k]);
}
render() {
confirm() {
const pub = this.props.id;
localState.get('cart').get(pub).map().once((v, k) => {
!!v && localState.get('cart').get(pub).get(k).put(null);
});
newChat(pub);
chats[pub].send('New order: ' + JSON.stringify(this.cart) + ', delivery: ' + JSON.stringify(this.state.delivery) + ', payment: ' + this.state.paymentMethod);
route('/chat/' + pub);
}
renderCart() {
const total = Object.keys(this.cart).reduce((sum, currentKey) => {
return sum + parseInt(this.state.items[currentKey].price) * this.cart[currentKey];
}, 0);
return html`
<h3 class="side-padding-xs">Shopping cart</h3>
<div class="flex-table">
${Object.keys(this.cart).filter(k => !!this.cart[k]).map(k => {
const i = this.state.items[k];
return html`
<div class="flex-row">
<div class="flex-cell">
<${SafeImg} src=${i.thumbnail}/>
${i.name}
</div>
<div class="flex-cell no-flex price-cell">
<p>
<span class="unit-price">${parseInt(i.price)} </span>
<button onClick=${() => this.changeItemCount(k, -1)}>-</button>
<input type="text" value=${this.cart[k]} onInput=${e => this.changeItemCount(k, null, e)}/>
<button onClick=${() => this.changeItemCount(k, 1)}>+</button>
</p>
<span class="price">${parseInt(i.price) * this.cart[k]} </span>
</div>
</div>
`;
})}
<div class="flex-row">
<div class="flex-cell"></div>
<div class="flex-cell no-flex"><b>Total ${total} </b></div>
</div>
</div>
<p class="side-padding-xs">
<button onClick=${() => this.setState({page:'delivery'})}>Next</button>
</p>
`;
}
renderDelivery() {
return html`
<div class="side-padding-xs">
<h3>Delivery</h3>
<p>
<input type="text" placeholder="Name" value=${this.state.delivery.name} onInput=${e => localState.get('delivery').get('name').put(e.target.value)}/>
</p>
<p>
<input type="text" placeholder="Address" value=${this.state.delivery.address} onInput=${e => localState.get('delivery').get('address').put(e.target.value)}/>
</p>
<p>
<input type="text" placeholder="Email (optional)" value=${this.state.delivery.email} onInput=${e => localState.get('delivery').get('email').put(e.target.value)}/>
</p>
<button onClick=${() => this.setState({page:'payment'})}>Next</button>
</div>
`;
}
paymentMethodChanged(e) {
const val = e.target.firstChild && e.target.firstChild.value;
val && localState.get('paymentMethod').put(val);
}
renderPayment() {
return html`
<div class="side-padding-xs">
<h3>Select a payment method</h3>
<p>
<label for="bitcoin" onClick=${e => this.paymentMethodChanged(e)}>
<input type="radio" name="payment" id="bitcoin" value="bitcoin" checked=${this.state.paymentMethod === 'bitcoin'}/>
Bitcoin
</label>
</p>
<p>
<label for="dogecoin" onClick=${e => this.paymentMethodChanged(e)}>
<input type="radio" name="payment" id="dogecoin" value="dogecoin" checked=${this.state.paymentMethod === 'dogecoin'}/>
Dogecoin
</label>
</p>
<button onClick=${() => this.setState({page:'confirmation'})}>Next</button>
</div>
`;
}
renderConfirmation() {
const total = Object.keys(this.cart).reduce((sum, currentKey) => {
return sum + parseInt(this.state.items[currentKey].price) * this.cart[currentKey];
}, 0);
return html`
<h3 class="side-padding-xs">Confirmation</h3>
<div class="flex-table">
${Object.keys(this.cart).filter(k => !!this.cart[k]).map(k => {
const i = this.state.items[k];
return html`
<div class="flex-row">
<div class="flex-cell">
<${SafeImg} src=${i.thumbnail}/>
${i.name}
</div>
<div class="flex-cell no-flex price-cell">
<p>
${this.cart[k]} x ${parseInt(i.price)}
</p>
<span class="price">${parseInt(i.price) * this.cart[k]} </span>
</div>
</div>
`;
})}
<div class="flex-row">
<div class="flex-cell"></div>
<div class="flex-cell no-flex"><b>Total ${total} </b></div>
</div>
</div>
<p>
Delivery:<br/>
${this.state.delivery.name}<br/>
${this.state.delivery.address}<br/>
${this.state.delivery.email}
</p>
<p>Payment method: <b>${this.state.paymentMethod}</b></p>
<p class="side-padding-xs"><button onClick=${() => this.confirm()}>Confirm</button></p>
`;
}
render() {
let page;
const p = this.state.page;
if (p === 'delivery') {
page = this.renderDelivery();
} else if (p === 'confirmation') {
page = this.renderConfirmation();
} else if (p === 'payment') {
page = this.renderPayment();
} else {
page = this.renderCart();
}
return html`
<div class="main-view" id="profile">
<div class="content">
<a href="/store/${this.props.id}"><iris-profile-attribute pub=${this.props.id}/></a>
<h2>Checkout</h2>
<div class="flex-table">
${Object.keys(this.cart).filter(k => !!this.cart[k]).map(k => {
const i = this.state.items[k];
return html`
<div class="flex-row">
<div class="flex-cell">
<${SafeImg} src=${i.thumbnail}/>
${i.name}
</div>
<div class="flex-cell no-flex price-cell">
<p>
${this.cart[k]} x ${parseInt(i.price)}
</p>
<span class="price">${parseInt(i.price) * this.cart[k]} </span>
</div>
</div>
`;
})}
<div class="flex-row">
<div class="flex-cell"><b>Total</b></div>
<div class="flex-cell no-flex"><b>${total} </b></div>
</div>
</div>
<p>
<button>Checkout</button>
<a href="/store/${this.props.id}"><iris-profile-attribute pub=${this.props.id}/></a>
</p>
<div id="store-steps">
<div class=${p === 'cart' ? 'active' : ''} onClick=${() => this.setState({page:'cart'})}>Cart</div>
<div class=${p === 'delivery' ? 'active' : ''} onClick=${() => this.setState({page:'delivery'})}>Delivery</div>
<div class=${p === 'payment' ? 'active' : ''} onClick=${() => this.setState({page:'payment'})}>Payment</div>
<div class=${p === 'confirmation' ? 'active' : ''} onClick=${() => this.setState({page:'confirmation'})}>Confirm</div>
</div>
${page}
</div>
</div>`;
}
@ -90,12 +213,15 @@ class CheckoutView extends Component {
componentDidMount() {
const pub = this.props.id;
this.setState({page:'cart'})
this.eventListeners.forEach(e => e.off());
this.cart = {};
localState.get('cart').get(pub).map().on((v, k) => {
this.cart[k] = v;
this.setState({cart: this.cart})
});
localState.get('paymentMethod').on(paymentMethod => this.setState({paymentMethod}));
localState.get('delivery').open(delivery => this.setState({delivery}));
}
}

View File

@ -22,6 +22,7 @@ class ProductView extends Component {
this.eventListeners = [];
this.followedUsers = new Set();
this.followers = new Set();
this.cart = {};
this.state = {
items: {
aa: {name: 'Doge T-shirt', description: 'Amazing t shirt with doge', price: '100€'},
@ -33,17 +34,31 @@ class ProductView extends Component {
};
}
addToCart() {
const count = (this.cart[this.props.id] || 0) + 1;
localState.get('cart').get(this.props.store).get(this.props.id).put(count);
}
showProduct() {
const cartTotalItems = Object.values(this.cart).reduce((sum, current) => sum + current, 0);
const i = this.state.items[this.props.id];
return html`
<div class="main-view" id="profile">
<div class="content">
<a href="/store/${this.props.store}"><iris-profile-attribute pub=${this.props.store}/></a>
${cartTotalItems ? html`
<p>
<button onClick=${() => route('/checkout/' + this.props.store)}>Shopping cart (${cartTotalItems})</button>
</p>
` : ''}
<h3>${i.name}</h3>
<${SafeImg} src=${i.thumbnail}/>
<p class="description">${i.description}</p>
<p class="price">${i.price}</p>
<button class="add">Add to cart</button>
<button class="add" onClick=${() => this.addToCart()}>
Add to cart
${this.cart[this.props.id] ? ` (${this.cart[this.props.id]})` : ''}
</button>
</div>
</div>`;
}
@ -89,6 +104,11 @@ class ProductView extends Component {
this.eventListeners.forEach(e => e.off());
this.setState({followedUserCount: 0, followerCount: 0, name: '', photo: '', about: ''});
this.isMyProfile = Session.getPubKey() === pub;
this.cart = {};
localState.get('cart').get(this.props.store).map().on((v, k) => {
this.cart[k] = v;
this.setState({cart: this.cart})
});
}
}

View File

@ -34,14 +34,14 @@ class StoreView extends Component {
};
}
addToCart(k) {
addToCart(k, e) {
e.stopPropagation();
const count = (this.cart[k] || 0) + 1;
localState.get('cart').get(this.props.id).get(k).put(count);
}
render() {
const reducer = (accumulator, currentValue) => accumulator + currentValue;
const cartTotalItems = Object.values(this.cart).reduce(reducer, 0);
const cartTotalItems = Object.values(this.cart).reduce((sum, current) => sum + current, 0);
this.isMyProfile = Session.getPubKey() === this.props.id;
const chat = chats[this.props.id];
const uuid = chat && chat.uuid;
@ -106,18 +106,23 @@ class StoreView extends Component {
<h3>Store</h3>
${cartTotalItems ? html`
<p>
<button onClick=${() => route('/cart/' + this.props.id)}>Shopping cart (${cartTotalItems})</button>
<button onClick=${() => route('/checkout/' + this.props.id)}>Shopping cart (${cartTotalItems})</button>
</p>
` : ''}
${this.isMyProfile ? html`
<div class="store-item" onClick=${() => route(`/product//${this.props.id}`)}>
<a href="/product/new/${this.props.id}" class="name">Add item</a>
</div>
` : ''}
${Object.keys(this.state.items).map(k => {
const i = this.state.items[k];
return html`
<div class="store-item">
<div class="store-item" onClick=${() => route(`/product/${k}/${this.props.id}`)}>
<${SafeImg} src=${i.thumbnail}/>
<a href="/product/${k}/${this.props.id}" class="name">${i.name}</a>
<p class="description">${i.description}</p>
<p class="price">${i.price}</p>
<button class="add" onClick=${() => this.addToCart(k)}>
<button class="add" onClick=${e => this.addToCart(k, e)}>
Add to cart
${this.cart[k] ? ` (${this.cart[k]})` : ''}
</button>