Wednesday 24 July 2019

django 61 adding price tag

shopping cart has price tags, default price is $10.

login as bob, change stages price to $8

stages is now $8

Nick's cart, stages price changed, total changed.

change stages qty to 7, total changed.

delete stages, total updated.

#django/modals

class Album(models.Model):
    artist = models.CharField(max_length=50)
    album_title = models.CharField(max_length=50)
    genre = models.CharField(max_length=50)
    album_logo = models.FileField()
    date_posted = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    price = models.DecimalField(decimal_places=2,max_digits=5, default=10)

-------------------------------
#django/api/serializers

class MusicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Album
        fields = ('id', 'artist', 'album_title', 'genre', 'album_logo', 'date_posted', 'author', 'price')

--------------------------------
//react/pages/shoppingCart

import React, { Component } from 'react';
import '../App.css';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { getShoppingItems } from '../redux/actions/getShoppingIItems';
import { getAlbums } from '../redux/actions/getAlbums';
import { getUsers } from '../redux/actions/getUser';
import { putShoppingItem } from '../redux/actions/putShoppingItem';
import { deleteShoppingItem } from '../redux/actions/deleteShoppingItem'
import { Input, message, Tag } from 'antd';
import { Button } from 'reactstrap';
import { MdExposureNeg1, MdExposurePlus1 } from "react-icons/md";

class ShoppingCart extends Component {
    constructor(props) {
        super(props);

        this.state = {
            qty: [],
            qty_input: [],
            qty_loaded: false,
        };
    }

    componentDidMount() {

        //start fetching database once logged in
        if (this.props.loggedin) {
            if (!this.props.gotAlbums) {
                this.props.dispatch(getAlbums(this.props.token));
            }
            if (!this.props.gotUsers) {
                this.props.dispatch(getUsers(this.props.token));
            }
            if (!this.props.gotShoppingItems) {
                this.props.dispatch(getShoppingItems(this.props.token));
            }

            //wait for 5sec, check every 0.1s to see if shoppingItems are fetched
            let i = 0;
            const waitShoppingItem = setInterval(async () => {
                if (this.props.gotShoppingItems) {
                    await this.setState({ qty: [], qty_input: [], qty_loaded: false })

                    this.props.shoppingItems.map(async (item, index) => {
                        await this.setState(prevState => {
                            return {
                                qty: prevState.qty.concat({ album_id: item.album, qty: item.quantity }),
                                qty_input: prevState.qty_input.concat({ album_id: item.album, qty: null })
                            }
                        })

                        if (this.state.qty.length === this.props.shoppingItems.length) { this.setState({ qty_loaded: true }) }
                    })

                    clearInterval(waitShoppingItem);
                }
                if (i == 50) {
                    message.error('fetching shopping items timed out.')
                    clearInterval(waitShoppingItem);
                }
                i++;
            }, 100)
        }
    }

