Create rudimentary catalogue

This commit is contained in:
2024-04-05 22:24:14 +02:00
parent 1447d8fc17
commit e0bb3a349d
7 changed files with 526 additions and 120 deletions

View File

@ -2,11 +2,22 @@ import React, { useEffect, useState } from 'react';
import { MapContainer, TileLayer, Marker, CircleMarker, Popup, Polyline, LayerGroup, GeoJSON, } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import L, { LatLngBounds } from 'leaflet';
import Contacts from './Contacts';
import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query";
import axios from "axios";
import Contacts from './Contacts';
import { Button } from './ui/Button';
import { Button, buttonVariants } from './ui/Button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
//Todo: Move types to own file
interface User {
@ -21,11 +32,24 @@ interface Node {
id: string;
}
export interface Media {
id: string;
alt?: string;
updatedAt: string;
createdAt: string;
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
}
interface Product extends Node {
id: string;
name: string;
weight?: number;
picture: string;
picture: Media;
createdAt: string;
updatedAt: string;
};
@ -209,11 +233,20 @@ export const KiosMap = () => {
setSelectedNode({ id: nodeId, type: typeParam })
console.log("set id:", nodeId)
}
//params: dispatch: Dispatch, courier: User
const handleAcceptRoute = () => {
}
const handleOpenCatalogue = () => {
}
//params
const handleRequestProduct = () => {
}
const blackDotIcon = L.divIcon({
className: 'bg-gray-950 rounded-full',
iconSize: [20, 20],
@ -235,35 +268,76 @@ export const KiosMap = () => {
return (
<div className='w-full flex justify-center align-middle'>
{
selectedNode.type !== 'none' && (
<div className='absolute bg-white border-gray-950 border-2 z-[999] left-10 top-1/4 p-4'>
<div className='absolute bg-white border-gray-950 border-2 z-[998] left-10 top-1/4 p-4'>
<div className='flex gap-8 flex-col'>
{selectedMaker !== undefined && (
<div>
<Contacts
name={selectedMaker.name}
email={selectedMaker.email}
phoneNumber={selectedMaker.phoneNumber}
role={'maker'}
<div className='flex gap-4 flex-col'>
<Contacts
name={selectedMaker.name}
email={selectedMaker.email}
phoneNumber={selectedMaker.phoneNumber}
role={'maker'}
/>
<Button
variant="kios"
onClick={() => handleOpenCatalogue()}
>See catalogue</Button>
{(selectedMaker.stock !== undefined && selectedMaker.stock.length > 0) &&
<Dialog>
<DialogTrigger
className={buttonVariants({ variant: "kios" })}
>
See catalogue
</DialogTrigger>
<DialogContent className='lg:max-w-screen-lg overflow-y-scroll max-h-screen'>
<DialogHeader>
<DialogTitle className="text-4xl underline underline-offset-2">{selectedMaker.name}'s stock</DialogTitle>
<DialogDescription>
<ul className='flex flex-col gap-4'>
{selectedMaker.stock.map((product, i) => {
return (
<li className="flex flex-row gap-4">
{product.picture.url &&
<img
width={160}
src={product.picture.url}
alt={product.picture.alt || ''}/>
}
<div className="flex flex-col gap-4 pb-4">
<h3 className='text-4xl text-black py-2'>
{product.name}
</h3>
<Button
variant={'kios'}
className='w-full'
onClick={() => handleRequestProduct()}
>
Request product
</Button>
</div>
</li>
)
})}
</ul>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
}
</div>
)}
{selectedRetailer !== undefined && (
<Contacts
name={selectedRetailer.name}
email={selectedRetailer.email}
phoneNumber={selectedRetailer.phoneNumber}
role={'retailer'}
/>
<>
<Contacts
name={selectedRetailer.name}
email={selectedRetailer.email}
phoneNumber={selectedRetailer.phoneNumber}
role={'retailer'}
/>
</>
)}
//Todo: Display products
{selectedDispatch !== undefined && (
<div className='flex flex-col gap-4'>
<Contacts
@ -291,12 +365,12 @@ export const KiosMap = () => {
) :
<div>
<h2 className='text-xl font-bold underline-offset-2 underline py-2'>No courier!</h2>
<Button
<Button
variant={"kios"}
onClick={() => handleAcceptRoute()}
>
Accept route as courier
</Button>
>
Accept route as courier
</Button>
</div>
}
</div>

View File

@ -9,7 +9,7 @@ const buttonVariants = cva(
{
variants: {
variant: {
kios: "border-2 border-gray-950 py-2 px-4 w-full hover:bg-gray-950 transition-all hover:text-white hover:font-bold rounded-none",
kios: "text-black border-2 border-gray-950 py-2 px-4 w-full hover:bg-gray-950 transition-all hover:text-white hover:font-bold rounded-none",
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",

View File

@ -0,0 +1,120 @@
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-[999] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-[999] w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}

View File

@ -22,6 +22,8 @@ export interface User {
id: string;
name: string;
phoneNumber?: number;
adminOfMakers?: string[] | Maker[];
adminOfRetailers?: string[] | Retailer[];
updatedAt: string;
createdAt: string;
email: string;
@ -33,6 +35,28 @@ export interface User {
lockUntil?: string;
password?: string;
}
export interface Maker {
id: string;
name: string;
phoneNumber?: string;
email?: string;
/**
* @minItems 2
* @maxItems 2
*/
location: [number, number];
stock?: string[] | Product[];
updatedAt: string;
createdAt: string;
}
export interface Product {
id: string;
name: string;
picture: string | Media;
weight?: number;
updatedAt: string;
createdAt: string;
}
export interface Media {
id: string;
alt?: string;
@ -45,6 +69,20 @@ export interface Media {
width?: number;
height?: number;
}
export interface Retailer {
id: string;
name: string;
phoneNumber?: string;
email?: string;
/**
* @minItems 2
* @maxItems 2
*/
location: [number, number];
stock?: string[] | Product[];
updatedAt: string;
createdAt: string;
}
export interface Courier {
id: string;
updatedAt: string;
@ -61,39 +99,3 @@ export interface Dispatch {
updatedAt: string;
createdAt: string;
}
export interface Product {
id: string;
name: string;
picture: string | Media;
weight?: number;
updatedAt: string;
createdAt: string;
}
export interface Maker {
id: string;
name: string;
phoneNumber?: string;
email?: string;
/**
* @minItems 2
* @maxItems 2
*/
location: [number, number];
stock?: string[] | Product[];
updatedAt: string;
createdAt: string;
}
export interface Retailer {
id: string;
name: string;
phoneNumber?: string;
email?: string;
/**
* @minItems 2
* @maxItems 2
*/
location: [number, number];
stock?: string[] | Product[];
updatedAt: string;
createdAt: string;
}