Complete management interface and small changes
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
|
||||
import { Manufacturer } from '@zenstackhq/runtime/models';
|
||||
import { Col, Container, Image, Row, Table } from 'react-bootstrap';
|
||||
import { ContainerWithSection, DrinkComposite, isDrinkWithContainersAndSection, isDrinkWithManufacturer, isDrinkWithStyle } from '~/models/types';
|
||||
import { Alert, Badge, Col, Container, Image, Row, Table } from 'react-bootstrap';
|
||||
import { ContainerWithSection, DrinkComposite, containerTypeToString, isBeer, isDrinkWithContainersAndSection, isDrinkWithManufacturer, isDrinkWithStyle } from '~/models/types';
|
||||
import { volume } from '~/utils/conversions';
|
||||
|
||||
interface Arguments {
|
||||
drink: DrinkComposite
|
||||
@@ -10,8 +11,10 @@ interface Arguments {
|
||||
function DrinkPage(arg:Arguments) {
|
||||
|
||||
var borderColor = "#bbb";
|
||||
var style = (<></>);
|
||||
if(isDrinkWithStyle(arg.drink)){
|
||||
borderColor = "#" + arg.drink.style.color;
|
||||
style = (<Badge bg="info">{arg.drink.style.name}</Badge>);
|
||||
}
|
||||
|
||||
var manufacturer: Manufacturer | undefined = undefined;
|
||||
@@ -19,40 +22,48 @@ function DrinkPage(arg:Arguments) {
|
||||
manufacturer = arg.drink.manufacturer;
|
||||
}
|
||||
|
||||
var glass = false;
|
||||
var ibu = (<></>);
|
||||
if(isBeer(arg.drink)){
|
||||
glass = arg.drink.glass;
|
||||
if(arg.drink.ibu)
|
||||
ibu = (<Badge bg={arg.drink.ibu > 45 ? "warning" : "secondary"}>IBU: {arg.drink.ibu}</Badge>);
|
||||
}
|
||||
|
||||
var containers : ContainerWithSection[] = [];
|
||||
var totalinventory = 0;
|
||||
var containerWithMultiplePortions = false;
|
||||
if(isDrinkWithContainersAndSection(arg.drink)){
|
||||
containers = arg.drink.containers;
|
||||
containers.map((container) => {
|
||||
totalinventory += container.inventory;
|
||||
|
||||
if((container.portions || 0) > 1){
|
||||
containerWithMultiplePortions = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{glass ? (
|
||||
<Alert variant='info'>
|
||||
There is a glass available for this beer!
|
||||
</Alert>
|
||||
): ""}
|
||||
|
||||
<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" : ""}
|
||||
<Badge bg={arg.drink.abv > 0 ? "warning" : "secondary"}>ABV: {arg.drink.abv}%</Badge> {style} {ibu} {arg.drink.sugar ? "" : <Badge bg="success">Sugar-free</Badge>} {arg.drink.gluten ? "" : <Badge bg="success">Gluten-free</Badge>} {arg.drink.organic ? <Badge bg="success">Organic</Badge> : ""}
|
||||
<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>
|
||||
@@ -69,6 +80,7 @@ function DrinkPage(arg:Arguments) {
|
||||
<tr>
|
||||
<th>Section</th>
|
||||
<th>Container</th>
|
||||
<th>Volume</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -76,13 +88,19 @@ function DrinkPage(arg:Arguments) {
|
||||
{containers.map((container) => (
|
||||
<tr key={container.barcode}>
|
||||
<td>{container.section.name}</td>
|
||||
<td>{container.type}</td>
|
||||
<td>{containerTypeToString(container.type)}</td>
|
||||
<td>{volume(container.volume)}</td>
|
||||
<td>{container.inventory}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
{containerWithMultiplePortions ? (
|
||||
<Alert variant="warning">
|
||||
This drink contains multiple portions! These should be scanned <u>only</u> when the container is empty!
|
||||
</Alert>
|
||||
) : ""}
|
||||
</div>
|
||||
</Col>
|
||||
{ manufacturer ? (
|
||||
|
||||
@@ -90,7 +90,7 @@ function DrinkFilter(arg : Arguments) {
|
||||
</Form.Group>
|
||||
<Form.Group controlId="drink-modifiers" className='mb-2'>
|
||||
<Form.Label>Drink</Form.Label>
|
||||
{ ["Gluten-free", "Organic"].map(key => (
|
||||
{ ["Sugar-free", "Gluten-free", "Organic"].map(key => (
|
||||
<Form.Check
|
||||
id={"drinkmod-" + key}
|
||||
type="checkbox"
|
||||
|
||||
@@ -12,6 +12,9 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
|
||||
let whereBase = {delegate_aux_drink: {AND: <any>[]}};
|
||||
|
||||
if(searchParams.has("sugar-free")){
|
||||
whereBase.delegate_aux_drink.AND.push({NOT: {sugar: true}});
|
||||
}
|
||||
if(searchParams.has("gluten-free")){
|
||||
whereBase.delegate_aux_drink.AND.push({NOT: {gluten: true}});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import { DrinkType, Manufacturer } from "@prisma/client";
|
||||
|
||||
export async function findIdFromSlug(slug:string | undefined) : Promise<number | undefined> {
|
||||
if(slug){
|
||||
const id = Number(slug);
|
||||
if(id) return id;
|
||||
|
||||
const drink = await db.drink.findUnique({where: {slug: slug}, select: {id: true}});
|
||||
return drink?.id;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export type DrinkWithManufacturer = Drink & { manufacturer: Manufacturer}
|
||||
export type BeerWithStyle = Beer & { style : BeerStyle }
|
||||
export type WineWithStyle = Wine & { style : WineStyle }
|
||||
|
||||
export type DrinkComposite = Drink | BeerWithStyle | WineWithStyle | Soda | Cocktail | DrinkWithManufacturer | DrinkWithContainers
|
||||
export type DrinkComposite = Drink | Beer | Wine | BeerWithStyle | WineWithStyle | Soda | Cocktail | DrinkWithManufacturer | DrinkWithContainers
|
||||
|
||||
export function isDrinkWithContainers(drink:DrinkComposite) : drink is DrinkWithContainers{
|
||||
return ((drink as { containers: Container[]}).containers != undefined) || ((drink as { containers: ContainerWithSection[]}).containers != undefined);
|
||||
@@ -43,10 +43,15 @@ export function isDrinkWithStyle(drink:DrinkComposite) : drink is WineWithStyle
|
||||
return beerstyle || winestyle;
|
||||
}
|
||||
|
||||
export function isBeer(drink:DrinkComposite) : drink is Beer {
|
||||
const beer = (drink as Beer).ibu != undefined;
|
||||
return beer;
|
||||
}
|
||||
|
||||
export function containerTypeToString(type: ContainerType){
|
||||
switch(type){
|
||||
case "BeerBottle":
|
||||
return "Beer";
|
||||
return "Beer bottle";
|
||||
case "Can":
|
||||
return "Can";
|
||||
case "Carton":
|
||||
@@ -54,6 +59,6 @@ export function containerTypeToString(type: ContainerType){
|
||||
case "PlasticBottle":
|
||||
return "PET";
|
||||
case "WineBottle":
|
||||
return "Wine";
|
||||
return "Wine bottle";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, InputGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
|
||||
const slug = String(form.get("slug"));
|
||||
const manufacturer = Number(form.get("manufacturer"));
|
||||
const style = Number(form.get("style"));
|
||||
|
||||
const name = String(form.get("name"));
|
||||
const description = String(form.get("description"));
|
||||
var link : string | null = String(form.get("link")) || null;
|
||||
if(link == "") link = null;
|
||||
|
||||
const abv = Number(form.get("abv"));
|
||||
const ibu = Number(form.get("ibu"));
|
||||
|
||||
const glass = form.has("glass") ? true : false;
|
||||
|
||||
const sugar = form.has("sugar") ? true : false;
|
||||
const gluten = form.has("gluten") ? true : false;
|
||||
const lactose = form.has("lactose") ? true : false;
|
||||
const organic = form.has("organic") ? true : false;
|
||||
|
||||
try{
|
||||
await dbe.beer.update({ data: {
|
||||
slug: slug,
|
||||
name: name,
|
||||
description: description,
|
||||
link: link,
|
||||
abv: abv,
|
||||
ibu: ibu,
|
||||
glass: glass,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
manufacturer: {connect: {id: manufacturer}},
|
||||
style: {connect: {id: style}}
|
||||
}, where: {id: id}
|
||||
});
|
||||
return redirect("/inventory/beer/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const styles = await dbe.beerStyle.findMany({orderBy: {name: "asc"}});
|
||||
const manufacturers = await dbe.manufacturer.findMany({orderBy: {name: "asc"}});
|
||||
|
||||
const beer = await dbe.beer.findUnique({where: {id: id}});
|
||||
|
||||
if(!beer) return redirect("/admin/edit/beer");
|
||||
|
||||
return json({styles, manufacturers, beer});
|
||||
}
|
||||
|
||||
export default function EditBeerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit beer ({loaderData.beer.id})</h1>
|
||||
<Form method="POST">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.beer.id}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" name="slug" pattern="[a-z0-9_]+" defaultValue={loaderData.beer.slug} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.beer.name} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="description" defaultValue={loaderData.beer.description}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Style</Form.Label>
|
||||
<Form.Select name="style" required>
|
||||
{loaderData.styles.map((style) => (
|
||||
<option value={style.id} selected={loaderData.beer.style_id == style.id}>
|
||||
{style.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Manufacturer</Form.Label>
|
||||
<Form.Select name="manufacturer" required>
|
||||
{loaderData.manufacturers.map((manu) => (
|
||||
<option value={manu.id} selected={loaderData.beer.manufacturer_id == manu.id}>
|
||||
{manu.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ABV</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<Form.Control type="number" min={0} max={100} step={0.1} name="abv" defaultValue={loaderData.beer.abv} required/>
|
||||
<InputGroup.Text>%</InputGroup.Text>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>IBU</Form.Label>
|
||||
<Form.Control type="number" min={0} max={200} step={1} name="ibu" defaultValue={loaderData.beer.ibu ?? 0} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Glass</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Has custom glass"
|
||||
name="glass"
|
||||
value={1}
|
||||
defaultChecked={loaderData.beer.glass}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
defaultChecked={loaderData.beer.sugar}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
name="gluten"
|
||||
value={1}
|
||||
defaultChecked={loaderData.beer.gluten}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Lactose"
|
||||
name="lactose"
|
||||
value={1}
|
||||
defaultChecked={loaderData.beer.lactose}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Organic"
|
||||
name="organic"
|
||||
value={1}
|
||||
defaultChecked={loaderData.beer.organic}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Link</Form.Label>
|
||||
<Form.Control type="text" name="link" defaultValue={loaderData.beer.link ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
//const beers = await dbe.beer.findMany({select: {id: true, name: true, _count: {select: {containers: true}}},orderBy: {name: "asc"}});
|
||||
const beers = await dbe.beer.findMany({select: {id: true, slug: true, name: true, containers: true},orderBy: {name: "asc"}});
|
||||
return json({beers});
|
||||
}
|
||||
|
||||
export default function EditBeerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit beer</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.beers.map((beer) => (
|
||||
<ListGroup.Item key={beer.id}>
|
||||
{beer.name} <Badge bg="secondary">{beer.containers.length} Containers</Badge>
|
||||
<div className="float-end">
|
||||
{ beer.containers.length <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/beer/" + beer.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"/admin/edit/image/beer/" + beer.id}>
|
||||
<Button variant="secondary" className="mx-3">
|
||||
Image
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"" + beer.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"/inventory/beer/" + beer.slug}>
|
||||
<Button variant="success" className="mx-3">
|
||||
View
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, Form, ListGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
const name = String(form.get("name"));
|
||||
const color = String(form.get("color")).replaceAll("#", "");
|
||||
|
||||
try{
|
||||
await dbe.beerStyle.update({ data: {name: name, color: color}, where: {id: id} });
|
||||
return redirect("/admin/edit/beerstyle");
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const style = await dbe.beerStyle.findUnique({where: {id: id}});
|
||||
|
||||
if(!style) return redirect("/admin/edit/beerstyle");
|
||||
|
||||
return json({style});
|
||||
}
|
||||
|
||||
export default function EditBeerStyleRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Beer style ({loaderData.style.id})</h1>
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.style.id} />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Style</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.style.name}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Color</Form.Label>
|
||||
<Form.Control type="color" name="color" defaultValue={"#" + loaderData.style.color} />
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { countBy } from "lodash";
|
||||
import { Container, Row, Col, Button, Form, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const beerstyles = await dbe.beerStyle.findMany({select: {id: true, name: true, color: true, _count: {select: {beers: true}}},orderBy: {name: "asc"}});
|
||||
return json({beerstyles});
|
||||
}
|
||||
|
||||
export default function EditBeerStyleRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit Beerstyle</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.beerstyles.map((style) => (
|
||||
<ListGroup.Item key={style.id}>
|
||||
<span style={{color:"#"+style.color}}>{style.name}</span> <Badge bg="secondary">{style._count.beers} Beers</Badge>
|
||||
<div className="float-end">
|
||||
{ style._count.beers <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/beerstyle/" + style.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"" + style.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, InputGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
|
||||
const slug = String(form.get("slug"));
|
||||
const manufacturer = Number(form.get("manufacturer"));
|
||||
|
||||
const name = String(form.get("name"));
|
||||
const description = String(form.get("description"));
|
||||
var link : string | null = String(form.get("link")) || null;
|
||||
if(link == "") link = null;
|
||||
|
||||
const abv = Number(form.get("abv"));
|
||||
|
||||
const mix = form.has("mix") ? true : false;
|
||||
|
||||
const sugar = form.has("sugar") ? true : false;
|
||||
const gluten = form.has("gluten") ? true : false;
|
||||
const lactose = form.has("lactose") ? true : false;
|
||||
const organic = form.has("organic") ? true : false;
|
||||
|
||||
try{
|
||||
await dbe.cocktail.update({ data: {
|
||||
slug: slug,
|
||||
name: name,
|
||||
description: description,
|
||||
link: link,
|
||||
abv: abv,
|
||||
mix: mix,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
manufacturer: {connect: {id: manufacturer}}
|
||||
}, where: {id: id}
|
||||
});
|
||||
return redirect("/inventory/cocktail/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const manufacturers = await dbe.manufacturer.findMany({orderBy: {name: "asc"}});
|
||||
|
||||
const cocktail = await dbe.cocktail.findUnique({where: {id: id}});
|
||||
|
||||
if(!cocktail) return redirect("/admin/edit/cocktail");
|
||||
|
||||
return json({manufacturers, cocktail});
|
||||
}
|
||||
|
||||
export default function EditCocktailRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit cocktail ({loaderData.cocktail.id})</h1>
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.cocktail.id}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" name="slug" pattern="[a-z0-9_]+" defaultValue={loaderData.cocktail.slug} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.cocktail.name} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="description" defaultValue={loaderData.cocktail.description}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Manufacturer</Form.Label>
|
||||
<Form.Select name="manufacturer" required>
|
||||
{loaderData.manufacturers.map((manu) => (
|
||||
<option value={manu.id} selected={loaderData.cocktail.manufacturer_id == manu.id}>
|
||||
{manu.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ABV</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<Form.Control type="number" min={0} max={100} step={0.1} name="abv" defaultValue={loaderData.cocktail.abv} required/>
|
||||
<InputGroup.Text>%</InputGroup.Text>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Cocktail specific</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Mix"
|
||||
name="mix"
|
||||
value={1}
|
||||
defaultChecked={loaderData.cocktail.mix}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
defaultChecked={loaderData.cocktail.sugar}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
name="gluten"
|
||||
value={1}
|
||||
defaultChecked={loaderData.cocktail.gluten}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Lactose"
|
||||
name="lactose"
|
||||
value={1}
|
||||
defaultChecked={loaderData.cocktail.lactose}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Organic"
|
||||
name="organic"
|
||||
value={1}
|
||||
defaultChecked={loaderData.cocktail.organic}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Link</Form.Label>
|
||||
<Form.Control type="text" name="link" defaultValue={loaderData.cocktail.link ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
//const cocktails = await dbe.cocktail.findMany({select: {id: true, name: true, _count: {select: {containers: true}}},orderBy: {name: "asc"}});
|
||||
const cocktails = await dbe.cocktail.findMany({select: {id: true, slug: true, name: true, containers: true},orderBy: {name: "asc"}});
|
||||
return json({cocktails});
|
||||
}
|
||||
|
||||
export default function EditCocktailRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit cocktail</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.cocktails.map((cocktail) => (
|
||||
<ListGroup.Item key={cocktail.id}>
|
||||
{cocktail.name} <Badge bg="secondary">{cocktail.containers.length} Containers</Badge>
|
||||
<div className="float-end">
|
||||
{ cocktail.containers.length <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/cocktail/" + cocktail.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"/admin/edit/image/cocktail/" + cocktail.id}>
|
||||
<Button variant="secondary" className="mx-3">
|
||||
Image
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"" + cocktail.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"/inventory/cocktail/" + cocktail.slug}>
|
||||
<Button variant="success" className="mx-3">
|
||||
View
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Col, ListGroup, InputGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
import { ContainerType } from "@prisma/client";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
|
||||
var barcode : string | null = String(form.get("barcode")) || null;
|
||||
if(barcode == "") barcode = null;
|
||||
|
||||
//const drink = Number(form.get("drink"));
|
||||
const section = Number(form.get("section"));
|
||||
|
||||
const type = form.get("type") as ContainerType;
|
||||
const volume = Number(form.get("volume"));
|
||||
const portions = Number(form.get("portions"));
|
||||
const price = Number(form.get("price"));
|
||||
const inventory = Number(form.get("inventory"));
|
||||
|
||||
try{
|
||||
const updatedContainer = await dbe.container.update({ data: {
|
||||
barcode: barcode,
|
||||
type: type,
|
||||
volume: volume,
|
||||
portions: portions,
|
||||
price: price,
|
||||
inventory: inventory,
|
||||
//drink: {connect: {id: drink}},
|
||||
section: {connect: {id: section}}
|
||||
}, select: {drink: {select: {slug: true, type: true}}},
|
||||
where: {id: id}
|
||||
});
|
||||
return redirect("/inventory/" + updatedContainer.drink.type.toLowerCase() + "/" + updatedContainer.drink.slug);
|
||||
}
|
||||
catch(e){
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const drinks = await dbe.drink.findMany({orderBy: {name: "asc"}});
|
||||
const sections = await dbe.section.findMany({orderBy: {name: "asc"}});
|
||||
|
||||
const container = await dbe.container.findUnique({where: {id: id}});
|
||||
|
||||
if(!container) return redirect("/admin/edit/container");
|
||||
|
||||
return json({drinks, sections, container});
|
||||
}
|
||||
|
||||
export default function EditContainerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Container ({loaderData.container.id})</h1>
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.container.id}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Barcode</Form.Label>
|
||||
<Form.Control type="text" name="barcode" value={loaderData.container.barcode ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Drink</Form.Label>
|
||||
<Form.Select name="drink" disabled required>
|
||||
{loaderData.drinks.map((drink) => (
|
||||
<option value={drink.id} selected={loaderData.container.drink_id == drink.id}>
|
||||
{drink.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Section</Form.Label>
|
||||
<Form.Select name="section" required>
|
||||
{loaderData.sections.map((section) => (
|
||||
<option value={section.id} selected={loaderData.container.section_id == section.id}>
|
||||
{section.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Container type</Form.Label>
|
||||
<Form.Select name="type" required>
|
||||
<option value={ContainerType.BeerBottle} selected={loaderData.container.type == ContainerType.BeerBottle}>Beer bottle</option>
|
||||
<option value={ContainerType.Can} selected={loaderData.container.type == ContainerType.Can}>Can</option>
|
||||
<option value={ContainerType.Carton} selected={loaderData.container.type == ContainerType.Carton}>Carton</option>
|
||||
<option value={ContainerType.PlasticBottle} selected={loaderData.container.type == ContainerType.PlasticBottle}>Plastic Bottle</option>
|
||||
<option value={ContainerType.WineBottle} selected={loaderData.container.type == ContainerType.WineBottle}>Wine bottle</option>
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Volume</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<Form.Control type="number" min={0} max={2500} step={1} name="volume" defaultValue={loaderData.container.volume} required/>
|
||||
<InputGroup.Text>ml</InputGroup.Text>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Portions</Form.Label>
|
||||
<Form.Control type="number" min={1} max={10} step={1} defaultValue={loaderData.container.portions ?? 1} name="portions" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Price</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<InputGroup.Text>€</InputGroup.Text>
|
||||
<Form.Control type="number" min={0} max={25} step={0.1} name="price" defaultValue={loaderData.container.price} required/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Inventory</Form.Label>
|
||||
<Form.Control type="number" min={0} step={1} defaultValue={loaderData.container.inventory} name="inventory" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { countBy } from "lodash";
|
||||
import { Container, Row, Col, Button, Form, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import { containerTypeToString } from "~/models/types";
|
||||
import { volume } from "~/utils/conversions";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const containers = await dbe.container.findMany({select: {id: true, drink: true, barcode: true, type: true, volume: true, inventory: true},orderBy: {drink: {name: "asc"}}});
|
||||
return json({containers});
|
||||
}
|
||||
|
||||
export default function EditContainerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit container</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.containers.map((container) => (
|
||||
<ListGroup.Item key={container.id}>
|
||||
{container.drink.name} - {containerTypeToString(container.type)} {volume(container.volume)} <Badge bg="secondary">{container.inventory} inventory</Badge>
|
||||
<div className="float-end">
|
||||
{ container.inventory <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/container/" + container.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"" + container.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, Form, ListGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const code = params.code;
|
||||
|
||||
const form = await request.formData();
|
||||
const name = String(form.get("name"));
|
||||
|
||||
try{
|
||||
await dbe.country.update({ data: {name: name}, where: {code: code}});
|
||||
return redirect("/admin/edit/country");
|
||||
}
|
||||
catch(e){
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const code = params.code;
|
||||
|
||||
const country = await dbe.country.findUnique({where: {code: code}});
|
||||
|
||||
if(!country) return redirect("/admin/edit/country");
|
||||
|
||||
return json({country});
|
||||
}
|
||||
|
||||
export default function EditCountryRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit country ({loaderData.country.code})</h1>
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Code</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.country.code} />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Country</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.country.name}/>
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { countBy } from "lodash";
|
||||
import { Container, Row, Col, Button, Form, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const countries = await dbe.country.findMany({select: {name: true, code: true, _count: {select: {manufacturers: true}}},orderBy: {name: "asc"}});
|
||||
return json({countries});
|
||||
}
|
||||
|
||||
export default function EditCountryRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit country</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.countries.map((country) => (
|
||||
<ListGroup.Item key={country.code}>
|
||||
{country.name} ({country.code}) <Badge bg="secondary">{country._count.manufacturers} Manufactureres</Badge>
|
||||
<div className="float-end">
|
||||
{ country._count.manufacturers <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/country/" + country.code}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={country.code}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, NodeOnDiskFile, json, redirect, unstable_composeUploadHandlers, unstable_createFileUploadHandler, unstable_createMemoryUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
|
||||
import { renameSync, rmSync } from "node:fs";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Image } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const uploadHandler = unstable_composeUploadHandlers(
|
||||
unstable_createFileUploadHandler({
|
||||
file: ({ filename }) => filename.toLowerCase().replaceAll(" ", "_")
|
||||
}),
|
||||
// parse everything else into memory
|
||||
unstable_createMemoryUploadHandler()
|
||||
);
|
||||
const parsedForm = await unstable_parseMultipartFormData(
|
||||
request,
|
||||
uploadHandler
|
||||
);
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const beer = await dbe.beer.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!beer) return redirect("/admin/edit/beer");
|
||||
|
||||
const newFilename = "/beers/" + beer.name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Remove old image
|
||||
if(beer.image) rmSync("public"+ beer.image, {force: true});
|
||||
|
||||
// Move the file to the beers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
await dbe.beer.update({data: {image: newFilename}, where: {id: id}});
|
||||
|
||||
return redirect("/inventory/beer/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
rmSync(image.getFilePath(), {force: true});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const beer = await dbe.beer.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!beer) return redirect("/admin/edit/beer");
|
||||
|
||||
return json({beer});
|
||||
}
|
||||
|
||||
export default function EditBeerImageRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit beer image ({loaderData.beer.name})</h1>
|
||||
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<h3>Current image</h3>
|
||||
{ loaderData.beer.image ? (
|
||||
<>
|
||||
<p>{loaderData.beer.image}</p>
|
||||
<Image src={loaderData.beer.image}></Image>
|
||||
</>
|
||||
) : "No current image" }
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, NodeOnDiskFile, json, redirect, unstable_composeUploadHandlers, unstable_createFileUploadHandler, unstable_createMemoryUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
|
||||
import { renameSync, rmSync } from "node:fs";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Image } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const uploadHandler = unstable_composeUploadHandlers(
|
||||
unstable_createFileUploadHandler({
|
||||
file: ({ filename }) => filename.toLowerCase().replaceAll(" ", "_")
|
||||
}),
|
||||
// parse everything else into memory
|
||||
unstable_createMemoryUploadHandler()
|
||||
);
|
||||
const parsedForm = await unstable_parseMultipartFormData(
|
||||
request,
|
||||
uploadHandler
|
||||
);
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const cocktail = await dbe.cocktail.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!cocktail) return redirect("/admin/edit/cocktail");
|
||||
|
||||
const newFilename = "/cocktails/" + cocktail.name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Remove old image
|
||||
if(cocktail.image) rmSync("public"+ cocktail.image, {force: true});
|
||||
|
||||
// Move the file to the cocktails folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
await dbe.cocktail.update({data: {image: newFilename}, where: {id: id}});
|
||||
|
||||
return redirect("/inventory/cocktail/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
rmSync(image.getFilePath(), {force: true});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const cocktail = await dbe.cocktail.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!cocktail) return redirect("/admin/edit/cocktail");
|
||||
|
||||
return json({cocktail});
|
||||
}
|
||||
|
||||
export default function EditCocktailImageRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit cocktail image ({loaderData.cocktail.name})</h1>
|
||||
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<h3>Current image</h3>
|
||||
{ loaderData.cocktail.image ? (
|
||||
<>
|
||||
<p>{loaderData.cocktail.image}</p>
|
||||
<Image src={loaderData.cocktail.image}></Image>
|
||||
</>
|
||||
) : "No current image" }
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, NodeOnDiskFile, json, redirect, unstable_composeUploadHandlers, unstable_createFileUploadHandler, unstable_createMemoryUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
|
||||
import { renameSync, rmSync } from "node:fs";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Image } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const uploadHandler = unstable_composeUploadHandlers(
|
||||
unstable_createFileUploadHandler({
|
||||
file: ({ filename }) => filename.toLowerCase().replaceAll(" ", "_")
|
||||
}),
|
||||
// parse everything else into memory
|
||||
unstable_createMemoryUploadHandler()
|
||||
);
|
||||
const parsedForm = await unstable_parseMultipartFormData(
|
||||
request,
|
||||
uploadHandler
|
||||
);
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const manufacturer = await dbe.manufacturer.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!manufacturer) return redirect("/admin/edit/manufacturer");
|
||||
|
||||
const newFilename = "/manufacturers/" + manufacturer.name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Remove old image
|
||||
if(manufacturer.image) rmSync("public"+ manufacturer.image, {force: true});
|
||||
|
||||
// Move the file to the manufacturers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
await dbe.manufacturer.update({data: {image: newFilename}, where: {id: id}});
|
||||
|
||||
return redirect("/inventory/manufacturer/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
rmSync(image.getFilePath(), {force: true});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const manufacturer = await dbe.manufacturer.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!manufacturer) return redirect("/admin/edit/manufacturer");
|
||||
|
||||
return json({manufacturer});
|
||||
}
|
||||
|
||||
export default function EditManufacturerImageRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Manufacturer image ({loaderData.manufacturer.name})</h1>
|
||||
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<h3>Current image</h3>
|
||||
{ loaderData.manufacturer.image ? (
|
||||
<>
|
||||
<p>{loaderData.manufacturer.image}</p>
|
||||
<Image src={loaderData.manufacturer.image}></Image>
|
||||
</>
|
||||
) : "No current image" }
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, NodeOnDiskFile, json, redirect, unstable_composeUploadHandlers, unstable_createFileUploadHandler, unstable_createMemoryUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
|
||||
import { renameSync, rmSync } from "node:fs";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Image } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const uploadHandler = unstable_composeUploadHandlers(
|
||||
unstable_createFileUploadHandler({
|
||||
file: ({ filename }) => filename.toLowerCase().replaceAll(" ", "_")
|
||||
}),
|
||||
// parse everything else into memory
|
||||
unstable_createMemoryUploadHandler()
|
||||
);
|
||||
const parsedForm = await unstable_parseMultipartFormData(
|
||||
request,
|
||||
uploadHandler
|
||||
);
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const soda = await dbe.soda.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!soda) return redirect("/admin/edit/soda");
|
||||
|
||||
const newFilename = "/soda/" + soda.name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Remove old image
|
||||
if(soda.image) rmSync("public"+ soda.image, {force: true});
|
||||
|
||||
// Move the file to the sodas folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
await dbe.soda.update({data: {image: newFilename}, where: {id: id}});
|
||||
|
||||
return redirect("/inventory/soda/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
rmSync(image.getFilePath(), {force: true});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const soda = await dbe.soda.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!soda) return redirect("/admin/edit/soda");
|
||||
|
||||
return json({soda});
|
||||
}
|
||||
|
||||
export default function EditSodaImageRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit soda image ({loaderData.soda.name})</h1>
|
||||
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<h3>Current image</h3>
|
||||
{ loaderData.soda.image ? (
|
||||
<>
|
||||
<p>{loaderData.soda.image}</p>
|
||||
<Image src={loaderData.soda.image}></Image>
|
||||
</>
|
||||
) : "No current image" }
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, NodeOnDiskFile, json, redirect, unstable_composeUploadHandlers, unstable_createFileUploadHandler, unstable_createMemoryUploadHandler, unstable_parseMultipartFormData } from "@remix-run/node";
|
||||
import { renameSync, rmSync } from "node:fs";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, Image } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const uploadHandler = unstable_composeUploadHandlers(
|
||||
unstable_createFileUploadHandler({
|
||||
file: ({ filename }) => filename.toLowerCase().replaceAll(" ", "_")
|
||||
}),
|
||||
// parse everything else into memory
|
||||
unstable_createMemoryUploadHandler()
|
||||
);
|
||||
const parsedForm = await unstable_parseMultipartFormData(
|
||||
request,
|
||||
uploadHandler
|
||||
);
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const wine = await dbe.wine.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!wine) return redirect("/admin/edit/wine");
|
||||
|
||||
const newFilename = "/wine/" + wine.name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Remove old image
|
||||
if(wine.image) rmSync("public"+ wine.image, {force: true});
|
||||
|
||||
// Move the file to the wines folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
await dbe.wine.update({data: {image: newFilename}, where: {id: id}});
|
||||
|
||||
return redirect("/inventory/wine/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
rmSync(image.getFilePath(), {force: true});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const wine = await dbe.wine.findUnique({select: {image: true, name: true}, where: {id: id}});
|
||||
|
||||
if(!wine) return redirect("/admin/edit/wine");
|
||||
|
||||
return json({wine});
|
||||
}
|
||||
|
||||
export default function EditWineImageRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit wine image ({loaderData.wine.name})</h1>
|
||||
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<h3>Current image</h3>
|
||||
{ loaderData.wine.image ? (
|
||||
<>
|
||||
<p>{loaderData.wine.image}</p>
|
||||
<Image src={loaderData.wine.image}></Image>
|
||||
</>
|
||||
) : "No current image" }
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
const country = String(form.get("country"));
|
||||
const name = String(form.get("name"));
|
||||
const description = String(form.get("description"));
|
||||
|
||||
try{
|
||||
await dbe.manufacturer.update({data: {name: name, description: description, country: {connect: {code: country}}}, where: {id: id} });
|
||||
return redirect("/inventory/manufacturer/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const countries = await dbe.country.findMany({orderBy: {name: "asc"}});
|
||||
const manufacturer = await dbe.manufacturer.findUnique({where: {id: id}});
|
||||
|
||||
if(!manufacturer) return redirect("/admin/edit/manufacturer");
|
||||
|
||||
return json({manufacturer, countries});
|
||||
}
|
||||
|
||||
export default function NewManufacturerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Manufacturer ({loaderData.manufacturer.id})</h1>
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.manufacturer.id} />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Country</Form.Label>
|
||||
<Form.Select name="country" required>
|
||||
{loaderData.countries.map((country) => (
|
||||
<option value={country.code} selected={loaderData.manufacturer.country_id == country.code}>
|
||||
{country.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.manufacturer.name} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="description" defaultValue={loaderData.manufacturer.description ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { countBy } from "lodash";
|
||||
import { Container, Row, Col, Button, Form, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const manufacturers = await dbe.manufacturer.findMany({select: {id: true, name: true, _count: {select: {drinks: true}}},orderBy: {name: "asc"}});
|
||||
return json({manufacturers});
|
||||
}
|
||||
|
||||
export default function EditManufacturerRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit manufacturer</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.manufacturers.map((manufacturer) => (
|
||||
<ListGroup.Item key={manufacturer.id}>
|
||||
{manufacturer.name} <Badge bg="secondary">{manufacturer._count.drinks} Drinks</Badge>
|
||||
<div className="float-end">
|
||||
{ manufacturer._count.drinks <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/manufacturer/" + manufacturer.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"/admin/edit/image/manufacturer/" + manufacturer.id}>
|
||||
<Button variant="secondary" className="mx-3">
|
||||
Image
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"" + manufacturer.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.beer.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/beer");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.beerStyle.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/beerstyle");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.cocktail.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/cocktail");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.container.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/container");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const code = params.code;
|
||||
|
||||
await dbe.country.delete({where: {code: code}});
|
||||
return redirect("/admin/edit/country");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.manufacturer.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/manufacturer");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.section.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/section");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.soda.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/soda");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.wine.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/wine");
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
await dbe.wineStyle.delete({where: {id: id}});
|
||||
return redirect("/admin/edit/winestyle");
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Link } from "@remix-run/react";
|
||||
import { ListGroup } from "react-bootstrap";
|
||||
|
||||
export default function AdminRoute() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Edit or remove</h1>
|
||||
<ListGroup>
|
||||
<ListGroup.Item><Link to="/admin/edit/country">country</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/section">section</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/beerstyle">beer style</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/winestyle">wine style</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/manufacturer">manufacturer</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/beer">beer</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/wine">wine</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/soda">soda</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/cocktail">cocktail</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/container">container</Link></ListGroup.Item>
|
||||
</ListGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, Form, ListGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
const name = String(form.get("name"));
|
||||
|
||||
try{
|
||||
await dbe.section.update({ data: {name: name}, where: {id: id} });
|
||||
return redirect("/admin/edit/section");
|
||||
}
|
||||
catch(e){
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const section = await dbe.section.findUnique({where: {id: id}});
|
||||
|
||||
if(!section) return redirect("/admin/edit/section");
|
||||
|
||||
return json({section});
|
||||
}
|
||||
|
||||
export default function EditSectionRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Section ({loaderData.section.id})</h1>
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.section.id} />
|
||||
</Form.Group>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Section</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.section.name}/>
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { countBy } from "lodash";
|
||||
import { Container, Row, Col, Button, Form, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const sections = await dbe.section.findMany({select: {name: true, id: true, _count: {select: {containers: true}}}, orderBy: {name: "asc"}});
|
||||
return json({sections});
|
||||
}
|
||||
|
||||
export default function EditSectionRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit section</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.sections.map((section) => (
|
||||
<ListGroup.Item key={section.id}>
|
||||
{section.name} <Badge bg="secondary">{section._count.containers} Containers</Badge>
|
||||
<div className="float-end">
|
||||
{ section._count.containers <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/section/" + section.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"" + section.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, InputGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
|
||||
const slug = String(form.get("slug"));
|
||||
const manufacturer = Number(form.get("manufacturer"));
|
||||
|
||||
const name = String(form.get("name"));
|
||||
const description = String(form.get("description"));
|
||||
var link : string | null = String(form.get("link")) || null;
|
||||
if(link == "") link = null;
|
||||
|
||||
const abv = Number(form.get("abv"));
|
||||
|
||||
const carbonated = form.has("carbonated") ? true : false;
|
||||
|
||||
const sugar = form.has("sugar") ? true : false;
|
||||
const gluten = form.has("gluten") ? true : false;
|
||||
const lactose = form.has("lactose") ? true : false;
|
||||
const organic = form.has("organic") ? true : false;
|
||||
|
||||
try{
|
||||
await dbe.soda.update({ data: {
|
||||
slug: slug,
|
||||
name: name,
|
||||
description: description,
|
||||
link: link,
|
||||
abv: abv,
|
||||
carbonated: carbonated,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
manufacturer: {connect: {id: manufacturer}}
|
||||
}, where: { id: id}
|
||||
});
|
||||
return redirect("/inventory/soda/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const manufacturers = await dbe.manufacturer.findMany({orderBy: {name: "asc"}});
|
||||
|
||||
const soda = await dbe.soda.findUnique({where: {id: id}});
|
||||
|
||||
if(!soda) return redirect("/admin/edit/soda");
|
||||
|
||||
return json({manufacturers, soda});
|
||||
}
|
||||
|
||||
export default function EditSodaRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit soda ({loaderData.soda.id})</h1>
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.soda.id}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" name="slug" pattern="[a-z0-9_]+" defaultValue={loaderData.soda.slug} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.soda.name} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="description" defaultValue={loaderData.soda.description}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Manufacturer</Form.Label>
|
||||
<Form.Select name="manufacturer" required>
|
||||
{loaderData.manufacturers.map((manu) => (
|
||||
<option value={manu.id} selected={loaderData.soda.manufacturer_id == manu.id}>
|
||||
{manu.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ABV</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<Form.Control type="number" min={0} max={100} step={0.1} name="abv" defaultValue={loaderData.soda.abv} required/>
|
||||
<InputGroup.Text>%</InputGroup.Text>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Soda specific</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Carbonated"
|
||||
name="carbonated"
|
||||
value={1}
|
||||
defaultChecked={loaderData.soda.carbonated}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
defaultChecked={loaderData.soda.sugar}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
name="gluten"
|
||||
value={1}
|
||||
defaultChecked={loaderData.soda.gluten}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Lactose"
|
||||
name="lactose"
|
||||
value={1}
|
||||
defaultChecked={loaderData.soda.lactose}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Organic"
|
||||
name="organic"
|
||||
value={1}
|
||||
defaultChecked={loaderData.soda.organic}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Link</Form.Label>
|
||||
<Form.Control type="text" name="link" defaultValue={loaderData.soda.link ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
//const sodas = await dbe.soda.findMany({select: {id: true, name: true, _count: {select: {containers: true}}},orderBy: {name: "asc"}});
|
||||
const sodas = await dbe.soda.findMany({select: {id: true, slug: true, name: true, containers: true},orderBy: {name: "asc"}});
|
||||
return json({sodas});
|
||||
}
|
||||
|
||||
export default function EditSodaRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit soda</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.sodas.map((soda) => (
|
||||
<ListGroup.Item key={soda.id}>
|
||||
{soda.name} <Badge bg="secondary">{soda.containers.length} Containers</Badge>
|
||||
<div className="float-end">
|
||||
{ soda.containers.length <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/soda/" + soda.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"/admin/edit/image/soda/" + soda.id}>
|
||||
<Button variant="secondary" className="mx-3">
|
||||
Image
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"" + soda.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"/inventory/soda/" + soda.slug}>
|
||||
<Button variant="success" className="mx-3">
|
||||
View
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Button, Form, InputGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
|
||||
const slug = String(form.get("slug"));
|
||||
const manufacturer = Number(form.get("manufacturer"));
|
||||
const style = Number(form.get("style"));
|
||||
|
||||
const name = String(form.get("name"));
|
||||
const description = String(form.get("description"));
|
||||
var link : string | null = String(form.get("link")) || null;
|
||||
if(link == "") link = null;
|
||||
|
||||
const abv = Number(form.get("abv"));
|
||||
|
||||
var heavy_score : number | null = Number(form.get("heavy_score"));
|
||||
if(heavy_score < 0) heavy_score = null;
|
||||
var tannine_score : number | null = Number(form.get("tannine_score"));
|
||||
if(tannine_score < 0) tannine_score = null;
|
||||
var dry_score : number | null = Number(form.get("dry_score"));
|
||||
if(dry_score < 0) dry_score = null;
|
||||
var fresh_score : number | null = Number(form.get("fresh_score"));
|
||||
if(fresh_score < 0) fresh_score = null;
|
||||
|
||||
var notes : string | null = String(form.get("notes")) || null;
|
||||
if(notes == "") notes = null;
|
||||
|
||||
const sugar = form.has("sugar") ? true : false;
|
||||
const gluten = form.has("gluten") ? true : false;
|
||||
const lactose = form.has("lactose") ? true : false;
|
||||
const organic = form.has("organic") ? true : false;
|
||||
|
||||
|
||||
try{
|
||||
await dbe.wine.update({ data: {
|
||||
slug: slug,
|
||||
name: name,
|
||||
description: description,
|
||||
link: link,
|
||||
abv: abv,
|
||||
heavy_score: heavy_score,
|
||||
tannine_score: tannine_score,
|
||||
dry_score: dry_score,
|
||||
fresh_score: fresh_score,
|
||||
notes: notes,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
manufacturer: {connect: {id: manufacturer}},
|
||||
style: {connect: {id: style}}
|
||||
}, where: {id: id}
|
||||
});
|
||||
return redirect("/inventory/wine/" + id);
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const styles = await dbe.wineStyle.findMany({orderBy: {name: "asc"}});
|
||||
const manufacturers = await dbe.manufacturer.findMany({orderBy: {name: "asc"}});
|
||||
|
||||
const wine = await dbe.wine.findUnique({where: {id: id}});
|
||||
|
||||
if(!wine) return redirect("/admin/edit/wine");
|
||||
|
||||
return json({styles, manufacturers, wine});
|
||||
}
|
||||
|
||||
export default function EditWineRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit wine ({loaderData.wine.id})</h1>
|
||||
<Form method="POST" encType="multipart/form-data">
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.wine.id}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" name="slug" pattern="[a-z0-9_]+" defaultValue={loaderData.wine.slug} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.wine.name} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Description</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="description" defaultValue={loaderData.wine.description}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Style</Form.Label>
|
||||
<Form.Select name="style" required>
|
||||
{loaderData.styles.map((style) => (
|
||||
<option value={style.id} selected={loaderData.wine.style_id == style.id}>
|
||||
{style.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Manufacturer</Form.Label>
|
||||
<Form.Select name="manufacturer" required>
|
||||
{loaderData.manufacturers.map((manu) => (
|
||||
<option value={manu.id} selected={loaderData.wine.manufacturer_id == manu.id}>
|
||||
{manu.name}
|
||||
</option>
|
||||
))}
|
||||
</Form.Select>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ABV</Form.Label>
|
||||
|
||||
<InputGroup>
|
||||
<Form.Control type="number" min={0} max={100} step={0.1} name="abv" defaultValue={loaderData.wine.abv} required/>
|
||||
<InputGroup.Text>%</InputGroup.Text>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Heavy</Form.Label>
|
||||
<Form.Range name="heavy_score" min={-1} max={5} step={1} defaultValue={loaderData.wine.heavy_score ?? -1}/>
|
||||
<Form.Text>Left = undefined, then light (0) - bold (5) </Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Tannine</Form.Label>
|
||||
<Form.Range name="tannine_score" min={-1} max={5} step={1} defaultValue={loaderData.wine.tannine_score ?? -1}/>
|
||||
<Form.Text>Left = undefined, then smooth (0) - tannic (5) </Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Dryness</Form.Label>
|
||||
<Form.Range name="dry_score" min={-1} max={5} step={1} defaultValue={loaderData.wine.dry_score ?? -1}/>
|
||||
<Form.Text>Left = undefined, then dry (0) - sweet (5) </Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Freshness</Form.Label>
|
||||
<Form.Range name="fresh_score" min={-1} max={5} step={1} defaultValue={loaderData.wine.fresh_score ?? -1}/>
|
||||
<Form.Text>Left = undefined, then soft (0) - acidic (5) </Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Notes</Form.Label>
|
||||
<Form.Control as="textarea" type="text" name="notes" defaultValue={loaderData.wine.notes ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
defaultChecked={loaderData.wine.sugar}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
name="gluten"
|
||||
value={1}
|
||||
defaultChecked={loaderData.wine.gluten}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Lactose"
|
||||
name="lactose"
|
||||
value={1}
|
||||
defaultChecked={loaderData.wine.lactose}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Organic"
|
||||
name="organic"
|
||||
value={1}
|
||||
defaultChecked={loaderData.wine.organic}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Link</Form.Label>
|
||||
<Form.Control type="text" name="link" defaultValue={loaderData.wine.link ?? ""}/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
//const wines = await dbe.wine.findMany({select: {id: true, name: true, _count: {select: {containers: true}}},orderBy: {name: "asc"}});
|
||||
const wines = await dbe.wine.findMany({select: {id: true, slug: true, name: true, containers: true}, orderBy: {name: "asc"}});
|
||||
return json({wines});
|
||||
}
|
||||
|
||||
export default function EditWineRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit wine</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.wines.map((wine) => (
|
||||
<ListGroup.Item key={wine.id}>
|
||||
{wine.name} <Badge bg="secondary">{wine.containers.length} Containers</Badge>
|
||||
<div className="float-end">
|
||||
{ wine.containers.length <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/wine/" + wine.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"/admin/edit/image/wine/" + wine.id}>
|
||||
<Button variant="secondary" className="mx-3">
|
||||
Image
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"" + wine.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={"/inventory/wine/" + wine.slug}>
|
||||
<Button variant="success" className="mx-3">
|
||||
View
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { LoaderFunctionArgs, json, redirect } from "@remix-run/node";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, Form, ListGroup } from "react-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
export const action = async ({
|
||||
request,
|
||||
params
|
||||
}: ActionFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const form = await request.formData();
|
||||
const name = String(form.get("name"));
|
||||
const color = String(form.get("color")).replaceAll("#", "");
|
||||
|
||||
try{
|
||||
await dbe.wineStyle.update({ data: {name: name, color: color}, where: {id: id} });
|
||||
return redirect("/admin/edit/winestyle");
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
params
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const id = Number(params.id);
|
||||
|
||||
const style = await dbe.wineStyle.findUnique({where: {id: id}});
|
||||
|
||||
if(!style) return redirect("/admin/edit/winestyle");
|
||||
|
||||
return json({style});
|
||||
}
|
||||
|
||||
export default function EditWineStyleRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<h1>Edit Wine style ({loaderData.style.id})</h1>
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ID</Form.Label>
|
||||
<Form.Control type="text" disabled value={loaderData.style.id} />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Style</Form.Label>
|
||||
<Form.Control type="text" name="name" defaultValue={loaderData.style.name}/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Color</Form.Label>
|
||||
<Form.Control type="color" name="color" defaultValue={"#" + loaderData.style.color} />
|
||||
</Form.Group>
|
||||
<Button variant="primary" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Container, Row, Col, Button, ListGroup, Badge } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
|
||||
|
||||
export const loader = async ({
|
||||
request,
|
||||
} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
|
||||
const winestyles = await dbe.wineStyle.findMany({select: {id: true, name: true, color: true, _count: {select: {wines: true}}},orderBy: {name: "asc"}});
|
||||
return json({winestyles});
|
||||
}
|
||||
|
||||
export default function EditWineStyleRoute() {
|
||||
var loaderData = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<h1>Edit Winestyle</h1>
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<ListGroup>
|
||||
{loaderData.winestyles.map((style) => (
|
||||
<ListGroup.Item key={style.id}>
|
||||
<span style={{color:"#"+style.color}}>{style.name}</span> <Badge bg="secondary">{style._count.wines} Wines</Badge>
|
||||
<div className="float-end">
|
||||
{ style._count.wines <= 0 ? (
|
||||
<LinkContainer to={"/admin/remove/winestyle/" + style.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
) : "" }
|
||||
<LinkContainer to={"" + style.id}>
|
||||
<Button variant="primary" className="mx-3">
|
||||
Edit
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -35,17 +35,22 @@ export const action = async ({
|
||||
const abv = Number(parsedForm.get("abv"));
|
||||
const ibu = Number(parsedForm.get("ibu"));
|
||||
|
||||
const glass = parsedForm.has("glass") ? true : false;
|
||||
|
||||
const sugar = parsedForm.has("sugar") ? true : false;
|
||||
const gluten = parsedForm.has("gluten") ? true : false;
|
||||
const lactose = parsedForm.has("lactose") ? true : false;
|
||||
const organic = parsedForm.has("organic") ? true : false;
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const newFilename = "/beers/" + slug + "." + image.name.split('.').pop();
|
||||
var newFilename = null;
|
||||
if(image.size > 0)
|
||||
newFilename = "/beers/" + slug + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Move the file to the beers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
if(newFilename) renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
const createdBeer = await dbe.beer.create({ data: {
|
||||
slug: slug,
|
||||
@@ -55,6 +60,8 @@ export const action = async ({
|
||||
link: link,
|
||||
abv: abv,
|
||||
ibu: ibu,
|
||||
glass: glass,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
@@ -94,7 +101,7 @@ export default function NewBeerRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" placeholder="blond_beer" name="slug" pattern="[a-z_]+" required/>
|
||||
<Form.Control type="text" placeholder="blond_beer" name="slug" pattern="[a-z0-9_]+" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
@@ -143,8 +150,24 @@ export default function NewBeerRoute() {
|
||||
<Form.Control type="number" min={0} max={200} step={1} name="ibu" defaultValue={0} required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Glass</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Has custom glass"
|
||||
name="glass"
|
||||
value={1}
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
@@ -172,7 +195,7 @@ export default function NewBeerRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
<Form.Control type="file" name="image" accept="image/*"/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
|
||||
@@ -35,17 +35,20 @@ export const action = async ({
|
||||
|
||||
const mix = parsedForm.has("mix") ? true : false;
|
||||
|
||||
const sugar = parsedForm.has("sugar") ? true : false;
|
||||
const gluten = parsedForm.has("gluten") ? true : false;
|
||||
const lactose = parsedForm.has("lactose") ? true : false;
|
||||
const organic = parsedForm.has("organic") ? true : false;
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const newFilename = "/cocktails/" + slug + "." + image.name.split('.').pop();
|
||||
var newFilename = null;
|
||||
if(image.size > 0)
|
||||
newFilename = "/cocktails/" + slug + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Move the file to the beers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
if(newFilename) renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
const createdCocktail = await dbe.cocktail.create({ data: {
|
||||
slug: slug,
|
||||
@@ -55,6 +58,7 @@ export const action = async ({
|
||||
link: link,
|
||||
abv: abv,
|
||||
mix: mix,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
@@ -92,7 +96,7 @@ export default function NewCocktailRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" placeholder="some_cocktail_drink" name="slug" pattern="[a-z_]+" required/>
|
||||
<Form.Control type="text" placeholder="some_cocktail_drink" name="slug" pattern="[a-z0-9_]+" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
@@ -137,6 +141,12 @@ export default function NewCocktailRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
@@ -164,7 +174,7 @@ export default function NewCocktailRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
<Form.Control type="file" name="image" accept="image/*"/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
|
||||
@@ -28,11 +28,13 @@ export const action = async ({
|
||||
const description = String(parsedForm.get("description"));
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const newFilename = "/manufacturers/" + name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
var newFilename = null;
|
||||
if(image.size > 0)
|
||||
newFilename = "/manufacturers/" + name.toLowerCase().replaceAll(" ", "_") + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Move the file to the manufacturers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
if(newFilename) renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
const createdManufacturer = await dbe.manufacturer.create({ data: {name: name, description: description, image: newFilename, country: {connect: {code: country}}} });
|
||||
return redirect("/inventory/manufacturer/" + createdManufacturer.id);
|
||||
@@ -88,7 +90,7 @@ export default function NewManufacturerRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
<Form.Control type="file" name="image" accept="image/*"/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
|
||||
@@ -34,18 +34,21 @@ export const action = async ({
|
||||
const abv = Number(parsedForm.get("abv"));
|
||||
|
||||
const carbonated = parsedForm.has("carbonated") ? true : false;
|
||||
|
||||
const gluten = parsedForm.has("gluten") ? true : false;
|
||||
|
||||
const sugar = parsedForm.has("sugar") ? true : false;
|
||||
const gluten = parsedForm.has("gluten") ? true : false;
|
||||
const lactose = parsedForm.has("lactose") ? true : false;
|
||||
const organic = parsedForm.has("organic") ? true : false;
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const newFilename = "/soda/" + slug + "." + image.name.split('.').pop();
|
||||
var newFilename = null;
|
||||
if(image.size > 0)
|
||||
newFilename = "/soda/" + slug + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Move the file to the beers folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
if(newFilename) renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
const createdSoda = await dbe.soda.create({ data: {
|
||||
slug: slug,
|
||||
@@ -55,6 +58,7 @@ export const action = async ({
|
||||
link: link,
|
||||
abv: abv,
|
||||
carbonated: carbonated,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
@@ -92,7 +96,7 @@ export default function NewSodaRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" placeholder="some_soda_drink" name="slug" pattern="[a-z_]+" required/>
|
||||
<Form.Control type="text" placeholder="some_soda_drink" name="slug" pattern="[a-z0-9_]+" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
@@ -137,6 +141,12 @@ export default function NewSodaRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
@@ -164,7 +174,7 @@ export default function NewSodaRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
<Form.Control type="file" name="image" accept="image/*"/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
|
||||
@@ -46,17 +46,20 @@ export const action = async ({
|
||||
var notes : string | null = String(parsedForm.get("notes")) || null;
|
||||
if(notes == "") notes = null;
|
||||
|
||||
const sugar = parsedForm.has("sugar") ? true : false;
|
||||
const gluten = parsedForm.has("gluten") ? true : false;
|
||||
const lactose = parsedForm.has("lactose") ? true : false;
|
||||
const organic = parsedForm.has("organic") ? true : false;
|
||||
|
||||
const image = parsedForm.get("image") as NodeOnDiskFile;
|
||||
|
||||
const newFilename = "/wine/" + slug + "." + image.name.split('.').pop();
|
||||
var newFilename = null;
|
||||
if(image.size > 0)
|
||||
newFilename = "/wine/" + slug + "." + image.name.split('.').pop();
|
||||
|
||||
try{
|
||||
// Move the file to the wines folder with the slug as name
|
||||
renameSync(image.getFilePath(), "public" + newFilename);
|
||||
if(newFilename) renameSync(image.getFilePath(), "public" + newFilename);
|
||||
|
||||
const createdWine = await dbe.wine.create({ data: {
|
||||
slug: slug,
|
||||
@@ -70,6 +73,7 @@ export const action = async ({
|
||||
dry_score: dry_score,
|
||||
fresh_score: fresh_score,
|
||||
notes: notes,
|
||||
sugar: sugar,
|
||||
lactose: lactose,
|
||||
gluten: gluten,
|
||||
organic: organic,
|
||||
@@ -109,7 +113,7 @@ export default function NewWineRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Slug</Form.Label>
|
||||
<Form.Control type="text" placeholder="sauvignon_blanc" name="slug" pattern="[a-z_]+" required/>
|
||||
<Form.Control type="text" placeholder="sauvignon_blanc" name="slug" pattern="[a-z0-9_]+" required/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
@@ -184,6 +188,12 @@ export default function NewWineRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Allergies</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Sugar"
|
||||
name="sugar"
|
||||
value={1}
|
||||
/>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Gluten"
|
||||
@@ -211,7 +221,7 @@ export default function NewWineRoute() {
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Image</Form.Label>
|
||||
<Form.Control type="file" name="image" accept="image/*" required/>
|
||||
<Form.Control type="file" name="image" accept="image/*"/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" type="submit">
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function NewWineStyleRoute() {
|
||||
<Form method="POST">
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>Style</Form.Label>
|
||||
<Form.Control type="text" placeholder="Dark ale" name="name" />
|
||||
<Form.Control type="text" placeholder="Light wine" name="name" />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
|
||||
@@ -7,8 +7,9 @@ export default function AdminRoute() {
|
||||
<h1>Admin</h1>
|
||||
<ListGroup>
|
||||
<ListGroup.Item><Link to="/admin/new">Add new items</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/inventory">Change inventory numbers</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit/checkout">Undo checkout</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/edit">Edit or remove items</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/manage/inventory">Change inventory numbers</Link></ListGroup.Item>
|
||||
<ListGroup.Item><Link to="/admin/manage/checkout">Undo checkout</Link></ListGroup.Item>
|
||||
</ListGroup>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData, useSearchParams } from "@remix-run/react";
|
||||
import { useLoaderData } 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 { Col, Container, Row, Table } from "react-bootstrap";
|
||||
import { ContainerWithDrink } from "~/models/types";
|
||||
import timeAgo from "~/utils/datetime";
|
||||
|
||||
import { db } from "~/utils/db.server";
|
||||
|
||||
@@ -79,7 +74,9 @@ export const loader = async ({request} : LoaderFunctionArgs) => {
|
||||
|
||||
const statsLast24H = statsForContainers(containersLast24);
|
||||
|
||||
return json({total, statsLast24H});
|
||||
const history = await dbe.history.findMany({select: {checkoutAt: true, inventoryAfter: true, container: {select: {drink: true}}}, take: 10, orderBy: {checkoutAt: "desc"}});
|
||||
|
||||
return json({total, statsLast24H, history});
|
||||
};
|
||||
|
||||
export default function StatsRoute() {
|
||||
@@ -111,6 +108,29 @@ export default function StatsRoute() {
|
||||
Average alcohol percentage: {loadData.statsLast24H.averageAlcoholPercentage}% <br />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<Col xs={12}>
|
||||
<h3>History</h3>
|
||||
<Table striped>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Drink</th>
|
||||
<th>When</th>
|
||||
<th>Amount left</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loadData.history.map((entry) => (
|
||||
<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>
|
||||
</Table>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function ScanRoute() {
|
||||
<Table striped>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Beer</th>
|
||||
<th>Drink</th>
|
||||
<th>When</th>
|
||||
<th>Amount left</th>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export function volume(volume: number) : string {
|
||||
|
||||
if(volume < 1000){
|
||||
return "" + volume + "ml";
|
||||
}
|
||||
|
||||
return "" + (volume / 1000) + "l"
|
||||
}
|
||||
@@ -51,6 +51,7 @@ model Drink {
|
||||
gluten Boolean
|
||||
lactose Boolean
|
||||
organic Boolean
|
||||
sugar Boolean @default(true)
|
||||
containers Container[]
|
||||
delegate_aux_beer Beer?
|
||||
delegate_aux_wine Wine?
|
||||
@@ -67,6 +68,7 @@ model Beer {
|
||||
style_id Int
|
||||
style BeerStyle @relation(fields: [style_id], references: [id])
|
||||
ibu Float?
|
||||
glass Boolean @default(false)
|
||||
delegate_aux_drink Drink @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ model Drink {
|
||||
gluten Boolean
|
||||
lactose Boolean
|
||||
organic Boolean
|
||||
sugar Boolean @default(true)
|
||||
|
||||
containers Container[]
|
||||
|
||||
@@ -65,6 +66,8 @@ model Beer extends Drink {
|
||||
|
||||
ibu Float?
|
||||
|
||||
glass Boolean @default(false)
|
||||
|
||||
@@allow('read', true)
|
||||
@@allow('all', auth().type == Admin)
|
||||
}
|
||||
|
||||
+56
-2
@@ -9,12 +9,15 @@ export default defineConfig({
|
||||
plugins: [remix({
|
||||
routes(defineRoutes) {
|
||||
return defineRoutes((route) => {
|
||||
|
||||
// Index
|
||||
route("/", "routes/_index.tsx", { index: true });
|
||||
|
||||
|
||||
// Resources
|
||||
route("/resource/checkouts", "routes/resource/checkouts.tsx");
|
||||
|
||||
|
||||
// Inventory
|
||||
route("inventory", "routes/inventory/layout.tsx", () => {
|
||||
route("", "routes/inventory/route.tsx", { index: true });
|
||||
@@ -28,15 +31,18 @@ export default defineConfig({
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Scanner
|
||||
route("scan", "routes/scan/layout.tsx", () => {
|
||||
route("", "routes/scan/route.tsx", { index: true });
|
||||
route("link/:barcode", "routes/scan/link.$barcode.tsx");
|
||||
});
|
||||
|
||||
|
||||
// Admin
|
||||
route("admin", "routes/admin/layout.tsx", () => {
|
||||
route("", "routes/admin/route.tsx", { index: true });
|
||||
|
||||
route("new", "routes/admin/new/route.tsx");
|
||||
route("new/country", "routes/admin/new/country.tsx");
|
||||
route("new/section", "routes/admin/new/section.tsx");
|
||||
@@ -48,8 +54,56 @@ export default defineConfig({
|
||||
route("new/cocktail", "routes/admin/new/cocktail.tsx");
|
||||
route("new/container", "routes/admin/new/container.tsx");
|
||||
route("new/manufacturer","routes/admin/new/manufacturer.tsx");
|
||||
route("edit/inventory", "routes/admin/edit/inventory.tsx");
|
||||
route("edit/checkout", "routes/admin/edit/checkout.tsx");
|
||||
|
||||
route("edit", "routes/admin/edit/route.tsx");
|
||||
|
||||
route("edit/country", "routes/admin/edit/country.tsx");
|
||||
route("edit/country/:code", "routes/admin/edit/country.$code.tsx");
|
||||
route("remove/country/:code", "routes/admin/edit/remove/country.$code.tsx");
|
||||
|
||||
route("edit/section", "routes/admin/edit/section.tsx");
|
||||
route("edit/section/:id", "routes/admin/edit/section.$id.tsx");
|
||||
route("remove/section/:id", "routes/admin/edit/remove/section.$id.tsx");
|
||||
|
||||
route("edit/beerstyle", "routes/admin/edit/beerstyle.tsx");
|
||||
route("edit/beerstyle/:id", "routes/admin/edit/beerstyle.$id.tsx");
|
||||
route("remove/beerstyle/:id", "routes/admin/edit/remove/beerstyle.$id.tsx");
|
||||
|
||||
route("edit/winestyle", "routes/admin/edit/winestyle.tsx");
|
||||
route("edit/winestyle/:id", "routes/admin/edit/winestyle.$id.tsx");
|
||||
route("remove/winestyle/:id", "routes/admin/edit/remove/winestyle.$id.tsx");
|
||||
|
||||
route("edit/manufacturer", "routes/admin/edit/manufacturer.tsx");
|
||||
route("edit/manufacturer/:id", "routes/admin/edit/manufacturer.$id.tsx");
|
||||
route("remove/manufacturer/:id", "routes/admin/edit/remove/manufacturer.$id.tsx");
|
||||
route("edit/image/manufacturer/:id", "routes/admin/edit/image/manufacturer.$id.tsx");
|
||||
|
||||
route("edit/beer", "routes/admin/edit/beer.tsx");
|
||||
route("edit/beer/:id", "routes/admin/edit/beer.$id.tsx");
|
||||
route("remove/beer/:id", "routes/admin/edit/remove/beer.$id.tsx");
|
||||
route("edit/image/beer/:id", "routes/admin/edit/image/beer.$id.tsx");
|
||||
|
||||
route("edit/wine", "routes/admin/edit/wine.tsx");
|
||||
route("edit/wine/:id", "routes/admin/edit/wine.$id.tsx");
|
||||
route("remove/wine/:id", "routes/admin/edit/remove/wine.$id.tsx");
|
||||
route("edit/image/wine/:id", "routes/admin/edit/image/wine.$id.tsx");
|
||||
|
||||
route("edit/soda", "routes/admin/edit/soda.tsx");
|
||||
route("edit/soda/:id", "routes/admin/edit/soda.$id.tsx");
|
||||
route("remove/soda/:id", "routes/admin/edit/remove/soda.$id.tsx");
|
||||
route("edit/image/soda/:id", "routes/admin/edit/image/soda.$id.tsx");
|
||||
|
||||
route("edit/cocktail", "routes/admin/edit/cocktail.tsx");
|
||||
route("edit/cocktail/:id", "routes/admin/edit/cocktail.$id.tsx");
|
||||
route("remove/cocktail/:id", "routes/admin/edit/remove/cocktail.$id.tsx");
|
||||
route("edit/image/cocktail/:id", "routes/admin/edit/image/cocktail.$id.tsx");
|
||||
|
||||
route("edit/container", "routes/admin/edit/container.tsx");
|
||||
route("edit/container/:id", "routes/admin/edit/container.$id.tsx");
|
||||
route("remove/container/:id", "routes/admin/edit/remove/container.$id.tsx");
|
||||
|
||||
route("manage/inventory", "routes/admin/manage/inventory.tsx");
|
||||
route("manage/checkout", "routes/admin/manage/checkout.tsx");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user