    add = async (album_id) => {
        await this.setState(prevState => { return { qty_input: prevState.qty_input.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: null }) } });
        this.setState(prevState => { return { qty: prevState.qty.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: prevState.qty.find(item => item.album_id === album_id).qty + 1 }) } })
    }

    subtract = async (album_id) => {
        await this.setState(prevState => { return { qty_input: prevState.qty_input.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: null }) } });
        if (this.state.qty.find(item => item.album_id === album_id).qty > 1) {
            this.setState(prevState => { return { qty: prevState.qty.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: prevState.qty.find(item => item.album_id === album_id).qty - 1 }) } })
        }
    }

    inputChange = (e, album_id) => {
        if (e.target.value > 0) {
            const newQty = parseInt(e.target.value)
            this.setState(prevState => { return { qty: prevState.qty.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: newQty }) } })
            this.setState(prevState => { return { qty_input: prevState.qty_input.filter(item => item.album_id !== album_id).concat({ album_id: album_id, qty: newQty }) } })
        } else {
            message.error('entered invalid value')
        }
    }

    deleteItem = (id) => {
        this.props.dispatch(deleteShoppingItem(this.props.token, id));

        //wait for 5sec, check every sec to see if delete successful
        let i = 0;
        const waitDelete = setInterval(() => {

            if (this.props.shoppingItemDeleted) {

                clearInterval(waitDelete);
            }
            if (i == 50) {
                message.error('connection timed out.')
                clearInterval(waitDelete);
            }
            i++;
        }, 100)
    }

    updateCart = () => {

        this.state.qty.map((item, index) => {
            //only update items with qty changed
            const oldItem = this.props.shoppingItems.find(i => i.album === item.album_id)
            if (item.qty !== oldItem.quantity) {
                const formData = new FormData();
                formData.set('shopper', this.props.users.find(user => user.username === this.props.username).id);
                formData.set('album', item.album_id);
                formData.set('quantity', item.qty);

                this.props.dispatch(putShoppingItem(this.props.token, oldItem.id, formData));

                //wait for 5sec, check every sec to see if updateShoppingItem successful
                let i = 0;
                const waitUpdate = setInterval(() => {
                    const newItem = this.props.shoppingItems.find(i => i.album === item.album_id)
                    if (newItem.quantity === item.qty) {
                        message.success('Updated quantity of ' + this.props.albums.filter(album => album.id === item.album_id)[0].album_title + ' to ' + item.qty)

                        clearInterval(waitUpdate);
                    }
                    if (i == 50) {
                        message.error('connection timed out.')
                        clearInterval(waitUpdate);
                    }
                    i++;
                }, 100)
            }
        })
    }

    render() {
        if (!this.props.loggedin) {
            return <Redirect to='/login' />
        }

        let total = 0
        return (
            <div style={{ padding: '10px', marginTop: '10px' }}>
                <legend>Shopping Cart</legend>
                <hr />
                <div style={{ color: 'red' }}>{this.props.errorShoppingItem} {this.props.errorAlbum}</div>
                {
                    this.state.qty_loaded ?
                        this.props.shoppingItems
                            .sort((a, b) => { return this.props.albums.find(album => album.id === a.album).album_title.toUpperCase().localeCompare(this.props.albums.find(album => album.id === b.album).album_title.toUpperCase()) })
                            .map((item, index) => {
                                const album = this.props.albums.find(album => album.id === item.album)
                                total = total + album.price * item.quantity

                                return <div key={index}>
                                    <div style={{ width: '90%', display: 'flex', justifyContent: 'space-between' }}>
                                        <div style={{ width: '90%', display: 'flex', justifyContent: 'space-between' }}>

                                            <span style={{ fontStyle: 'italic' }}>{album.album_title} <Tag color='geekblue'>${album.price}</Tag></span>
                                            <Input
                                                type='number'
                                                onChange={(e) => { this.inputChange(e, item.album) }}
                                                style={{ width: '150px' }}
                                                addonBefore={<MdExposureNeg1 onClick={() => this.subtract(item.album)} style={{ cursor: 'pointer', fontSize: '20px' }} />}
                                                addonAfter={<MdExposurePlus1 onClick={() => this.add(item.album)} style={{ cursor: 'pointer', fontSize: '20px' }} />}
                                                value={this.state.qty_input.find(i => i.album_id === item.album).qty || this.state.qty.find(i => i.album_id === item.album).qty}
                                            />

                                        </div>
                                        <b style={{ textAlign: 'right' }} onClick={() => this.deleteItem(item.id)} style={{ cursor: 'pointer' }}>X</b>
                                    </div>
                                    <hr />
                                </div>
                            })
                        : null
                }
                <div style={{ width: '80%', display: 'flex', justifyContent: 'space-between' }}>
                    <Button color='success' size='sm' onClick={() => this.updateCart()}>Update</Button>
                    <span>Total: <Tag color='geekblue'>${total}</Tag></span>
                </div>
            </div>
        );
    }
}

export default connect(
    (store) => {
        return {
            loggedin: store.login.fetched,
            gotShoppingItems: store.shoppingItems.fetched,
            shoppingItems: store.shoppingItems.shoppingItems,
            errorShoppingItem: store.shoppingItems.error,
            shoppingItemUpdated: store.shoppingItems.updated,
            shoppingItemDeleted: store.shoppingItems.deleted,
            albums: store.albums.albums,
            gotAlbums: store.albums.fetched,
            errorAlbum: store.albums.error,
            users: store.users.users,
            gotUsers: store.users.fetched,
            errorUser: store.users.error,
            username: store.login.username,
            token: store.login.token,
            loggedin: store.login.fetched,
        };
    }
)(ShoppingCart);

No comments:

Post a Comment