Styling changes, suggestions delete as admin
This commit is contained in:
@@ -5,6 +5,7 @@ import { LinkContainer } from 'react-router-bootstrap';
|
||||
import { ContainerWithSection, DrinkComposite, containerTypeToString, isBeer, isDrinkWithContainersAndSection, isDrinkWithManufacturer, isDrinkWithStyle } from '~/models/types';
|
||||
import { volume } from '~/utils/conversions';
|
||||
import DrinkCard from './drink.card';
|
||||
import { Link } from '@remix-run/react';
|
||||
|
||||
interface Arguments {
|
||||
drink: DrinkComposite
|
||||
@@ -172,6 +173,7 @@ function DrinkPage(arg:Arguments) {
|
||||
<Col md={10}>
|
||||
<h2>{manufacturer.name} ({manufacturer.country_id})</h2>
|
||||
<p>{manufacturer.description}</p>
|
||||
<LinkContainer to={{pathname: "/inventory", search: "?manufacturer=" + manufacturer.id}} ><Button variant='secondary'>All drinks from {manufacturer.name}</Button></LinkContainer>
|
||||
</Col>
|
||||
{manufacturer.image ? (
|
||||
<Col md={2}>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BeerStyle, Manufacturer, WineStyle } from '@zenstackhq/runtime/models';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Form, Col, Row } from 'react-bootstrap';
|
||||
import ReactSlider from 'react-slider'
|
||||
import { ManufacturerWithDrinks } from '~/models/types';
|
||||
|
||||
interface Arguments {
|
||||
beerStyles: BeerStyle[];
|
||||
@@ -16,6 +17,7 @@ function DrinkFilter(arg : Arguments) {
|
||||
|
||||
const submit = useSubmit();
|
||||
const [abv, setAbv] = useState({min: Number(arg.searchParams.get("min-abv") || 0), max: Number(arg.searchParams.get("max-abv") || 50)})
|
||||
const [ibu, setIbu] = useState({min: Number(arg.searchParams.get("min-ibu") || 0), max: Number(arg.searchParams.get("max-ibu") || 150)})
|
||||
|
||||
const showBeerFilter = arg.searchParams.has("drink", "Beer");
|
||||
const showWineFilter = arg.searchParams.has("drink", "Wine");
|
||||
@@ -28,7 +30,7 @@ function DrinkFilter(arg : Arguments) {
|
||||
setTimeout(() => submit(form.current), 10);
|
||||
}
|
||||
|
||||
const slider = () =>
|
||||
const sliderAbv = () =>
|
||||
(<div>
|
||||
<ReactSlider
|
||||
className="form-range"
|
||||
@@ -47,14 +49,33 @@ function DrinkFilter(arg : Arguments) {
|
||||
|
||||
</div>);
|
||||
|
||||
const sliderIbu = () =>
|
||||
(<div>
|
||||
<ReactSlider
|
||||
className="form-range"
|
||||
thumbClassName="bg-primary text-light rounded dummy"
|
||||
trackClassName="bg-secondary h-100 rounded dummy"
|
||||
min={0}
|
||||
max={150}
|
||||
defaultValue={[ibu.min, ibu.max]}
|
||||
renderThumb={(props, state) => <div {...props}>{state.valueNow}</div>}
|
||||
pearling
|
||||
minDistance={2}
|
||||
onAfterChange={(number, index) => {setIbu({min: number[0], max: number[1]}); submitAfterRender()}}
|
||||
/>
|
||||
<input type="hidden" name="min-ibu" value={ibu.min} />
|
||||
<input type="hidden" name="max-ibu" value={ibu.max} />
|
||||
|
||||
</div>);
|
||||
|
||||
const sortKey = arg.searchParams.get("sort") || "name";
|
||||
|
||||
return (
|
||||
<Form noValidate method="GET" ref={form} onChange={(event) => {submit(event.currentTarget); }}>
|
||||
<Form noValidate method="GET" ref={form} className='pe-2' onChange={(event) => {submit(event.currentTarget); }}>
|
||||
<Row>
|
||||
<Col xs={6} md={12}>
|
||||
<Form.Group controlId="sort" className='mb-2'>
|
||||
<Form.Label>Sort</Form.Label>
|
||||
<Col>
|
||||
<Form.Group controlId="sort" className='bg-body-secondary rounded p-3'>
|
||||
<Form.Label className='fw-bold'>Sort</Form.Label>
|
||||
{ [
|
||||
["name", "Name"],
|
||||
["popular", "Popularity"],
|
||||
@@ -74,8 +95,8 @@ function DrinkFilter(arg : Arguments) {
|
||||
))}
|
||||
|
||||
</Form.Group>
|
||||
<Form.Group controlId="drink-type" className='mb-2'>
|
||||
<Form.Label>Drink type</Form.Label>
|
||||
<Form.Group controlId="drink-type" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Drink type</Form.Label>
|
||||
{ ["Beer", "Wine", "Soda", "Cocktail"].map(key => (
|
||||
<Form.Check
|
||||
id={"drink-" + key}
|
||||
@@ -88,8 +109,8 @@ function DrinkFilter(arg : Arguments) {
|
||||
))}
|
||||
|
||||
</Form.Group>
|
||||
<Form.Group controlId="drink-modifiers" className='mb-2'>
|
||||
<Form.Label>Drink</Form.Label>
|
||||
<Form.Group controlId="drink-modifiers" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Drink</Form.Label>
|
||||
{ ["Sugar-free", "Gluten-free", "Organic"].map(key => (
|
||||
<Form.Check
|
||||
id={"drinkmod-" + key}
|
||||
@@ -100,11 +121,13 @@ function DrinkFilter(arg : Arguments) {
|
||||
defaultChecked={arg.searchParams.has(key.toLowerCase()) ? true : false}
|
||||
/>
|
||||
))}
|
||||
<Form.Label>ABV</Form.Label>
|
||||
{slider()}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="manufacturer" className='mb-2'>
|
||||
<Form.Label>Manufacturer</Form.Label>
|
||||
<Form.Group controlId="drink-abv" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>ABV</Form.Label>
|
||||
{sliderAbv()}
|
||||
</Form.Group>
|
||||
{/* <Form.Group controlId="manufacturer" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Manufacturer</Form.Label>
|
||||
{ arg.manufacturers.map(manufacturer => (
|
||||
<Form.Check
|
||||
id={"manufacturer-" + manufacturer.name}
|
||||
@@ -115,12 +138,11 @@ function DrinkFilter(arg : Arguments) {
|
||||
defaultChecked={arg.searchParams.has("manufacturer", manufacturer.id.toString()) ? true : false}
|
||||
/>
|
||||
))}
|
||||
</Form.Group>
|
||||
</Col>
|
||||
<Col>
|
||||
</Form.Group> */}
|
||||
{ showBeerFilter ? (
|
||||
<Form.Group controlId="beer-style" className='mb-2'>
|
||||
<Form.Label>Beer style</Form.Label>
|
||||
<>
|
||||
<Form.Group controlId="beer-style" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold mt-3'>Beer style</Form.Label>
|
||||
{ arg.beerStyles.map(style => (
|
||||
<Form.Check
|
||||
id={"beerstyle-" + style.name}
|
||||
@@ -132,10 +154,15 @@ function DrinkFilter(arg : Arguments) {
|
||||
/>
|
||||
))}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="beer-ibu" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>IBU</Form.Label>
|
||||
{sliderIbu()}
|
||||
</Form.Group>
|
||||
</>
|
||||
) : ""}
|
||||
{ showWineFilter ? (
|
||||
<Form.Group controlId="wine-style" className='mb-2'>
|
||||
<Form.Label>Wine style</Form.Label>
|
||||
<Form.Group controlId="wine-style" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Wine style</Form.Label>
|
||||
{ arg.wineStyles.map(style => (
|
||||
<Form.Check
|
||||
id={"winestyle-" + style.name}
|
||||
@@ -149,8 +176,8 @@ function DrinkFilter(arg : Arguments) {
|
||||
</Form.Group>
|
||||
) : ""}
|
||||
{ showSodaFilter ? (
|
||||
<Form.Group controlId="soda-carbonated" className='mb-2'>
|
||||
<Form.Label>Soda</Form.Label>
|
||||
<Form.Group controlId="soda-carbonated" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Soda</Form.Label>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
label="Both"
|
||||
@@ -175,8 +202,8 @@ function DrinkFilter(arg : Arguments) {
|
||||
</Form.Group>
|
||||
) : ""}
|
||||
{ showCocktailFilter ? (
|
||||
<Form.Group controlId="cocktail-mix" className='mb-2'>
|
||||
<Form.Label>Cocktail</Form.Label>
|
||||
<Form.Group controlId="cocktail-mix" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>Cocktail</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Mix"
|
||||
@@ -186,8 +213,8 @@ function DrinkFilter(arg : Arguments) {
|
||||
/>
|
||||
</Form.Group>
|
||||
) : ""}
|
||||
<Form.Group controlId="inventory" className='mb-2'>
|
||||
<Form.Label>No Inventory</Form.Label>
|
||||
<Form.Group controlId="inventory" className='bg-body-secondary rounded p-3 mt-3'>
|
||||
<Form.Label className='fw-bold'>No Inventory</Form.Label>
|
||||
<Form.Check
|
||||
type="checkbox"
|
||||
label="Show"
|
||||
|
||||
@@ -47,13 +47,24 @@ export async function findDrinksFromSearch(searchParams: URLSearchParams) : Prom
|
||||
// Beers
|
||||
if(searchParams.has("drink", "Beer") || noDrinkSelected){
|
||||
|
||||
let where = Object.assign({}, whereBase, {style: {}});
|
||||
let where = Object.assign({}, whereBase, {style: {}, AND: <any>[]});
|
||||
|
||||
// Beer styles
|
||||
if(searchParams.has("beerstyle")){
|
||||
where.style = {id: {in: searchParams.getAll("beerstyle").map(Number)}};
|
||||
}
|
||||
|
||||
let minibu = 0;
|
||||
let maxibu = 150;
|
||||
if(searchParams.has("min-ibu")){
|
||||
minibu = Number(searchParams.get("min-ibu"));
|
||||
}
|
||||
if(searchParams.has("max-ibu")){
|
||||
maxibu = Number(searchParams.get("max-ibu"));
|
||||
}
|
||||
where.AND.push({ibu: {gte: minibu}});
|
||||
where.AND.push({ibu: {lte: maxibu}});
|
||||
|
||||
result = result.concat(await dbe.beer.findMany({
|
||||
include: {style: true, manufacturer: true, containers: {include: {section: true, checkouts: true}}},
|
||||
where: where
|
||||
|
||||
@@ -23,13 +23,13 @@ export async function findManufacturersFromSearch(searchParams: URLSearchParams)
|
||||
|
||||
if(searchParams.has("drink")){
|
||||
result = result.concat(await dbe.manufacturer.findMany({
|
||||
orderBy: {name: "asc"},
|
||||
orderBy: {drinks: {_count: "desc"}},
|
||||
where: {drinks: { some: { type: { in: searchParams.getAll("drink").map((str) => {return DrinkType[str as keyof typeof DrinkType]})}}}}
|
||||
}));
|
||||
}
|
||||
else {
|
||||
result = result.concat(await dbe.manufacturer.findMany({
|
||||
orderBy: {name: "asc"},
|
||||
orderBy: {drinks: {_count: "desc"}},
|
||||
where: {NOT: {drinks: {none: {}}}}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export type DrinkWithContainersAndSectionAndHistory = (Drink & { containers: Con
|
||||
export type DrinkWithContainers = DrinkWithContainersAndSection | DrinkWithContainersAndHistory | DrinkWithContainersAndSectionAndHistory | (Drink & { containers: Container[]})
|
||||
export type DrinkWithManufacturer = Drink & { manufacturer: Manufacturer}
|
||||
|
||||
export type ManufacturerWithDrinks = Manufacturer & {drinks: Drink[]}
|
||||
|
||||
export type BeerWithStyle = Beer & { style : BeerStyle }
|
||||
export type WineWithStyle = Wine & { style : WineStyle }
|
||||
|
||||
|
||||
@@ -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.suggestion.delete({where: {id: id}});
|
||||
return redirect("/inventory/suggestions");
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData, useSearchParams } from "@remix-run/react";
|
||||
import { enhance } from "@zenstackhq/runtime";
|
||||
import { Accordion, Button, Col, Container, Row, useAccordionButton } from "react-bootstrap";
|
||||
import { useState } from "react";
|
||||
import { Accordion, Button, Col, Container, Offcanvas, 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";
|
||||
@@ -30,42 +31,37 @@ export default function BeersRoute() {
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
function CustomToggle({ children, eventKey } : any) {
|
||||
const decoratedOnClick = useAccordionButton(eventKey, () => {});
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={decoratedOnClick}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
const handleClose = () => setShow(false);
|
||||
const handleShow = () => setShow(true);
|
||||
|
||||
const numResults = loadData.drinkResults.length;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row className="mt-3">
|
||||
<Col key="filtering" xs={12} md={4} xl={3} className="mb-3">
|
||||
<Accordion>
|
||||
<Col key="filtering" className="mb-3">
|
||||
<div>
|
||||
<CustomToggle eventKey="0">Sorting and Filtering</CustomToggle> <span className="float-end">{numResults} Results</span>
|
||||
<Button variant="primary" onClick={handleShow}>Sorting and Filtering</Button> <span className="float-end">{numResults} Results</span>
|
||||
</div>
|
||||
|
||||
<Accordion.Collapse eventKey="0">
|
||||
<div className="h-100 p-3 mt-3 text-bg-dark rounded-3">
|
||||
<Offcanvas show={show} onHide={handleClose}>
|
||||
<Offcanvas.Header closeButton>
|
||||
<Offcanvas.Title>Sorting and filtering <br/> <span className="fs-6">{numResults} Results</span></Offcanvas.Title>
|
||||
</Offcanvas.Header>
|
||||
<Offcanvas.Body>
|
||||
<DrinkFilter
|
||||
beerStyles={loadData.beerStyles}
|
||||
wineStyles={loadData.wineStyles}
|
||||
manufacturers={loadData.manufacturers}
|
||||
searchParams={searchParams} >
|
||||
</DrinkFilter>
|
||||
</div>
|
||||
</Accordion.Collapse>
|
||||
</Accordion>
|
||||
</Offcanvas.Body>
|
||||
</Offcanvas>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col key="results">
|
||||
<Row xs={2} md={3} lg={4} className="g-4">
|
||||
{loadData.drinkResults.map((drink) => (
|
||||
|
||||
@@ -89,29 +89,111 @@ export default function StatsRoute() {
|
||||
return (
|
||||
<Container>
|
||||
<Row className="mt-3">
|
||||
<Col key="stats-total" className="mb-3">
|
||||
<Col key="stats-total" className="mb-3" xs={12} sm={6}>
|
||||
<h2>Total inventory</h2>
|
||||
Total drinks in inventory: {loadData.total.drinksInventory} <br />
|
||||
Total beers in inventory: {loadData.total.beersInventory} <br />
|
||||
Total wines in inventory: {loadData.total.wineInventory} <br />
|
||||
Total sodas in inventory: {loadData.total.sodaInventory} <br />
|
||||
Total cocktails in inventory: {loadData.total.cocktailInventory} <br /><br />
|
||||
|
||||
Total pure alcohol in inventory: {loadData.total.totalLiterAlcohol} Liter <br />
|
||||
Total price inventory: €{loadData.total.totalPrice} <br />
|
||||
Average alcohol percentage: {loadData.total.averageAlcoholPercentage}% <br />
|
||||
<Table className="pe-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td># Drinks</td>
|
||||
<td>{loadData.total.drinksInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Beers</td>
|
||||
<td>{loadData.total.beersInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Wines</td>
|
||||
<td>{loadData.total.wineInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Sodas</td>
|
||||
<td>{loadData.total.sodaInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Cocktails</td>
|
||||
<td>{loadData.total.cocktailInventory}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Statistic</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Pure alcohol</td>
|
||||
<td>{loadData.total.totalLiterAlcohol} Liter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Price</td>
|
||||
<td>€{loadData.total.totalPrice}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Average alcohol percentage</td>
|
||||
<td>{loadData.total.averageAlcoholPercentage}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Col>
|
||||
<Col key="stats-24h" className="mb-3">
|
||||
<h2>Last 24H checkouts</h2>
|
||||
Drinks checked out: {loadData.statsLast24H.drinksInventory} <br />
|
||||
Beer checked out: {loadData.statsLast24H.beersInventory} <br />
|
||||
Wines checked out: {loadData.statsLast24H.wineInventory} <br />
|
||||
Sodas checked out: {loadData.statsLast24H.sodaInventory} <br />
|
||||
Cocktails checked out: {loadData.statsLast24H.cocktailInventory} <br /><br />
|
||||
|
||||
Pure alcohol checked out: {loadData.statsLast24H.totalLiterAlcohol} Liter <br />
|
||||
Price checked out: €{loadData.statsLast24H.totalPrice} <br />
|
||||
Average alcohol percentage: {loadData.statsLast24H.averageAlcoholPercentage}% <br />
|
||||
<Table className="pe-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td># Drinks</td>
|
||||
<td>{loadData.statsLast24H.drinksInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Beers</td>
|
||||
<td>{loadData.statsLast24H.beersInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Wines</td>
|
||||
<td>{loadData.statsLast24H.wineInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Sodas</td>
|
||||
<td>{loadData.statsLast24H.sodaInventory}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Cocktails</td>
|
||||
<td>{loadData.statsLast24H.cocktailInventory}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Statistic</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Pure alcohol</td>
|
||||
<td>{loadData.statsLast24H.totalLiterAlcohol} Liter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Price</td>
|
||||
<td>€{loadData.statsLast24H.totalPrice}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Average alcohol percentage</td>
|
||||
<td>{loadData.statsLast24H.averageAlcoholPercentage}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ActionFunctionArgs, LoaderFunctionArgs, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Button, Col, Container, Form, Row, Table } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import timeAgo from "~/utils/datetime";
|
||||
|
||||
import { enhance } from "~/utils/db.server";
|
||||
@@ -33,11 +34,14 @@ export const action = async ({
|
||||
};
|
||||
|
||||
export const loader = async ({request} : LoaderFunctionArgs) => {
|
||||
const { dbe } = await enhance(request);
|
||||
const { dbe, session } = await enhance(request);
|
||||
|
||||
const suggestions = await dbe.suggestion.findMany({take: 15, orderBy: {createdAt: "desc"}});
|
||||
|
||||
return json({suggestions});
|
||||
let user = session.get("user");
|
||||
let isAdmin = user?.type == "Admin";
|
||||
|
||||
return json({suggestions, isAdmin});
|
||||
};
|
||||
|
||||
export default function SuggestionsRoute() {
|
||||
@@ -61,7 +65,7 @@ export default function SuggestionsRoute() {
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group>
|
||||
<Form.Group className="mt-3">
|
||||
<Form.Label>
|
||||
Suggestion*:
|
||||
</Form.Label>
|
||||
@@ -73,7 +77,7 @@ export default function SuggestionsRoute() {
|
||||
/>
|
||||
</Form.Group>
|
||||
|
||||
<Button variant="primary" className="mt-1" type="submit">
|
||||
<Button variant="primary" className="mt-3" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
@@ -89,6 +93,9 @@ export default function SuggestionsRoute() {
|
||||
<th>Name</th>
|
||||
<th>Date</th>
|
||||
<th>Suggestions</th>
|
||||
{ loadData.isAdmin ? (
|
||||
<th>Delete</th>
|
||||
) : ""}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -97,6 +104,15 @@ export default function SuggestionsRoute() {
|
||||
<td>{entry.name ? (<>{entry.name}</>) : ( <><i>No name</i></> ) }</td>
|
||||
<td>{timeAgo(new Date(entry.createdAt))}</td>
|
||||
<td>{entry.content}</td>
|
||||
{ loadData.isAdmin ? (
|
||||
<td>
|
||||
<LinkContainer to={"/admin/remove/suggestion/" + entry.id}>
|
||||
<Button variant="danger">
|
||||
Remove
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</td>
|
||||
) : ""}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@@ -10,26 +10,26 @@ export default function timeAgo(previous: Date) {
|
||||
const elapsed : number = (now.getTime() - previous.getTime());
|
||||
|
||||
if (elapsed < msPerMinute) {
|
||||
return Math.round(elapsed/1000) + ' seconds ago';
|
||||
return Math.round(elapsed/1000) + ' seconds ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerHour) {
|
||||
return Math.round(elapsed/msPerMinute) + ' minutes ago';
|
||||
return Math.round(elapsed/msPerMinute) + ' minutes ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerDay ) {
|
||||
return Math.round(elapsed/msPerHour ) + ' hours ago';
|
||||
return Math.round(elapsed/msPerHour ) + ' hours ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerMonth) {
|
||||
return 'approximately ' + Math.round(elapsed/msPerDay) + ' days ago';
|
||||
return Math.round(elapsed/msPerDay) + ' days ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerYear) {
|
||||
return 'approximately ' + Math.round(elapsed/msPerMonth) + ' months ago';
|
||||
return Math.round(elapsed/msPerMonth) + ' months ago';
|
||||
}
|
||||
|
||||
else {
|
||||
return 'approximately ' + Math.round(elapsed/msPerYear ) + ' years ago';
|
||||
return Math.round(elapsed/msPerYear ) + ' years ago';
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,8 @@ export default defineConfig({
|
||||
route("edit/container/:id", "routes/admin/edit/container.$id.tsx");
|
||||
route("remove/container/:id", "routes/admin/edit/remove/container.$id.tsx");
|
||||
|
||||
route("remove/suggestion/:id", "routes/admin/edit/remove/suggestion.$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