Item card and updates to item view

main
Ashelyn Dawn 5 years ago
parent 1732c7518b
commit b5206279cc

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\index.js"
}
]
}

@ -50,11 +50,6 @@ router.delete('/:uuid', async (req, res) => {
}) })
router.post('/:uuid/images', upload.single('image'), async (req, res) => { router.post('/:uuid/images', upload.single('image'), async (req, res) => {
// TODO: Use the real user when we have authentication
req.user = {
uuid: '56881ad0-8d80-496a-b036-aed03d0895ce'
}
// Handle either image upload body or JSON body // Handle either image upload body or JSON body
try { try {
if(req.file) if(req.file)

@ -0,0 +1,36 @@
import React from 'react'
import Link from 'next/link'
import styles from './style.module.css'
export default function Card({item, numberInCart}) {
let featuredImage = item.images.filter(i=>i.featured)[0]
if(!featuredImage) featuredImage = item.images[0]
return (
<div className={styles.card}>
<h3><Link href={`/store/sock/${item.urlslug}`}><a>{item.name}</a></Link></h3>
<div className={styles['card-image']}>
{featuredImage && <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']}>
{
item.number_in_stock > 0
? `${item.number_in_stock} pairs in stock`
: 'Currently out of stock'
}
</li>
<li><Link href={`/store/sock/${item.urlSlug}`}><a>Details</a></Link></li>
{
item.number_in_stock > 0 && (
<li>
<a disabled={!(item.number_in_stock > 0)}>Add to Cart</a>
</li>
)
}
</ul>
</div>
)
}

@ -0,0 +1,138 @@
.card{
background:white;
display:inline-block;
position:relative;
margin:20px 5%;
width:90%;
top:0;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}
.card:last-child {
/* Fixes the odd flickering bug */
margin-bottom:0px;
}
.card:hover {
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
top:-3px;
}
.card h3{
font-family:'Cormorant Infant', serif;
font-weight:600;
font-size:22px;
border-bottom:solid 1px black;
text-align:center;
margin:5px;
}
.card h3{
border-bottom:none;
}
.card h3 a{
color:inherit;
text-decoration:none;
border-bottom:solid 1px black;
}
.card a:hover{
color: #760c88;
border-color: #760c88;
transition: .2s ease-in-out;
transition-property: color, border-color;
}
.card .card-text {
margin:0;
left: 40%;
top: 34px;
}
.card .card-text p:first-child{
margin-top:0px;
}
.card .card-text{
padding:10px;
}
.card .card-text:after{
content:'';
display:block;
clear:both;
}
.card .card-image{
width: 40%;
padding: 10px;
position:relative;
float:left;
}
.card img{
width:80%;
padding:10px;
background: #d4d4fb;
border: solid 1px #dec8e2;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}
.card p, .card a{
color:black;
opacity:.87;
}
.card .card-details{
font-family: 'Cormorant SC', serif;
list-style:none;
text-align:center;
margin-top:0px;
padding:0;
}
.card .card-details.out-of-stock{
opacity:.54;
}
.card .card-details li a.disabled{
opacity:.54;
text-decoration:none;
}
.card .card-details.out-of-stock li.numberInStock{
color:#5d0000
}
.card .card-details li{
display:inline-block;
margin-right:20px;
}
.card .card-details .numberInStock{
font-weight:600;
}
.card .card-details li:last-child{
margin-right:0;
}
.catalogue{
max-width:2000px;
column-count: 3;
}
@media (max-width:400px){
.card .card-image{
width:100%;
float:none;
padding:0;
display: block;
margin: 10px auto;
text-align: center;
}
.card .card-image img{
margin: 10px;
}
}

@ -19,7 +19,8 @@ module.exports = [{
'description', 'description',
'urlslug', 'urlslug',
'price_cents', 'price_cents',
'published' 'published',
'number_in_stock'
], ],
collections: [ collections: [
{name: 'images', mapId: 'imageMap', columnPrefix: 'image_'} {name: 'images', mapId: 'imageMap', columnPrefix: 'image_'}

@ -6,7 +6,7 @@ const mappings = require('../mappings')
const category = module.exports = {} const category = module.exports = {}
category.findAll = async () => { category.findAll = async () => {
const query = 'select * from v_category' const query = 'select * from sos.v_category'
debug(query); debug(query);
@ -16,7 +16,7 @@ category.findAll = async () => {
category.create = async (name, urlslug, description) => { category.create = async (name, urlslug, description) => {
const query = { const query = {
text: 'select * from public.create_category($1::text, $2::citext, $3::text)', text: 'select * from sos.create_category($1::text, $2::citext, $3::text)',
values: [ values: [
name, name,
urlslug, urlslug,
@ -32,7 +32,7 @@ category.create = async (name, urlslug, description) => {
category.addItem = async (category_uuid, item_uuid) => { category.addItem = async (category_uuid, item_uuid) => {
const query = { const query = {
text: 'select * from public.add_item_to_category($1::uuid, $2::uuid)', text: 'select * from sos.add_item_to_category($1::uuid, $2::uuid)',
values: [ values: [
category_uuid, category_uuid,
item_uuid item_uuid
@ -47,7 +47,7 @@ category.addItem = async (category_uuid, item_uuid) => {
category.removeItem = async (category_uuid, item_uuid) => { category.removeItem = async (category_uuid, item_uuid) => {
const query = { const query = {
text: 'select * from public.remove_item_from_category($1::uuid, $2::uuid)', text: 'select * from sos.remove_item_from_category($1::uuid, $2::uuid)',
values: [ values: [
category_uuid, category_uuid,
item_uuid item_uuid

@ -8,7 +8,7 @@ const sharp = require('sharp')
const item = module.exports = {} const item = module.exports = {}
item.findAll = async () => { item.findAll = async () => {
const query = 'select * from v_item' const query = 'select * from sos.v_item'
debug(query); debug(query);
@ -18,7 +18,7 @@ item.findAll = async () => {
item.findById = async (item_uuid) => { item.findById = async (item_uuid) => {
const query = { const query = {
text: 'select * from v_item where item_uuid = $1', text: 'select * from sos.v_item where item_uuid = $1',
values: [ values: [
item_uuid item_uuid
] ]
@ -32,7 +32,7 @@ item.findById = async (item_uuid) => {
item.findBySlug = async (item_slug) => { item.findBySlug = async (item_slug) => {
const query = { const query = {
text: 'select * from v_item where item_urlslug = $1', text: 'select * from sos.v_item where item_urlslug = $1',
values: [ values: [
item_slug item_slug
] ]
@ -46,7 +46,7 @@ item.findBySlug = async (item_slug) => {
item.create = async (name, urlslug, description, price_cents, published) => { item.create = async (name, urlslug, description, price_cents, published) => {
const query = { const query = {
text: 'select * from public.create_item($1::text, $2::citext, $3::text, $4::integer, $5::boolean)', text: 'select * from sos..create_item($1::text, $2::citext, $3::text, $4::integer, $5::boolean)',
values: [ values: [
name, name,
urlslug, urlslug,
@ -75,7 +75,7 @@ item.addImage = async (item_uuid, image_buffer, uploader_uuid) => {
]) ])
const query = { const query = {
text: 'select * from public.add_image_to_item($1, $2, $3, $4, $5)', text: 'select * from sos.add_image_to_item($1, $2, $3, $4, $5)',
values: [ values: [
item_uuid, item_uuid,
image, image,
@ -90,8 +90,8 @@ item.addImage = async (item_uuid, image_buffer, uploader_uuid) => {
} }
const imageSizeQueries = { const imageSizeQueries = {
'large': 'select * from get_image_large($1)', 'large': 'select * from sos.get_image_large($1)',
'thumb': 'select * from get_image_thumb($1)' 'thumb': 'select * from sos.get_image_thumb($1)'
} }
item.getImage = async (image_uuid, size) => { item.getImage = async (image_uuid, size) => {
@ -109,7 +109,7 @@ item.getImage = async (image_uuid, size) => {
item.removeImage = async (image_uuid) => { item.removeImage = async (image_uuid) => {
const query = { const query = {
text: 'select * from public.delete_image($1)', text: 'select * from sos.delete_image($1)',
values: [ values: [
image_uuid image_uuid
] ]
@ -121,7 +121,7 @@ item.removeImage = async (image_uuid) => {
item.removeItem = async (item_uuid) => { item.removeItem = async (item_uuid) => {
const query = { const query = {
text: 'select * from public.delete_item($1)', text: 'select * from sos.delete_item($1)',
values: [ values: [
item_uuid item_uuid
] ]

@ -7,7 +7,7 @@ const session = module.exports = {}
session.create = async (user_uuid, ip_address, user_agent, referer, origin_link_uuid) => { session.create = async (user_uuid, ip_address, user_agent, referer, origin_link_uuid) => {
const query = { const query = {
text: 'select * from login_user_session($1, $2, $3, $4, $5, $6)', text: 'select * from sos.login_user_session($1, $2, $3, $4, $5, $6)',
values: [ values: [
user_uuid, user_uuid,
'2 hours', '2 hours',
@ -26,7 +26,7 @@ session.create = async (user_uuid, ip_address, user_agent, referer, origin_link_
session.validate = async (session_uuid) => { session.validate = async (session_uuid) => {
const query = { const query = {
text: 'select * from validate_session($1)', text: 'select * from sos.validate_session($1)',
values: [ values: [
session_uuid session_uuid
] ]
@ -40,7 +40,7 @@ session.validate = async (session_uuid) => {
session.update = async (session_uuid) => { session.update = async (session_uuid) => {
const query = { const query = {
text: 'select * from update_session($1)', text: 'select * from sos.update_session($1)',
values: [ values: [
session_uuid session_uuid
] ]

@ -41,7 +41,7 @@ user.register = async (email, password) => {
const hash = await bcrypt.hash(password, saltRounds) const hash = await bcrypt.hash(password, saltRounds)
const query = { const query = {
text: 'select * from register_user($1, $2)', text: 'select * from sos.register_user($1, $2)',
values: [ values: [
email, email,
hash hash

@ -1,7 +1,18 @@
CREATE database sos; CREATE database sos;
create schema sos;
create user sos with encrypted password 'password'; create user sos with encrypted password 'password';
grant all privileges on database sos to sos; grant all privileges on database sos to sos;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO sos; grant usage on schema public to sos;
grant usage on schema sos to sos;
alter default privileges in schema public grant all privileges on all tables to sos;
alter default privileges in schema sos grant all privileges on all tables to sos;
alter default privileges in schema sos grant all privileges on all procedures to sos;
grant all privileges on all tables in schema public to sos;
grant all privileges on all tables in schema sos to sos;
create extension if not exists "uuid-ossp"; create extension if not exists "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS citext; CREATE EXTENSION IF NOT EXISTS citext;

@ -1,4 +1,4 @@
create or replace view public.v_session as create or replace view sos.v_session as
select select
"session".*, "session".*,
"session_user".user_email as session_user_email, "session_user".user_email as session_user_email,
@ -8,35 +8,46 @@ create or replace view public.v_session as
"session_user".user_time_email_confirmed as session_user_time_email_confirmed, "session_user".user_time_email_confirmed as session_user_time_email_confirmed,
"login_link".* "login_link".*
from "session" from "session"
left join "user" "session_user" on "session".session_user_uuid = "session_user".user_uuid left join "user" "session_user" on "session".session_user_uuid = "session_user".user_uuid
left join "login_link" on "session".session_originating_link = "login_link".login_link_uuid; left join "login_link" on "session".session_originating_link = "login_link".login_link_uuid;
create or replace view public.v_login_link as create or replace view sos.v_login_link as
select select
* *
from "login_link" from "login_link"
left join "user" on "login_link".login_link_user_uuid = "user".user_uuid; left join "user" on "login_link".login_link_user_uuid = "user".user_uuid;
create or replace view public.v_cart as create or replace view sos.v_cart as
select select
* *
from "cart" from "cart"
left join "cart_item" on "cart".cart_uuid = "cart_item".cart_item_cart_uuid left join "cart_item" on "cart".cart_uuid = "cart_item".cart_item_cart_uuid
left join "item" on "cart_item".cart_item_item_uuid = "item".item_uuid; left join "item" on "cart_item".cart_item_item_uuid = "item".item_uuid;
create or replace view public.v_item as create or replace view sos.v_item as
select select
"item".*, "item".*,
"image".image_uuid, "image".image_uuid,
"image".image_featured, "image".image_featured,
"image".image_mime_type, "image".image_mime_type,
"image".image_date_uploaded, "image".image_date_uploaded,
"user".user_email "user".user_email,
num_added - num_removed as item_number_in_stock
from "item" from "item"
left join "image" on "item".item_uuid = "image".image_item_uuid left join "image" on item.item_uuid = image.image_item_uuid
left join "user" on "image".image_uploader_uuid = "user".user_uuid; left join "user" on image.image_uploader_uuid = "user".user_uuid
left join
(
select
stockchange_item_uuid,
sum(case when stockchange_direction = 'added' then stockchange_change end)::int4 as num_added,
sum(case when stockchange_direction = 'subtracted' then stockchange_change end)::int4 as num_removed
from "item_stockchange"
group by stockchange_item_uuid
) stock_counts
on stock_counts.stockchange_item_uuid = item.item_uuid;
create or replace view public.v_category as create or replace view sos.v_category as
select select
"category".*, "category".*,
"child_category".category_uuid as child_category_uuid, "child_category".category_uuid as child_category_uuid,
@ -47,4 +58,4 @@ create or replace view public.v_category as
left join "category_category" on "category".category_uuid = "category_category".category_category_parent_uuid left join "category_category" on "category".category_uuid = "category_category".category_category_parent_uuid
left join "category" "child_category" on "category_category".category_category_child_uuid = "child_category".category_uuid left join "category" "child_category" on "category_category".category_category_child_uuid = "child_category".category_uuid
left join "category_item" on "category".category_uuid = "category_item".category_item_category_uuid left join "category_item" on "category".category_uuid = "category_item".category_item_category_uuid
left join v_item on "category_item".category_item_item_uuid = item_uuid; left join sos.v_item on "category_item".category_item_item_uuid = item_uuid;

@ -1,5 +1,5 @@
create or replace function public.register_user(_email text, _password_hash text) create or replace function sos.register_user(_email text, _password_hash text)
returns setof public.user returns setof "user"
language plpgsql language plpgsql
as $function$ as $function$
declare declare
@ -16,8 +16,8 @@ begin
return query select * from "user" where user_uuid = _user_uuid; return query select * from "user" where user_uuid = _user_uuid;
end; $function$; end; $function$;
create or replace function public.validate_session(_session_uuid uuid) create or replace function sos.validate_session(_session_uuid uuid)
returns setof public.v_session returns setof sos.v_session
language plpgsql language plpgsql
as $function$ as $function$
begin begin
@ -26,8 +26,8 @@ begin
and session_time_last_active + session_timeout_length > now(); and session_time_last_active + session_timeout_length > now();
end; $function$; end; $function$;
create or replace function public.update_session(_session_uuid uuid) create or replace function sos.update_session(_session_uuid uuid)
returns setof public.v_session returns setof sos.v_session
language plpgsql language plpgsql
as $function$ as $function$
begin begin
@ -39,8 +39,8 @@ begin
return query select * from validate_session(_session_uuid); return query select * from validate_session(_session_uuid);
end; $function$; end; $function$;
create or replace function public.login_user_session(_user_uuid uuid, _timeout_length interval, _ip_addr varchar(50), _user_agent varchar(500), _referer varchar(500), _link uuid) create or replace function sos.login_user_session(_user_uuid uuid, _timeout_length interval, _ip_addr varchar(50), _user_agent varchar(500), _referer varchar(500), _link uuid)
returns setof public.v_session returns setof sos.v_session
language plpgsql language plpgsql
as $function$ as $function$
declare declare
@ -62,11 +62,11 @@ begin
_link _link
) returning session_uuid into _session_uuid; ) returning session_uuid into _session_uuid;
return query select * from public.validate_session(_session_uuid); return query select * from sos.validate_session(_session_uuid);
end; $function$; end; $function$;
create or replace function public.create_item(_name text, _urlslug citext, _description text, _price_cents integer, _published boolean) create or replace function sos.create_item(_name text, _urlslug citext, _description text, _price_cents integer, _published boolean)
returns setof public.v_item returns setof sos.v_item
language plpgsql language plpgsql
as $function$ as $function$
declare declare
@ -86,11 +86,11 @@ begin
_published _published
) returning item_uuid into _item_uuid; ) returning item_uuid into _item_uuid;
return query select * from public.v_item where item_uuid = _item_uuid; return query select * from sos.v_item where item_uuid = _item_uuid;
end; $function$; end; $function$;
create or replace function public.add_image_to_item(_item_uuid uuid, _large_file bytea, _thumb_file bytea, _mime_type varchar, _uploader_uuid uuid) create or replace function sos.add_image_to_item(_item_uuid uuid, _large_file bytea, _thumb_file bytea, _mime_type varchar, _uploader_uuid uuid)
returns setof public.v_item returns setof sos.v_item
language plpgsql language plpgsql
as $function$ as $function$
declare declare
@ -110,11 +110,11 @@ begin
_uploader_uuid _uploader_uuid
) returning image_uuid into _image_uuid; ) returning image_uuid into _image_uuid;
return query select * from public.v_item where item_uuid = _item_uuid; return query select * from sos.v_item where item_uuid = _item_uuid;
end; $function$; end; $function$;
create or replace function public.set_featured_image(_item_uuid uuid, _image_uuid uuid) create or replace function sos.set_featured_image(_item_uuid uuid, _image_uuid uuid)
returns setof public.v_item returns setof sos.v_item
language plpgsql language plpgsql
as $function$ as $function$
begin begin
@ -127,10 +127,10 @@ begin
image_featured = true image_featured = true
where image_uuid = _image_uuid; where image_uuid = _image_uuid;
return query select * from public.v_item where item_uuid = _item_uuid; return query select * from sos.v_item where item_uuid = _item_uuid;
end; $function$; end; $function$;
create or replace function public.get_image_large(_image_uuid uuid) create or replace function sos.get_image_large(_image_uuid uuid)
returns table (image_uuid uuid, image_mime_type varchar, image_file bytea) returns table (image_uuid uuid, image_mime_type varchar, image_file bytea)
language plpgsql language plpgsql
as $function$ as $function$
@ -143,7 +143,7 @@ begin
where "image".image_uuid = _image_uuid; where "image".image_uuid = _image_uuid;
end; $function$; end; $function$;
create or replace function public.get_image_thumb(_image_uuid uuid) create or replace function sos.get_image_thumb(_image_uuid uuid)
returns table (image_uuid uuid, image_mime_type varchar, image_file bytea) returns table (image_uuid uuid, image_mime_type varchar, image_file bytea)
language plpgsql language plpgsql
as $function$ as $function$
@ -156,8 +156,8 @@ begin
where "image".image_uuid = _image_uuid; where "image".image_uuid = _image_uuid;
end; $function$; end; $function$;
create or replace function public.create_category(_category_name text, _category_urlslug citext, _category_description text) create or replace function sos.create_category(_category_name text, _category_urlslug citext, _category_description text)
returns setof public.v_category returns setof sos.v_category
language plpgsql language plpgsql
as $function$ as $function$
declare declare

@ -6,9 +6,19 @@ import Header from '~/components/header'
import Footer from '~/components/footer' import Footer from '~/components/footer'
import "../styles/layout.css" import "../styles/layout.css"
Layout.getInitialProps = async ({ctx}) => { Layout.getInitialProps = async ({Component, ctx}) => {
const {data: user} = await axios.get(`/api/auth`, { headers: ctx.req ? { cookie: ctx.req.headers.cookie } : undefined }) // Configure axios instance
return {user} ctx.axios = axios.create({
headers: ctx.req ? {cookie: ctx.req.headers.cookie} : undefined
})
const {data: user} = await ctx.axios.get(`/api/auth`)
let pageProps = {};
if(Component.getInitialProps)
pageProps = await Component.getInitialProps({ctx})
return {pageProps, user}
} }
function Layout({ Component, pageProps, user }){ function Layout({ Component, pageProps, user }){

@ -1,11 +1,22 @@
import React from 'react' import React from 'react'
import Hero from '~/components/hero' import Hero from '~/components/hero'
import Card from '~/components/card'
const Index = () => ( Index.getInitialProps = async ({ctx})=>{
<> const {data: items} = await ctx.axios.get('/api/items')
<Hero/> return {items}
<p>Homepage</p> }
</>
) export default function Index({items}){
export default Index return (
<>
<Hero/>
<div className="cardContainer">
{items.map(item=>
<Card item={item}/>
)}
</div>
</>
)
}

@ -20,3 +20,33 @@ main {
color: black; color: black;
padding: 30px 0; padding: 30px 0;
} }
.cardContainer{
margin-left: auto;
margin-right: auto;
margin-bottom: 30px;
padding-left: 50px;
padding-right: 50px;
max-width:2000px;
column-count: 3;
}
@media (max-width:1200px){
.cardContainer{
padding:0 2%;
}
}
@media (max-width: 1000px) {
.cardContainer {
column-count: 2;
}
}
@media (max-width: 600px) {
.cardContainer {
margin-top: 20px;
column-count: 1;
}
}

Loading…
Cancel
Save