diff --git a/src/app/page.scss b/src/app/page.scss
index 7cfa8a3..2c11957 100644
--- a/src/app/page.scss
+++ b/src/app/page.scss
@@ -42,28 +42,52 @@ body {
#index {
display: flex;
- flex-direction: row;
- justify-content: center;
- flex-wrap: wrap;
- align-items: stretch;
+ flex-direction: column;
+ justify-content: felx-start;
+ flex-wrap: nowrap;
+ align-items: flex-start;
height: calc(100% - 50px);
font-size: 3em;
- div {
- height: calc(33% - 20px - 4px);
- width: calc(50% - 20px - 4px - 20px);
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: var(--primary);
- margin: 10px;
+ div.link_category {
+ display: grid;
+ grid-template-columns: 100%;
+ grid-template-rows: 3rem auto;
+ row-gap: 10px;
+ height: calc(33% - 20px);
+ width: calc(100% - 20px);
+ padding: 10px;
- .fa {
- margin-right: 15px;
+ p {
+ font-size: 1em;
+ margin: 0px;
+ width: max-content;
}
- &:hover {
- filter: brightness(0.9);
+ div.links_btns {
+ height: calc(100%);
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+
+ div {
+ height: 100%;
+ width: calc(25% - 20px);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ row-gap: 5%;
+ background-color: var(--primary);
+ margin: 0px 10px;
+ border-radius: .2em;
+
+ &:hover {
+ filter: brightness(0.9);
+ }
+ }
}
}
-}
+
+
+}
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 2339463..d6fc478 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -2,11 +2,12 @@
import React from 'react';
import 'moment/locale/fr';
import moment from 'moment';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import Navbar from '@/components/navbar';
import FontAwesome from 'react-fontawesome';
import { logout } from '@/reducers/login';
import { Action } from 'redux';
+import { State } from "@/types";
import { useRouter } from 'next/navigation';
moment.locale('fr');
@@ -14,6 +15,7 @@ moment.locale('fr');
const App = () => {
const dispatch = useDispatch();
const router = useRouter();
+ const login = useSelector((state: State) => state.login);
return (
<>
@@ -22,23 +24,72 @@ const App = () => {
-
router.push('/sell?except=goodies')}>
- Vente de bouffe
-
-
router.push('/sell?only=goodies')}>
- Vente de goodies
-
-
router.push('/preparation')}>
- Préparation générale
-
-
router.push('/preparation?only=pizzas')}>
- Préparation des pizzas
-
-
router.push('/tv')}>
- TV
-
-
router.push('/items')}>
-
Gestion des items
+ { ['admin','preparator'].includes(login.key) &&
+ <>
+
+
Préparation par commande :
+
+
router.push('/preparation')}>
+ Général
+
+
router.push('/preparation?only=pizzas')}>
+ Pizzas
+
+
router.push('/preparation?only=crepes,galettes')}>
+ Crêpes
+
+
router.push('/preparation?only=croques')}>
+ Croques
+
+
+
+
+
Préparation par item :
+
+
router.push('/preparation?by=item')}>
+ Général
+
+
router.push('/preparation?only=pizzas&by=item')}>
+ Pizzas
+
+
router.push('/preparation?only=crepes,galettes&by=item')}>
+ Crêpes
+
+
router.push('/preparation?only=croques&by=item')}>
+ Croques
+
+
+
+ >
+ }
+
+ { ['admin','seller','preparator','tv'].includes(login.key) &&
+ <>
+
Autres controles :
+
+ { ['admin','seller'].includes(login.key) &&
+
router.push('/sell')}>
+ Vente
+
+ }
+ { login.key === 'admin' &&
+
router.push('/items')}>
+ Gestion
+
+ }
+ { ['admin','preparator'].includes(login.key) &&
+
router.push('/service')}>
+ Service
+
+ }
+ { ['admin','tv'].includes(login.key) &&
+
router.push('/tv')}>
+ TV
+
+ }
+
+ >
+ }
>
diff --git a/src/app/preparation/page.scss b/src/app/preparation/page.scss
index c10a323..10d2b77 100644
--- a/src/app/preparation/page.scss
+++ b/src/app/preparation/page.scss
@@ -19,12 +19,12 @@
.status {
margin: auto;
width: 100%;
- height: 100%;
+ height: calc(100% - 20px);
padding: 10px 14px 0;
-
- &::-webkit-scrollbar {
- width: 0 !important;
- }
+ overflow-x: hidden;
+ overflow-y: auto;
+ scrollbar-color: var(--primary-light) transparent;
+ scrollbar-width: auto;
.title {
font-size: 1.5em;
@@ -40,6 +40,7 @@
.status .orders .order {
background: var(--primary-light);
padding-left: 10px;
+ position: relative;
display: flex;
justify-content: space-between;
align-items: center;
@@ -51,9 +52,13 @@
margin: 5px 0;
width: 20%;
+ .place,
+ .quantity {
+ font-size: 1.75em;
+ }
+
.place {
margin-bottom: 10px;
- font-size: 1.75em;
}
}
@@ -66,6 +71,23 @@
}
}
+ &.timewarning:before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 10px;
+ height: 100%;
+ }
+
+ &.orange:before {
+ background-color: var(--toastify-color-warning);
+ }
+
+ &.red:before {
+ background-color: var(--toastify-color-error);
+ }
+
.next {
background-color: var(--success);
width: 20%;
@@ -81,6 +103,11 @@
transition: transform 0.5s ease-in, opacity 0.5s ease-in;
}
+ &.disabled {
+ visibility: hidden;
+ background-color: var(--warning-dark);
+ }
+
&.downgrade {
background-color: var(--warning-dark);
diff --git a/src/app/preparation/page.tsx b/src/app/preparation/page.tsx
index 4a10a9c..5f20893 100644
--- a/src/app/preparation/page.tsx
+++ b/src/app/preparation/page.tsx
@@ -19,9 +19,15 @@ const Page = () => {
// Renvoie les commandes contenant au moins un item dans la catégory du paramètre
if (searchParams.has("only")) {
+ const categoriesToDisplay = searchParams.get("only").split(",");
orders = orders.filter((order) =>
- order.orderItems.some((orderItem) => orderItem.item.category.key == searchParams.get("only"))
+ order.orderItems.some((orderItem) => categoriesToDisplay.includes(orderItem.item.category.key))
);
+ if (searchParams.has("by") && searchParams.get("by") == "item") {
+ orders.forEach((order) => {
+ order.orderItems = order.orderItems.filter((orderItem) => categoriesToDisplay.includes(orderItem.item.category.key));
+ });
+ }
}
// used only to refresh the component every minute
@@ -65,11 +71,44 @@ const Page = () => {
}
};
+ const separate_by: string = searchParams.get('by') ?? 'order';
+
const displayOrders = (orders: Array
) => {
+
+ const tenMinutesAgo: moment.Moment = moment().subtract(1, 'minutes');
+ const twentyMinutesAgo: moment.Moment = moment().subtract(20, 'minutes');
+
+ type itemQuantity = {
+ id: number,
+ name: string,
+ quantity: number,
+ }
+
+ const items: Array = [];
+
+ if (separate_by === 'item') {
+ orders.forEach((order) => {
+ order.orderItems.forEach(orderItem => {
+ const itemId: number = orderItem.item.id;
+ const itemName: string = orderItem.item.name
+ if (items.some(item => item.id === itemId)) {
+ items.find(item => item.id === itemId).quantity ++;
+ } else {
+ const item: itemQuantity = {
+ id: itemId,
+ name: itemName,
+ quantity: 1
+ };
+ items.push(item);
+ }
+ })
+ });
+ }
+
return (
- {orders.map((order) => (
-
+ {separate_by === 'order' && orders.map((order) => (
+
{order.place}
{moment(order.createdAt).fromNow(true)}
@@ -89,12 +128,22 @@ const Page = () => {
) : (
-
editOrder(order)}>
+
editOrder(order)}>
)}
))}
+ {separate_by === 'item' && items.map((item) => (
+
+
+ x {item.quantity}
+
+
+
+ ))}
);
};
diff --git a/src/app/service/page.scss b/src/app/service/page.scss
new file mode 100644
index 0000000..73ebe18
--- /dev/null
+++ b/src/app/service/page.scss
@@ -0,0 +1,161 @@
+.preparation-mode-button {
+ padding: 0 10px;
+ width: 210px;
+ text-align: left;
+ height: 100%;
+ line-height: 50px;
+
+ &.downgrade {
+ background-color: var(--warning-dark);
+ }
+}
+
+#preparation {
+ display: flex;
+ height: calc(100% - 50px);
+ justify-content: space-between;
+ background-color: var(--primary);
+
+ .status.service {
+ margin: auto;
+ width: 100%;
+ height: calc(100% - 20px);
+ padding: 10px 14px 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ scrollbar-color: var(--primary-light) transparent;
+ scrollbar-width: auto;
+
+ .title {
+ font-size: 1.5em;
+ line-height: 60px;
+ }
+
+ // Removes the visuals clicks for pending (because you can't downgrade a pending order)
+ &.pending .orders .order .next.downgrade {
+ background-color: var(--danger-dark);
+ }
+ }
+
+ .status .orders.service {
+ width: calc(100% - 10px);
+ display: grid;
+ grid-template-columns: 50% 50%;
+ column-gap: 10px;
+ }
+
+ .status .orders .order {
+ background: var(--primary-light);
+ padding-left: 10px;
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+
+ .titles {
+ display: flex;
+ flex-direction: column;
+ margin: 5px 0;
+ width: 20%;
+
+ .place,
+ .quantity {
+ font-size: 1.75em;
+ }
+
+ .place {
+ margin-bottom: 10px;
+ }
+ }
+
+ .items {
+ text-align: left;
+ margin-right: auto;
+
+ .options {
+ font-style: italic;
+ }
+ }
+
+ &.timewarning:before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 10px;
+ height: 100%;
+ }
+
+ &.orange:before {
+ background-color: var(--toastify-color-warning);
+ }
+
+ &.red:before {
+ background-color: var(--toastify-color-error);
+ }
+
+ .next {
+ background-color: var(--success);
+ width: 20%;
+ font-size: 2em;
+ position: relative;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ transition: background-color 0.5s ease-in;
+
+ .fa {
+ transition: transform 0.5s ease-in, opacity 0.5s ease-in;
+ }
+
+ &.downgrade {
+ background-color: var(--warning-dark);
+
+ .fa {
+ transform: rotateZ(180deg);
+ }
+ }
+
+ &:before {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ &:hover {
+ filter: brightness(0.9);
+ }
+ }
+ }
+}
+
+.preparation-modal {
+ .actions {
+ display: flex;
+ flex-flow: row nowrap;
+ justify-content: center;
+
+ .button {
+ display: inline-block;
+ padding: 40px 100px;
+ margin: 0 20px;
+
+ color: #fff;
+
+ &.cancel {
+ background: var(--danger);
+ }
+
+ &.confirm {
+ background: var(--success);
+ }
+
+ &:hover {
+ filter: brightness(0.9);
+ }
+ }
+ }
+}
diff --git a/src/app/service/page.tsx b/src/app/service/page.tsx
new file mode 100644
index 0000000..b779c10
--- /dev/null
+++ b/src/app/service/page.tsx
@@ -0,0 +1,144 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import "../page.scss";
+import "./page.scss";
+import Navbar from "../../components/navbar";
+import moment from "moment";
+import FontAwesome from "react-fontawesome";
+import { useSelector } from "react-redux";
+import { Order, State, Status } from "@/types";
+import { downgradeOrder, upgradeOrder } from "@/utils/orders";
+import Modal from "../../components/modals/modal";
+import Loader from "../../components/loader";
+import { useSearchParams } from "next/navigation";
+
+const Page = () => {
+ const searchParams = useSearchParams();
+ let orders = useSelector((state: State) => state.orders);
+
+ // Renvoie les commandes contenant au moins un item dans la catégory du paramètre
+ if (searchParams.has("only")) {
+ orders = orders.filter((order) =>
+ order.orderItems.some((orderItem) => orderItem.item.category.key == searchParams.get("only"))
+ );
+ }
+
+ // used only to refresh the component every minute
+ const [tictac, setTicTac] = useState(false);
+ const [loading, setLoading] = useState
(null);
+ const [confirmOrder, setConfirmOrder] = useState(null);
+ const [downgradeMode, setDowngradeMode] = useState(false);
+
+ // used only to refresh the component every minute to refresh the duration on the
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setTicTac(!tictac);
+ }, 1000 * 60);
+
+ return () => clearInterval(interval);
+ });
+
+ const editOrder = async (order: Order, confirmed = false) => {
+ // If ready to be confirmed
+ if (order.status === Status.READY && !confirmed && !downgradeMode) {
+ setConfirmOrder(order);
+ }
+ // If ready to be cancelled
+ else if (order.status === Status.PENDING && !confirmed && downgradeMode) {
+ setConfirmOrder(order);
+ } else {
+ if (!loading) {
+ setLoading(order);
+ try {
+ if (!downgradeMode) {
+ await upgradeOrder(order);
+ } else {
+ setDowngradeMode(false);
+ await downgradeOrder(order);
+ }
+ } catch (e) {
+ }
+ setLoading(null);
+ setConfirmOrder(null);
+ }
+ }
+ };
+
+ const displayOrders = (orders: Array) => {
+
+ const tenMinutesAgo: moment.Moment = moment().subtract(10, 'minutes');
+ const twentyMinutesAgo: moment.Moment = moment().subtract(20, 'minutes');
+
+ return (
+
+ {orders.map((order) => (
+
+
+ {order.place}
+ {moment(order.createdAt).fromNow(true)}
+
+
+ {loading && loading.id === order.id ? (
+
+
+
+ ) : (
+
editOrder(order)}>
+
+
+ )}
+
+ ))}
+
+ );
+ };
+
+ return (
+ <>
+
+ setDowngradeMode(!downgradeMode)}
+ className={`preparation-mode-button ${downgradeMode ? "downgrade" : ""}`}>
+ {!downgradeMode ? "Retour cuisine" : "Servir"}
+
+
+
+
+ A servir
+ {displayOrders(orders.filter((order) => order.status === Status.READY))}
+
+
+
+ {downgradeMode ? (
+ Annuler la commande {confirmOrder && confirmOrder.place} ?
+ ) : (
+ La commande {confirmOrder && confirmOrder.place} a-t-elle bien été livrée ?
+ )}
+
+
setConfirmOrder(null)}>
+ {loading ? : "Annuler"}
+
+
{
+ await editOrder(confirmOrder, true);
+ setConfirmOrder(null);
+ }}>
+ {loading ? : "Confirmer"}
+
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/tv/page.tsx b/src/app/tv/page.tsx
index fbe03d7..8b5cfc9 100644
--- a/src/app/tv/page.tsx
+++ b/src/app/tv/page.tsx
@@ -70,9 +70,11 @@ const OrderGrid = ({ orders, passingRef }: { orders: Array; passingRe
const Page = () => {
const orders = useSelector((state: State) => state.orders);
- const pendingOrders = orders.filter((order) => order.status === Status.PENDING);
- const preparingOrders = orders.filter((order) => order.status === Status.PREPARING);
- const readyOrders = orders.filter((order) => order.status === Status.READY);
+ const tvOrders = orders.filter(order => order.orderItems.some(item => item.item.category.needsPreparation) === true)
+
+ const pendingOrders = tvOrders.filter((order) => order.status === Status.PENDING);
+ const preparingOrders = tvOrders.filter((order) => order.status === Status.PREPARING);
+ const readyOrders = tvOrders.filter((order) => order.status === Status.READY);
const refs = useRef([null, null, null]);
const router = useRouter();
diff --git a/src/types.ts b/src/types.ts
index 6ada748..3c9f30a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -36,6 +36,7 @@ export interface Category extends Identifiable {
name: string;
key: string;
items: Array- ;
+ needsPreparation: boolean;
}
export interface OrderItem {