Add statistics page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
|
||||
import { Manufacturer } from '@zenstackhq/runtime/models';
|
||||
import { Col, Container, Image, Row } from 'react-bootstrap';
|
||||
import { Col, Container, Image, Row, Table } from 'react-bootstrap';
|
||||
import { ContainerWithSection, DrinkComposite, isDrinkWithContainersAndSection, isDrinkWithManufacturer, isDrinkWithStyle } from '~/models/types';
|
||||
|
||||
interface Arguments {
|
||||
@@ -30,39 +30,74 @@ function DrinkPage(arg:Arguments) {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div className="p-5 mb-4 bg-body-tertiary rounded-3" style={{ borderColor: `${borderColor}`, borderWidth: '2px' }} >
|
||||
<div className="p-5 mb-4 bg-body-tertiary rounded-3 border" style={{ borderColor: `${borderColor}`, borderWidth: '2px' }} >
|
||||
<Container fluid>
|
||||
<Row>
|
||||
<Col md={11}>
|
||||
<h1 className="display-5 fw-bold">{arg.drink.name}</h1>
|
||||
ABV: {arg.drink.abv}% {arg.drink.gluten ? "" : " | Gluten-free"} {arg.drink.organic ? " | Organic" : ""}
|
||||
<p className="col-md-8 fs-4">{arg.drink.description}</p>
|
||||
</Col>
|
||||
<Col md={1}>
|
||||
<Image src={arg.drink.image ?? undefined} fluid rounded/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
</Col>
|
||||
<Col>
|
||||
|
||||
</Col>
|
||||
<Col>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</Container>
|
||||
</div>
|
||||
|
||||
<Row className="align-items-md-stretch">
|
||||
<Col md={6}>
|
||||
<Col md={6} className='mb-4'>
|
||||
<div className="h-100 p-5 text-bg-dark rounded-3">
|
||||
<h2>Inventory</h2>
|
||||
{totalinventory == 0 ? (
|
||||
"No inventory"
|
||||
) : ""}
|
||||
{containers.map((container) => (
|
||||
<p>{container.section.name} | {container.type} | {container.inventory}</p>
|
||||
))}
|
||||
) : (
|
||||
<Table data-bs-theme="dark">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Section</th>
|
||||
<th>Container</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{containers.map((container) => (
|
||||
<tr key={container.barcode}>
|
||||
<td>{container.section.name}</td>
|
||||
<td>{container.type}</td>
|
||||
<td>{container.inventory}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
{ manufacturer ? (
|
||||
<Col md={6}>
|
||||
<div className="h-100 p-5 bg-body-tertiary border rounded-3">
|
||||
<h2>{manufacturer.name}</h2>
|
||||
<p>{manufacturer.description}</p>
|
||||
</div>
|
||||
<Col md={6} className='mb-4'>
|
||||
<Container>
|
||||
<Row className="p-5 bg-body-tertiary border rounded-3">
|
||||
<Col md={10}>
|
||||
<h2>{manufacturer.name} ({manufacturer.country_id})</h2>
|
||||
<p>{manufacturer.description}</p>
|
||||
</Col>
|
||||
<Col md={2}>
|
||||
<Image src={manufacturer.image ?? undefined} fluid rounded/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</Col>
|
||||
) : ""}
|
||||
</Row>
|
||||
|
||||
@@ -57,6 +57,7 @@ function DrinkFilter(arg : Arguments) {
|
||||
<Form.Label>Sort</Form.Label>
|
||||
{ [
|
||||
["name", "Name"],
|
||||
["popular", "Popularity"],
|
||||
["abv-asc", "ABV Low -> High"],
|
||||
["abv-desc", "ABV High -> Low"],
|
||||
["inv-asc", "Inventoy Low -> High"],
|
||||
@@ -89,7 +90,7 @@ function DrinkFilter(arg : Arguments) {
|
||||
</Form.Group>
|
||||
<Form.Group controlId="drink-modifiers" className='mb-2'>
|
||||
<Form.Label>Drink</Form.Label>
|
||||
{ ["Gluten-free", "Lactose-free", "Organic"].map(key => (
|
||||
{ ["Gluten-free", "Organic"].map(key => (
|
||||
<Form.Check
|
||||
id={"drinkmod-" + key}
|
||||
type="checkbox"
|
||||
|
||||
@@ -29,14 +29,15 @@ function Header(data: Arguments) {
|
||||
</LinkContainer>
|
||||
<Navbar.Toggle />
|
||||
<Navbar.Collapse>
|
||||
{ loggedin ? (
|
||||
<Nav>
|
||||
<LinkContainer to="/inventory">
|
||||
<Nav.Link>Inventory</Nav.Link>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
) : "" }
|
||||
<Nav className="me-auto">
|
||||
{ loggedin ? (
|
||||
<LinkContainer to="/inventory">
|
||||
<Nav.Link>Inventory</Nav.Link>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to="/inventory/stats">
|
||||
<Nav.Link>Stats</Nav.Link>
|
||||
</LinkContainer>
|
||||
{ showScanButtons ? (
|
||||
<LinkContainer to="/scan">
|
||||
<Nav.Link>Scan</Nav.Link>
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
}
|
||||
|
||||
result = result.concat(await dbe.beer.findMany({
|
||||
include: {style: true, manufacturer: true, containers: {include: {section: true}}},
|
||||
include: {style: true, manufacturer: true, containers: {include: {section: true, checkouts: true}}},
|
||||
where: where
|
||||
}));
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
}
|
||||
|
||||
result = result.concat(await dbe.wine.findMany({
|
||||
include: {style: true, manufacturer: true, containers: {include: {section: true}}},
|
||||
include: {style: true, manufacturer: true, containers: {include: {section: true, checkouts: true}}},
|
||||
where: where
|
||||
}));
|
||||
}
|
||||
@@ -82,7 +82,7 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
}
|
||||
|
||||
result = result.concat(await dbe.soda.findMany({
|
||||
include: {manufacturer: true, containers: {include: {section: true}}},
|
||||
include: {manufacturer: true, containers: {include: {section: true, checkouts: true}}},
|
||||
where: where
|
||||
}));
|
||||
}
|
||||
@@ -96,7 +96,7 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
}
|
||||
|
||||
result = result.concat(await dbe.cocktail.findMany({
|
||||
include: {manufacturer: true, containers: {include: {section: true}}},
|
||||
include: {manufacturer: true, containers: {include: {section: true, checkouts: true}}},
|
||||
where: where
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -18,11 +18,15 @@ export async function findManufacturersFromSearch(searchParams: URLSearchParams)
|
||||
|
||||
if(searchParams.has("drink")){
|
||||
result = result.concat(await dbe.manufacturer.findMany({
|
||||
orderBy: {name: "asc"},
|
||||
where: {drinks: { some: { type: { in: searchParams.getAll("drink").map((str) => {return DrinkType[str as keyof typeof DrinkType]})}}}}
|
||||
}));
|
||||
}
|
||||
else {
|
||||
result = result.concat(await dbe.manufacturer.findMany());
|
||||
result = result.concat(await dbe.manufacturer.findMany({
|
||||
orderBy: {name: "asc"},
|
||||
where: {NOT: {drinks: {none: {}}}}
|
||||
}));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DrinkComposite, isDrinkWithContainers } from "./types";
|
||||
import { DrinkComposite, isDrinkWithContainers, isDrinkWithContainersAndHistory } from "./types";
|
||||
|
||||
function sortName(n1:DrinkComposite, n2:DrinkComposite) : number{
|
||||
if (n1.name > n2.name) {
|
||||
@@ -39,6 +39,24 @@ function sortInventoryHighLow(n1:DrinkComposite, n2:DrinkComposite) : number{
|
||||
return totalInventory(n2) - totalInventory(n1);
|
||||
}
|
||||
|
||||
function totalPopularity(n:DrinkComposite) : number{
|
||||
|
||||
if(isDrinkWithContainersAndHistory(n)){
|
||||
let total = 0;
|
||||
n.containers.map((container) =>{
|
||||
total += container.checkouts.length;
|
||||
});
|
||||
return total;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function sortPopularity(n1:DrinkComposite, n2:DrinkComposite) : number{
|
||||
return totalPopularity(n2) - totalPopularity(n1);
|
||||
}
|
||||
|
||||
|
||||
export function sortDrinksFromSearch(searchParams: URLSearchParams, searchResult: DrinkComposite[]) : DrinkComposite[] {
|
||||
|
||||
var callback = sortName;
|
||||
@@ -63,6 +81,10 @@ export function sortDrinksFromSearch(searchParams: URLSearchParams, searchResult
|
||||
callback = sortInventoryHighLow;
|
||||
}
|
||||
|
||||
if(searchParams.has("sort", "popular")){
|
||||
callback = sortPopularity;
|
||||
}
|
||||
|
||||
var sortedArray: DrinkComposite[] = searchResult.sort(callback);
|
||||
|
||||
return sortedArray;
|
||||
|
||||
+15
-2
@@ -1,9 +1,14 @@
|
||||
import { ContainerType } from '@prisma/client';
|
||||
import { Drink, Beer, BeerStyle, Wine, WineStyle, Soda, Manufacturer, Container, Cocktail, Section } from '@zenstackhq/runtime/models';
|
||||
import { Drink, Beer, BeerStyle, Wine, WineStyle, Soda, Manufacturer, Container, Cocktail, Section, History } from '@zenstackhq/runtime/models';
|
||||
|
||||
export type ContainerWithDrink = Container & { drink: Drink}
|
||||
export type ContainerWithSection = Container & { section: Section}
|
||||
export type ContainerWithHistory = Container & { checkouts: History[]}
|
||||
export type ContainerWithSectionAndHistory = ContainerWithSection & ContainerWithHistory
|
||||
export type DrinkWithContainersAndSection = (Drink & { containers: ContainerWithSection[]})
|
||||
export type DrinkWithContainers = (Drink & { containers: ContainerWithSection[]}) | (Drink & { containers: Container[]})
|
||||
export type DrinkWithContainersAndHistory = (Drink & { containers: ContainerWithHistory[]})
|
||||
export type DrinkWithContainersAndSectionAndHistory = (Drink & { containers: ContainerWithSectionAndHistory[]})
|
||||
export type DrinkWithContainers = DrinkWithContainersAndSection | DrinkWithContainersAndHistory | DrinkWithContainersAndSectionAndHistory | (Drink & { containers: Container[]})
|
||||
export type DrinkWithManufacturer = Drink & { manufacturer: Manufacturer}
|
||||
|
||||
export type BeerWithStyle = Beer & { style : BeerStyle }
|
||||
@@ -19,6 +24,14 @@ export function isDrinkWithContainersAndSection(drink:DrinkComposite) : drink is
|
||||
return ((drink as { containers: ContainerWithSection[]}).containers != undefined);
|
||||
}
|
||||
|
||||
export function isDrinkWithContainersAndHistory(drink:DrinkComposite) : drink is DrinkWithContainersAndHistory{
|
||||
return ((drink as { containers: ContainerWithHistory[]}).containers != undefined);
|
||||
}
|
||||
|
||||
export function isDrinkWithContainersAndSectionAndHistory(drink:DrinkComposite) : drink is DrinkWithContainersAndSectionAndHistory{
|
||||
return ((drink as { containers: ContainerWithSectionAndHistory[]}).containers != undefined);
|
||||
}
|
||||
|
||||
export function isDrinkWithManufacturer(drink:DrinkComposite) : drink is DrinkWithManufacturer{
|
||||
return (drink as { manufacturer: Manufacturer}).manufacturer != undefined;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ export const loader = async ({request} : LoaderFunctionArgs) => {
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
const beerStyles = await dbe.beerStyle.findMany();
|
||||
const wineStyles = await dbe.wineStyle.findMany();
|
||||
const beerStyles = await dbe.beerStyle.findMany({orderBy: {name: "asc"}});
|
||||
const wineStyles = await dbe.wineStyle.findMany({orderBy: {name: "asc"}});
|
||||
const manufacturers = await findManufacturersFromSearch(url.searchParams);
|
||||
|
||||
const drinkResultsUnsorted = await findDrinksFromSearch(url.searchParams);
|
||||
@@ -45,13 +45,17 @@ export default function BeersRoute() {
|
||||
);
|
||||
}
|
||||
|
||||
const numResults = loadData.drinkResults.length;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row className="mt-3">
|
||||
<Col xs={12} md={4} xl={3} className="mb-3">
|
||||
<Col key="filtering" xs={12} md={4} xl={3} className="mb-3">
|
||||
<Accordion>
|
||||
<CustomToggle eventKey="0">Sorting and Filtering</CustomToggle>
|
||||
|
||||
<div>
|
||||
<CustomToggle eventKey="0">Sorting and Filtering</CustomToggle> <span className="float-end">{numResults} Results</span>
|
||||
</div>
|
||||
|
||||
<Accordion.Collapse eventKey="0">
|
||||
<div className="h-100 p-3 mt-3 text-bg-dark rounded-3">
|
||||
<DrinkFilter
|
||||
@@ -64,7 +68,7 @@ export default function BeersRoute() {
|
||||
</Accordion.Collapse>
|
||||
</Accordion>
|
||||
</Col>
|
||||
<Col>
|
||||
<Col key="results">
|
||||
<Row xs={2} md={3} lg={4} className="g-4">
|
||||
{loadData.drinkResults.map((drink) => (
|
||||
<Col >
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData, useSearchParams } from "@remix-run/react";
|
||||
import { enhance } from "@zenstackhq/runtime";
|
||||
import { includes } from "lodash";
|
||||
import { Accordion, Button, Col, Container, Row, useAccordionButton } from "react-bootstrap";
|
||||
import DrinkCard from "~/components/cards/drink.card";
|
||||
import DrinkFilter from "~/components/filters/drink.filter";
|
||||
import { findDrinksFromSearch } from "~/models/drinks.filter.server";
|
||||
import { findManufacturersFromSearch } from "~/models/drinks.server";
|
||||
import { sortDrinksFromSearch } from "~/models/drinks.sort.server";
|
||||
import { ContainerWithDrink } from "~/models/types";
|
||||
|
||||
import { db } from "~/utils/db.server";
|
||||
|
||||
function statsForContainers(containers : ContainerWithDrink[]){
|
||||
var drinksInventory = 0;
|
||||
var beersInventory = 0;
|
||||
var wineInventory = 0;
|
||||
var sodaInventory = 0;
|
||||
var cocktailInventory = 0;
|
||||
var totalMilliliterAlcohol = 0.0;
|
||||
var averageAlcoholPercentage = 0.0;
|
||||
var divider = 0;
|
||||
|
||||
containers.forEach((container) => {
|
||||
drinksInventory += container.inventory;
|
||||
|
||||
switch(container.drink.type){
|
||||
case "Beer":
|
||||
beersInventory += container.inventory;
|
||||
break;
|
||||
case "Wine":
|
||||
wineInventory += container.inventory;
|
||||
break;
|
||||
case "Soda":
|
||||
sodaInventory += container.inventory;
|
||||
break;
|
||||
case "Cocktail":
|
||||
cocktailInventory += container.inventory;
|
||||
break;
|
||||
}
|
||||
|
||||
totalMilliliterAlcohol += (container.inventory * (container.volume * (container.drink.abv / 100)));
|
||||
|
||||
if(container.drink.abv > 0){
|
||||
averageAlcoholPercentage += container.drink.abv;
|
||||
divider += 1;
|
||||
}
|
||||
});
|
||||
|
||||
const totalLiterAlcohol = totalMilliliterAlcohol / 1000;
|
||||
averageAlcoholPercentage /= divider;
|
||||
|
||||
return {drinksInventory, beersInventory, wineInventory, sodaInventory, cocktailInventory, totalLiterAlcohol, averageAlcoholPercentage};
|
||||
}
|
||||
|
||||
export const loader = async ({request} : LoaderFunctionArgs) => {
|
||||
const dbe = enhance(db);
|
||||
|
||||
// Drink statistics
|
||||
const containers = await dbe.container.findMany({where: {inventory: {gt: 0}}, include: {drink: true}});
|
||||
const total = statsForContainers(containers);
|
||||
|
||||
// Checkout statistics
|
||||
let lastDayDate = Date.now() - (24 * 60 * 60 * 1000);
|
||||
let lastDay = new Date(lastDayDate).toISOString();
|
||||
|
||||
const historyLast24H = await dbe.history.findMany({where: {checkoutAt: {gte: lastDay}}, include: {container: {include: {drink: true}}}});
|
||||
|
||||
let containersLast24 : ContainerWithDrink[] = [];
|
||||
historyLast24H.forEach((entry) => {
|
||||
entry.container.inventory = 1;
|
||||
containersLast24.push(entry.container);
|
||||
});
|
||||
|
||||
const statsLast24H = statsForContainers(containersLast24);
|
||||
|
||||
return json({total, statsLast24H});
|
||||
};
|
||||
|
||||
export default function StatsRoute() {
|
||||
const loadData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row className="mt-3">
|
||||
<Col key="stats-total" className="mb-3">
|
||||
<h2>Total inventory</h2>
|
||||
Total drinks in inventory: {loadData.total.drinksInventory} <br />
|
||||
Total beers in inventory: {loadData.total.beersInventory} <br />
|
||||
Total wines in inventory: {loadData.total.wineInventory} <br />
|
||||
Total sodas in inventory: {loadData.total.sodaInventory} <br />
|
||||
Total cocktails in inventory: {loadData.total.cocktailInventory} <br /><br />
|
||||
|
||||
Total alcohol in inventory: {loadData.total.totalLiterAlcohol} Liter <br />
|
||||
Average alcohol percentage: {loadData.total.averageAlcoholPercentage}% <br />
|
||||
</Col>
|
||||
<Col key="stats-24h" className="mb-3">
|
||||
<h2>Last 24H checkouts</h2>
|
||||
Drinks checked out: {loadData.statsLast24H.drinksInventory} <br />
|
||||
Beer checked out: {loadData.statsLast24H.beersInventory} <br />
|
||||
Wines checked out: {loadData.statsLast24H.wineInventory} <br />
|
||||
Sodas checked out: {loadData.statsLast24H.sodaInventory} <br />
|
||||
Cocktails checked out: {loadData.statsLast24H.cocktailInventory} <br /><br />
|
||||
|
||||
Alcohol checked out: {loadData.statsLast24H.totalLiterAlcohol} Liter <br />
|
||||
Average alcohol percentage: {loadData.statsLast24H.averageAlcoholPercentage}% <br />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function loader({
|
||||
return redirect("/");
|
||||
}
|
||||
|
||||
const history = await dbe.history.findMany({select: {checkoutAt: true ,container: {select: {drink: true}}}, take: 10, orderBy: {checkoutAt: "desc"}});
|
||||
const history = await dbe.history.findMany({select: {checkoutAt: true, inventoryAfter: true, container: {select: {drink: true}}}, take: 10, orderBy: {checkoutAt: "desc"}});
|
||||
return json({history});
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ export async function action({
|
||||
return json({error: "No inventory!"});
|
||||
}
|
||||
|
||||
// Log entry
|
||||
await dbe.history.create({data: {container: {connect: {id: container.id}}}});
|
||||
|
||||
// Update inventory
|
||||
const newInventory = Math.max(container.inventory - 1, 0);
|
||||
await dbe.container.update({data: {inventory: newInventory}, where: {id: container.id}});
|
||||
|
||||
// Log entry
|
||||
await dbe.history.create({data: {container: {connect: {id: container.id}}, inventoryAfter: newInventory}});
|
||||
|
||||
return json({error: ""});
|
||||
};
|
||||
|
||||
@@ -88,6 +88,7 @@ export default function ScanRoute() {
|
||||
<tr>
|
||||
<th>Beer</th>
|
||||
<th>When</th>
|
||||
<th>Amount left</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -95,6 +96,7 @@ export default function ScanRoute() {
|
||||
<tr key={entry.container.drink.slug}>
|
||||
<td>{entry.container.drink.name}</td>
|
||||
<td>{timeAgo(new Date(entry.checkoutAt))}</td>
|
||||
<td>{entry.inventoryAfter}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@@ -126,7 +126,7 @@ model Cocktail {
|
||||
|
||||
/// @@allow('read', true)
|
||||
/// @@allow('update', auth() != null)
|
||||
/// @@allow('create,delete', auth().type == Admin)
|
||||
/// @@allow('all', auth().type == Admin)
|
||||
model Container {
|
||||
id Int @id() @default(autoincrement())
|
||||
barcode String? @unique()
|
||||
@@ -174,10 +174,11 @@ model Country {
|
||||
/// @@allow('create', auth().type == Scanner)
|
||||
/// @@allow('all', auth().type == Admin)
|
||||
model History {
|
||||
id Int @id() @default(autoincrement())
|
||||
container_id Int
|
||||
container Container @relation(fields: [container_id], references: [id])
|
||||
checkoutAt DateTime @default(now())
|
||||
id Int @id() @default(autoincrement())
|
||||
container_id Int
|
||||
container Container @relation(fields: [container_id], references: [id])
|
||||
checkoutAt DateTime @default(now())
|
||||
inventoryAfter Int
|
||||
}
|
||||
|
||||
/// @@allow('all', auth() == this)
|
||||
|
||||
+18
-32
@@ -6,8 +6,8 @@ const pr = new PrismaClient();
|
||||
|
||||
async function seed() {
|
||||
|
||||
await pr.user.upsert({ create: {username: "kenneth", password: "asdf1239", type: UserType.Admin}, update: {}, where: {username: "kenneth"} });
|
||||
await pr.user.upsert({ create: {username: "scanner", password: "asdf1239", type: UserType.Scanner}, update: {}, where: {username: "scanner"} });
|
||||
await pr.user.upsert({ create: {username: "kenneth", password: "asdf1239", type: UserType.Admin}, update: {password: "asdf1239"}, where: {username: "kenneth"} });
|
||||
await pr.user.upsert({ create: {username: "scanner", password: "asdf1239", type: UserType.Scanner}, update: {password: "asdf1239"}, where: {username: "scanner"} });
|
||||
const user = await pr.user.findUnique({where: {username: "kenneth"}}) || undefined;
|
||||
|
||||
// Delete all sessions
|
||||
@@ -518,52 +518,52 @@ function getBeerStyles() : BeerStyle[] {
|
||||
{
|
||||
id: 10,
|
||||
name: "Tarwe",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "4a1615"
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "Radler",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "fde8a1"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "Pilsner",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "fad96f"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: "Fruit",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "d81946"
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: "Cider",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "e7c899"
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: "Wit",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "f8c059"
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "IPA",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "d36429"
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: "Bock",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "983b22"
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: "Dark Ale",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "4a1615"
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: "Ale",
|
||||
color: "NA" //nog opzoeken
|
||||
color: "f5ad3a"
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -577,7 +577,7 @@ function getBeers() : BeerQuery[] {
|
||||
ibu: 0,
|
||||
|
||||
manufacturer: {connect: { id: 16}},
|
||||
style: {connect: { id: 1}},
|
||||
style: {connect: { id: 12}},
|
||||
|
||||
description: "Kordaat Premium Pilsener heeft een toegankelijke, frisse smaak met een aangename bittere afdronk.",
|
||||
image: "/beers/kordaat_0.jpeg",
|
||||
@@ -1240,7 +1240,7 @@ function getBeers() : BeerQuery[] {
|
||||
ibu: 22,
|
||||
|
||||
manufacturer: {connect: { id: 2}},
|
||||
style: {connect: { id: 1}},
|
||||
style: {connect: { id: 12}},
|
||||
|
||||
description: "Well balanced in taste, refreshing with a light bitterness. A 0.0 for every beerlover with the same characteristics as our Hertog Jan Pilsener.",
|
||||
image: "/beers/hertog_jan_0.jpeg",
|
||||
@@ -1698,20 +1698,6 @@ function getSodas() : SodaQuery[] {
|
||||
|
||||
function getCocktails() : CocktailQuery[] {
|
||||
return [
|
||||
{
|
||||
name: "Some coktail mix",
|
||||
slug: "cocktail_mix",
|
||||
image: null,
|
||||
description: "Lekker lekker cocktail",
|
||||
abv: 3,
|
||||
|
||||
manufacturer: {connect: {id: 15}},
|
||||
mix: true,
|
||||
|
||||
gluten: true,
|
||||
lactose: false,
|
||||
organic: false
|
||||
},
|
||||
{
|
||||
name: "Prosecco Rosato",
|
||||
slug: "prosecco_rosato",
|
||||
@@ -1720,7 +1706,7 @@ function getCocktails() : CocktailQuery[] {
|
||||
abv: 6.9,
|
||||
|
||||
manufacturer: {connect: {id: 16}},
|
||||
mix: false,
|
||||
mix: true,
|
||||
|
||||
gluten: false,
|
||||
lactose: false,
|
||||
@@ -1734,7 +1720,7 @@ function getCocktails() : CocktailQuery[] {
|
||||
abv: 6.9,
|
||||
|
||||
manufacturer: {connect: {id: 16}},
|
||||
mix: false,
|
||||
mix: true,
|
||||
|
||||
gluten: false,
|
||||
lactose: false,
|
||||
@@ -1748,7 +1734,7 @@ function getCocktails() : CocktailQuery[] {
|
||||
abv: 6.9,
|
||||
|
||||
manufacturer: {connect: {id: 16}},
|
||||
mix: false,
|
||||
mix: true,
|
||||
|
||||
gluten: false,
|
||||
lactose: false,
|
||||
@@ -1762,7 +1748,7 @@ function getCocktails() : CocktailQuery[] {
|
||||
abv: 6.9,
|
||||
|
||||
manufacturer: {connect: {id: 16}},
|
||||
mix: false,
|
||||
mix: true,
|
||||
|
||||
gluten: false,
|
||||
lactose: false,
|
||||
|
||||
@@ -188,6 +188,7 @@ model History {
|
||||
container Container @relation(fields: [container_id], references: [id])
|
||||
|
||||
checkoutAt DateTime @default(now())
|
||||
inventoryAfter Int
|
||||
|
||||
@@allow('read', true)
|
||||
@@allow('create', auth().type == Scanner)
|
||||
|
||||
@@ -18,6 +18,9 @@ export default defineConfig({
|
||||
route("beer/:beerslug", "routes/inventory/beer.$beerslug.tsx");
|
||||
route("wine/:wineslug", "routes/inventory/wine.$wineslug.tsx");
|
||||
route("soda/:sodaslug", "routes/inventory/soda.$sodaslug.tsx");
|
||||
|
||||
route("stats", "routes/inventory/stats/route.tsx");
|
||||
|
||||
});
|
||||
|
||||
// Scanner
|
||||
|
||||
Reference in New Issue
Block a user