Category pages

main
Ashelyn Dawn 5 years ago
parent b59134dc5e
commit 5ccda75fff

@ -10,6 +10,12 @@ router.get('/', async (req, res) => {
res.json(categories)
})
router.get('/by-slug/:slug', async (req, res) => {
const category = await db.category.findBySlug(req.params.slug)
res.json(category)
})
const newCategoryValidators = [
validate.validUrlSlug('urlslug'),
validate.requiredString('name'),
@ -35,3 +41,8 @@ router.delete('/:category_uuid/items/:item_uuid', async (req, res) => {
const category = await db.category.removeItem(req.params.category_uuid, req.params.item_uuid);
res.json(category)
})
router.put('/:parent_uuid/children/:child_uuid', async (req, res) => {
const category = await db.category.addCategory(req.params.parent_uuid, req.params.child_uuid);
res.json(category)
})

@ -10,9 +10,11 @@ export default function Card({item, numberInCart}) {
return (
<div className={styles.card}>
<h3><Link href={`/store/item/${item.urlslug}`}><a>{item.name}</a></Link></h3>
<div className={styles['card-image']}>
{featuredImage && <img src={`/api/images/${featuredImage.uuid}/thumb`} />}
</div>
{featuredImage && (
<div className={styles['card-image']}>
<img src={`/api/images/${featuredImage.uuid}/thumb`} />
</div>
)}
<div className={styles['card-text']}>{item.description}</div>
<ul className={styles['card-details'] + (item.number_in_stock > 0 ? '' : ' ' + styles['out-of-stock'])}>
<li className={styles['number-in-stock']}>

@ -40,7 +40,19 @@ module.exports = [{
'description',
'urlslug'
],
associations: [
{name: 'parent', mapId: 'bareCategoryMap', columnPrefix: 'parent_category_'}
],
collections: [
{name: 'items', mapId: 'itemMap', columnPrefix: 'item_'}
{name: 'items', mapId: 'itemMap', columnPrefix: 'item_'},
{name: 'children', mapId: 'bareCategoryMap', columnPrefix: 'child_category_'}
]
},{
mapId: 'bareCategoryMap',
idProperty: 'uuid',
properties: [
'name',
'description',
'urlslug'
]
}]

@ -14,6 +14,18 @@ category.findAll = async () => {
return joinjs.map(rows, mappings, 'categoryMap', 'category_');
}
category.findBySlug = async slug => {
const query = {
text: 'select * from sos.v_category where category_urlslug = $1',
values: [slug]
}
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'categoryMap', 'category_')[0];
}
category.create = async (name, urlslug, description) => {
const query = {
text: 'select * from sos.create_category($1::text, $2::citext, $3::text)',
@ -60,3 +72,18 @@ category.removeItem = async (category_uuid, item_uuid) => {
return joinjs.map(rows, mappings, 'categoryMap', 'category_')[0];
}
category.addCategory = async (parent_uuid, child_uuid) => {
const query = {
text: 'select * from sos.add_category_to_category($1, $2)',
values: [
parent_uuid,
child_uuid
]
}
debug(query)
const {rows} = await pg.query(query);
return joinjs.map(rows, mappings, 'categoryMap', 'category_')[0];
}

@ -53,9 +53,14 @@ create or replace view sos.v_category as
"child_category".category_uuid as child_category_uuid,
"child_category".category_name as child_category_name,
"child_category".category_urlslug as child_category_urlslug,
"parent_category".category_uuid as parent_category_uuid,
"parent_category".category_name as parent_category_name,
"parent_category".category_urlslug as parent_category_urlslug,
v_item.*
from sos."category"
left join sos."category_category" on "category".category_uuid = "category_category".category_category_parent_uuid
left join sos."category" "child_category" on "category_category".category_category_child_uuid = "child_category".category_uuid
left join sos."category_category" "child_category_link" on "category".category_uuid = "child_category_link".category_category_parent_uuid
left join sos."category_category" "parent_category_link" on "category".category_uuid = "parent_category_link".category_category_child_uuid
left join sos."category" "child_category" on "child_category_link".category_category_child_uuid = "child_category".category_uuid
left join sos."category" "parent_category" on "parent_category_link".category_category_parent_uuid = "parent_category".category_uuid
left join sos."category_item" on "category".category_uuid = "category_item".category_item_category_uuid
left join sos.v_item on "category_item".category_item_item_uuid = item_uuid;

@ -192,6 +192,38 @@ begin
return query select * from sos.v_category where category_uuid = _category_uuid;
end; $function$;
create or replace function sos.add_item_to_category(_category_uuid uuid, _item_uuid uuid)
returns setof sos.v_category
language plpgsql
as $function$
begin
insert into sos."category_item" (
category_item_item_uuid,
category_item_category_uuid
) values (
_item_uuid,
_category_uuid
);
return query select * from sos.v_category where category_uuid = _category_uuid;
end; $function$;
create or replace function sos.add_category_to_category(_parent_uuid uuid, _child_uuid uuid)
returns setof sos.v_category
language plpgsql
as $function$
begin
insert into sos."category_category" (
category_category_parent_uuid,
category_category_child_uuid
) values (
_parent_uuid,
_child_uuid
);
return query select * from sos.v_category where category_uuid = _parent_uuid;
end; $function$;
create or replace function sos.create_cart(_session_uuid uuid)
returns setof sos.v_cart
language plpgsql

@ -22,51 +22,49 @@ export default function Cart({cart, setCart}){
return (
<>
<Head><title>Cart</title></Head>
<>
<h2>Cart</h2>
<h2>Cart</h2>
{
numItems > 0
?<Table
columns={[
{name: 'Item', extractor: row => row.item.name},
{name: 'Quantity in Cart', extractor: row => row.count},
{name: 'Price Each', extractor: row => '$' + (row.item.price_cents / 100).toFixed(2)},
{name: 'Total Price', extractor: row => '$' + (row.count * row.item.price_cents / 100).toFixed(2)},
{name: '', extractor: row =>
<button className="buttonLink" onClick={handleRemove(row.item.uuid)}>Remove</button>
}
]}
rows={cart?.items?.map(row=>({
...row,
id: row.item.uuid
}))}
foot={[
'Total:',
cart?.items.map(r=>r.count).reduce((a,b) => (a+b), 0) || 0,
'',
'$' + ((cart?.items.map(r=>r.count * r.item.price_cents).reduce((a,b) => (a+b), 0) || 0) / 100).toFixed(2),
''
]}
/>
// Empty cart table
:<Table
columns={[
{name: 'No items in cart'}
]}
foot={[
'Total:',
'',
'',
'$0.00'
]}
/>
}
{
numItems > 0
?<Table
columns={[
{name: 'Item', extractor: row => row.item.name},
{name: 'Quantity in Cart', extractor: row => row.count},
{name: 'Price Each', extractor: row => '$' + (row.item.price_cents / 100).toFixed(2)},
{name: 'Total Price', extractor: row => '$' + (row.count * row.item.price_cents / 100).toFixed(2)},
{name: '', extractor: row =>
<button className="buttonLink" onClick={handleRemove(row.item.uuid)}>Remove</button>
}
]}
rows={cart?.items?.map(row=>({
...row,
id: row.item.uuid
}))}
foot={[
'Total:',
cart?.items.map(r=>r.count).reduce((a,b) => (a+b), 0) || 0,
'',
'$' + ((cart?.items.map(r=>r.count * r.item.price_cents).reduce((a,b) => (a+b), 0) || 0) / 100).toFixed(2),
''
]}
/>
// Empty cart table
:<Table
columns={[
{name: 'No items in cart'}
]}
foot={[
'Total:',
'',
'',
'$0.00'
]}
/>
}
<FormController>
<Button enabled={!!(cart?.items?.length)} type="submit">Proceed to Checkout</Button>
</FormController>
</>
<FormController>
<Button enabled={!!(cart?.items?.length)} type="submit">Proceed to Checkout</Button>
</FormController>
</>
)
}

@ -0,0 +1,52 @@
import React, {useState} from 'react'
import Head from 'next/head'
import Link from 'next/link'
import Card from '~/components/card'
import styles from './style.module.css'
Category.getInitialProps = async function({ctx: {axios, query: {slug}}}){
console.log(slug)
const {data: category} = await axios.get(`/api/categories/by-slug/${slug}`)
console.log(category)
if(!category) {
const err = new Error("Not found")
err.status = 404
throw err;
}
return {category}
}
export default function Category({category: {items, children, parent, ...category}}){
return (
<>
<Head><title>{category.name}</title></Head>
<h2>{category.name}</h2>
<div className={styles.pageContainer}>
{parent && (
<Link href={`/store/category/${parent.urlslug}`}><a className={styles.parentLink}>&lt; Back to {parent.name}</a></Link>
)}
<p className={styles.description}>{category.description}</p>
{children.length > 0 && <div className={styles.childCategories}>
{children.map(childCategory => (
<>
<Link href={`/store/category/${childCategory.urlslug}`}><a>{childCategory.name}</a></Link>
</>
))}
</div>}
<div className={styles.items}>
{items.length > 0 ? (
<div className="cardContainer">
{items.map(item=>
<Card item={item}/>
)}
</div>
) : <p>No items found</p> }
</div>
</div>
</>
)
}

@ -0,0 +1,36 @@
.pageContainer {
max-width: 800px;
margin: 0 auto;
width: calc(100% - 100px);
position: relative;
}
.parentLink {
position: absolute;
top: -65px;
color: rgb(121, 10, 141);
}
.description {
text-align: center;
margin-top: -20px;
}
.childCategories {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
}
.childCategories > a {
display: inline-block;
border: solid 1px rgb(248, 48, 255);
background: rgba(248, 48, 255, 0.4);
padding: 4px 10px;
border-radius: 15px;
text-decoration: none;
color: black;
margin: 10px 20px;
}

@ -4,7 +4,7 @@ import Router from 'next/router'
import {FormController, NumInput, Button} from '~/components/form'
import isNum from 'validator/lib/isNumeric'
import styles from './slug.module.css'
import styles from './style.module.css'
Item.getInitialProps = async function({ctx: {axios, query: {slug}}}){
const {data: item} = await axios.get(`/api/items/by-slug/${slug}`)

Loading…
Cancel
Save